diff options
Diffstat (limited to 'cpukit/libfs/src/devfs')
-rw-r--r-- | cpukit/libfs/src/devfs/devclose.c | 41 | ||||
-rw-r--r-- | cpukit/libfs/src/devfs/devfs.h | 280 | ||||
-rw-r--r-- | cpukit/libfs/src/devfs/devfs_eval.c | 85 | ||||
-rw-r--r-- | cpukit/libfs/src/devfs/devfs_init.c | 92 | ||||
-rw-r--r-- | cpukit/libfs/src/devfs/devfs_mknod.c | 81 | ||||
-rw-r--r-- | cpukit/libfs/src/devfs/devfs_node_type.c | 26 | ||||
-rw-r--r-- | cpukit/libfs/src/devfs/devfs_show.c | 36 | ||||
-rw-r--r-- | cpukit/libfs/src/devfs/devioctl.c | 45 | ||||
-rw-r--r-- | cpukit/libfs/src/devfs/devopen.c | 42 | ||||
-rw-r--r-- | cpukit/libfs/src/devfs/devread.c | 48 | ||||
-rw-r--r-- | cpukit/libfs/src/devfs/devstat.c | 46 | ||||
-rw-r--r-- | cpukit/libfs/src/devfs/devwrite.c | 48 |
12 files changed, 870 insertions, 0 deletions
diff --git a/cpukit/libfs/src/devfs/devclose.c b/cpukit/libfs/src/devfs/devclose.c new file mode 100644 index 0000000000..773cade5c6 --- /dev/null +++ b/cpukit/libfs/src/devfs/devclose.c @@ -0,0 +1,41 @@ +/* + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems.h> +#include <rtems/io.h> + +#include "devfs.h" + +int devFS_close( + rtems_libio_t *iop +) +{ + rtems_libio_open_close_args_t args; + rtems_status_code status; + rtems_device_name_t *np; + + np = (rtems_device_name_t *)iop->pathinfo.node_access; + + args.iop = iop; + args.flags = 0; + args.mode = 0; + + status = rtems_io_close( + np->major, + np->minor, + (void *) &args + ); + + return rtems_deviceio_errno(status); +} + + diff --git a/cpukit/libfs/src/devfs/devfs.h b/cpukit/libfs/src/devfs/devfs.h new file mode 100644 index 0000000000..eafa069cb8 --- /dev/null +++ b/cpukit/libfs/src/devfs/devfs.h @@ -0,0 +1,280 @@ +/** +* @file libfs/devfs/devfs.h +* +* This include file contains all constants and structures associated +* with the 'device-only' filesystem. +*/ + +#ifndef _RTEMS_DEVFS_H +#define _RTEMS_DEVFS_H + +#include <rtems/libio_.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * This structure define the type of device table + */ + +typedef struct +{ + /** This member points to device name which is a null-terminated string */ + char *device_name; + /** This member is the name length of a device */ + uint32_t device_name_length; + /** major number of a device */ + rtems_device_major_number major; + /** minor number of a device */ + rtems_device_minor_number minor; + /** device creation mode, only device file can be created */ + mode_t mode; + +} rtems_device_name_t; + + + +/** + * This routine associates RTEMS status code with errno + */ + +extern int rtems_deviceio_errno(rtems_status_code code); + + +/** + * The following defines the device table size. This values + * is configured during application configuration time by + * the user. The default value is set to 4. + */ + +extern uint32_t rtems_device_table_size; + +/** + * This handler maps open operation to rtems_io_open. + * @param iop This is the RTEMS's internal representation of file. + * @param pathname a null-terminated string that starts with /dev. + * @param flag access flags + * @param mode access mode + * @retval the same as open + */ + +extern int devFS_open( + rtems_libio_t *iop, + const char *pathname, + uint32_t flag, + uint32_t mode +); + + +/** + * This handler maps close operation to rtems_io_close. + * @param iop This is the RTEMS's internal representation of file + * @retval the same as close + */ + + +extern int devFS_close( + rtems_libio_t *iop +); + + +/** + * This handler maps read operation to rtems_io_read. + * @param iop This is the RTEMS's internal representation of file + * @param buffer memory location to store read data + * @param count how many bytes to read + * @retval On successful, this routine returns total bytes read. On error + * it returns -1 and errno is set to proper value. + */ + +extern ssize_t devFS_read( + rtems_libio_t *iop, + void *buffer, + size_t count +); + + +/** + * This handler maps write operation to rtems_io_write. + * @param iop This is the RTEMS's internal representation of file + * @param buffer data to be written + * @param count how many bytes to write + * @retval On successful, this routine returns total bytes written. On error + * it returns -1 and errno is set to proper value. + */ + +extern ssize_t devFS_write( + rtems_libio_t *iop, + const void *buffer, + size_t count +); + + +/** + * This handler maps ioctl operation to rtems_io_ioctl. + * @param iop This is the RTEMS's internal representation of file + * @param command io control command + * @param buffer io control parameters + * @retval On successful, this routine returns total bytes written. On error + * it returns -1 and errno is set to proper value. + */ + +extern int devFS_ioctl( + rtems_libio_t *iop, + uint32_t command, + void *buffer +); + + + + +/** + * This handler gets the device file information. This routine only set the following member of struct stat: + * st_dev : device number + * st_mode: device file creation mode, only two mode are accepted: + * S_IFCHR: character device file + * S_IFBLK: block device file + * @param loc contains filesystem access information + * @param buf buffer to hold the device file's information + * @retval On successful, this routine returns 0. On error + * it returns -1 and errno is set to proper value. + */ + +extern int devFS_stat( + rtems_filesystem_location_info_t *loc, + struct stat *buf +); + + + +/** + * This routine is invoked upon determination of a node type. + * Since this is a device-only filesystem, so there is only + * one node type in the system. + * + * @param pathloc contains filesytem access information, this + * parameter is ignored + * @retval always returns RTEMS_FILESYSTEM_DEVICE + */ + +extern rtems_filesystem_node_types_t devFS_node_type( + rtems_filesystem_location_info_t *pathloc +); + + + +/** + * This routine is invoked to determine if 'pathname' exists. + * This routine first check access flags, then it searches + * the device table to get the information. + * + * @param pathname device name to be searched + * @param flags access flags + * @param pathloc contains filesystem access information + * @retval upon success(pathname exists), this routines + * returns 0; if 'flag' is invalid, it returns -1 and errno + * is set to EIO; otherwise, it returns -1 and errno is set to ENOENT + */ + +extern int devFS_evaluate_path( + const char *pathname, + size_t pathnamelen, + int flags, + rtems_filesystem_location_info_t *pathloc +); + + +/** + * This routine is given a path to evaluate and a valid start + * location. It is responsible for finding the parent node for + * a requested make command, setting pathloc information to + * identify the parent node, and setting the name pointer to + * the first character of the name of the new node. In device + * only filesystem, devices do not has a tree hierarchy, there + * are no parent-child relationship. So this routine is rather + * simple, it just set *name to path and returns + * + * @param path device path to be evaluated + * @param pathloc contains filesystem access information, this + * parameter is ignored + * @param name + * @retval always returns 0 + */ + +extern int devFS_evaluate_for_make( + const char *path, + rtems_filesystem_location_info_t *pathloc, + const char **name +); + + + +/** + * This routine is invoked upon registration of a new device + * file. It is responsible for creating a item in the main + * device table. This routine searches the device table in + * sequential order, when found a empty slot, it fills the slot + * with proper values. + * + * @param path the device file name to be registered + * @param mode file mode, this parameter is ignored + * @param dev device major and minor number + * @param pathloc contains filesystem access information + * @retval upon success, this routine returns 0; if 'path' + * already exist, it returns -1 and errno is set to EEXIST; + * if device table is full, it returns -1 and errno is set + * to ENOMEM + */ + +extern int devFS_mknod( + const char *path, + mode_t mode, + dev_t dev, + rtems_filesystem_location_info_t *pathloc +); + + +/** + * This routine is invoked upon rtems filesystem initialization. + * It is responsible for creating the main device table, + * initializing it to a known state, and set device file operation + * handlers. After this, the device-only filesytem is ready for use + * + * @param mt_entry The filesystem mount table entry. + * @param data Filesystem specific data. + * @retval upon success, this routine returns 0; otherwise it returns + * -1 and errno is set to proper value. The only error is when malloc + * failed, and errno is set to NOMEM. + */ + +extern int devFS_initialize( + rtems_filesystem_mount_table_entry_t *mt_entry, + const void *data +); + + +/** + * This routine retrieves all the device registered in system, and + * prints out their detail information. For example, on one system, + * devFS_show will print out following message: + * + * /dev/console 0 0 + * /dev/clock 1 0 + * /dev/tty0 0 0 + * /flash 2 0 + * + * This routine is intended for debugging, and can be used by shell + * program to provide user with the system information. + * + * @retval 0 + */ + +extern int devFS_Show(void); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/cpukit/libfs/src/devfs/devfs_eval.c b/cpukit/libfs/src/devfs/devfs_eval.c new file mode 100644 index 0000000000..a3169fb3fa --- /dev/null +++ b/cpukit/libfs/src/devfs/devfs_eval.c @@ -0,0 +1,85 @@ +/* + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems/seterr.h> +#include <fcntl.h> +#include <assert.h> +#include "devfs.h" + +/** + * The following defines the device-only filesystem operating + * handlers. + */ + +extern rtems_filesystem_operations_table devFS_ops; + +/** + * The following defines the device-only filesystem operating + * handlers. + */ + +extern rtems_filesystem_file_handlers_r devFS_file_handlers; + +int devFS_evaluate_path( + const char *pathname, + size_t pathnamelen, + int flags, + rtems_filesystem_location_info_t *pathloc +) +{ + int i; + rtems_device_name_t *device_name_table; + + /* see if 'flags' is valid */ + if ( !rtems_libio_is_valid_perms( flags ) ) + rtems_set_errno_and_return_minus_one( EPERM ); + + /* get the device name table */ + device_name_table = (rtems_device_name_t *)pathloc->node_access; + if (!device_name_table) + rtems_set_errno_and_return_minus_one( EFAULT ); + + for (i = 0; i < rtems_device_table_size; i++) { + if (!device_name_table[i].device_name) + continue; + + if (strncmp(pathname, device_name_table[i].device_name, pathnamelen) != 0) + continue; + + if (device_name_table[i].device_name[pathnamelen] != '\0') + continue; + + /* find the device, set proper values */ + pathloc->node_access = (void *)&device_name_table[i]; + pathloc->handlers = &devFS_file_handlers; + pathloc->ops = &devFS_ops; + pathloc->mt_entry = rtems_filesystem_root.mt_entry; + return 0; + } + + /* no such file or directory */ + rtems_set_errno_and_return_minus_one( ENOENT ); +} + + + +int devFS_evaluate_for_make( + const char *path, + rtems_filesystem_location_info_t *pathloc, + const char **name +) +{ + /* we do nothing, just set name to path */ + *name = path; + return 0; +} + diff --git a/cpukit/libfs/src/devfs/devfs_init.c b/cpukit/libfs/src/devfs/devfs_init.c new file mode 100644 index 0000000000..5f6521a045 --- /dev/null +++ b/cpukit/libfs/src/devfs/devfs_init.c @@ -0,0 +1,92 @@ +/* + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <rtems.h> +#include <rtems/seterr.h> +#include <rtems/score/wkspace.h> +#include "devfs.h" + +rtems_filesystem_operations_table devFS_ops = +{ + devFS_evaluate_path, + devFS_evaluate_for_make, + rtems_filesystem_default_link, + rtems_filesystem_default_unlink, + devFS_node_type, + devFS_mknod, + rtems_filesystem_default_chown, + rtems_filesystem_default_freenode, + rtems_filesystem_default_mount, + devFS_initialize, + rtems_filesystem_default_unmount, + rtems_filesystem_default_fsunmount, + rtems_filesystem_default_utime, + rtems_filesystem_default_evaluate_link, + rtems_filesystem_default_symlink, + rtems_filesystem_default_readlink, + rtems_filesystem_default_rename, + rtems_filesystem_default_statvfs +}; + + +rtems_filesystem_file_handlers_r devFS_file_handlers = +{ + devFS_open, + devFS_close, + devFS_read, + devFS_write, + devFS_ioctl, + rtems_filesystem_default_lseek, + devFS_stat, + rtems_filesystem_default_fchmod, + rtems_filesystem_default_ftruncate, + rtems_filesystem_default_fpathconf, + rtems_filesystem_default_fsync, + rtems_filesystem_default_fdatasync, + rtems_filesystem_default_fcntl, + rtems_filesystem_default_rmnod +}; + + + +int devFS_initialize( + rtems_filesystem_mount_table_entry_t *temp_mt_entry, + const void *data +) +{ + rtems_device_name_t *device_name_table; + + /* allocate device only filesystem name table */ + device_name_table = (rtems_device_name_t *)_Workspace_Allocate( + sizeof( rtems_device_name_t ) * ( rtems_device_table_size ) + ); + + /* no memory for device filesystem */ + if (!device_name_table) + rtems_set_errno_and_return_minus_one( ENOMEM ); + + memset( + device_name_table, 0, + sizeof( rtems_device_name_t ) * ( rtems_device_table_size ) + ); + + /* set file handlers */ + temp_mt_entry->mt_fs_root.handlers = &devFS_file_handlers; + temp_mt_entry->mt_fs_root.ops = &devFS_ops; + + /* Set the node_access to device name table */ + temp_mt_entry->mt_fs_root.node_access = (void *)device_name_table; + + return 0; +} + diff --git a/cpukit/libfs/src/devfs/devfs_mknod.c b/cpukit/libfs/src/devfs/devfs_mknod.c new file mode 100644 index 0000000000..d8e5a30d5f --- /dev/null +++ b/cpukit/libfs/src/devfs/devfs_mknod.c @@ -0,0 +1,81 @@ +/* + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> +#include <stdlib.h> + +#include <rtems/seterr.h> +#include "devfs.h" + +int devFS_mknod( + const char *path, + mode_t mode, + dev_t dev, + rtems_filesystem_location_info_t *pathloc +) +{ + int i; + int slot; + rtems_device_name_t *device_name_table; + rtems_device_major_number major; + rtems_device_minor_number minor; + ISR_Level level; + + /* + * This is a special case. In rtems_filesystem_initialize, + * a special device '/dev' will be created. We check this + * condition and do not create the '/dev' and the 'path' + * actually passed in is 'dev', not '/dev'. Just return 0 to + * indicate we are OK. + */ + + if ((path[0] == 'd') && (path[1] == 'e') && + (path[2] == 'v') && (path[3] == '\0')) + return 0; + + /* must be a character device or a block device */ + if (!S_ISBLK(mode) && !S_ISCHR(mode)) + rtems_set_errno_and_return_minus_one( EINVAL ); + else + rtems_filesystem_split_dev_t(dev, major, minor); + + /* Find an empty slot in device name table */ + device_name_table = (rtems_device_name_t *)pathloc->node_access; + if (!device_name_table) + rtems_set_errno_and_return_minus_one( EFAULT ); + + for (slot = -1, i = 0; i < rtems_device_table_size; i++){ + if (device_name_table[i].device_name == NULL) + slot = i; + else + if (strcmp(path, device_name_table[i].device_name) == 0) + rtems_set_errno_and_return_minus_one( EEXIST ); + } + + if (slot == -1) + rtems_set_errno_and_return_minus_one( ENOMEM ); + + _ISR_Disable(level); + device_name_table[slot].device_name = (char *)path; + device_name_table[slot].device_name_length = strlen(path); + device_name_table[slot].major = major; + device_name_table[slot].minor = minor; + device_name_table[slot].mode = mode; + _ISR_Enable(level); + + return 0; +} + diff --git a/cpukit/libfs/src/devfs/devfs_node_type.c b/cpukit/libfs/src/devfs/devfs_node_type.c new file mode 100644 index 0000000000..0bede52d2d --- /dev/null +++ b/cpukit/libfs/src/devfs/devfs_node_type.c @@ -0,0 +1,26 @@ +/* + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include "devfs.h" + +rtems_filesystem_node_types_t devFS_node_type( + rtems_filesystem_location_info_t *pathloc +) +{ + /* + * There is only one type of node: device + */ + + return RTEMS_FILESYSTEM_DEVICE; +} + + diff --git a/cpukit/libfs/src/devfs/devfs_show.c b/cpukit/libfs/src/devfs/devfs_show.c new file mode 100644 index 0000000000..e449caf49f --- /dev/null +++ b/cpukit/libfs/src/devfs/devfs_show.c @@ -0,0 +1,36 @@ +/* + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems/seterr.h> +#include "devfs.h" + +int devFS_Show(void) +{ + int i; + rtems_filesystem_location_info_t *temp_loc; + rtems_device_name_t *device_name_table; + + temp_loc = &rtems_filesystem_root; + device_name_table = (rtems_device_name_t *)temp_loc->node_access; + if (!device_name_table) + rtems_set_errno_and_return_minus_one( EFAULT ); + + for (i = 0; i < rtems_device_table_size; i++){ + if (device_name_table[i].device_name){ + printk("/%s %d %d\n", device_name_table[i].device_name, + device_name_table[i].major, device_name_table[i].minor); + } + } + return 0; +} + + diff --git a/cpukit/libfs/src/devfs/devioctl.c b/cpukit/libfs/src/devfs/devioctl.c new file mode 100644 index 0000000000..15965b8c0e --- /dev/null +++ b/cpukit/libfs/src/devfs/devioctl.c @@ -0,0 +1,45 @@ +#if HAVE_CONFIG_H +/* + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#include "config.h" +#endif + +#include <rtems.h> +#include <rtems/io.h> + +#include "devfs.h" + +int devFS_ioctl( + rtems_libio_t *iop, + uint32_t command, + void *buffer +) +{ + rtems_libio_ioctl_args_t args; + rtems_status_code status; + rtems_device_name_t *np; + + np = (rtems_device_name_t *)iop->pathinfo.node_access; + + args.iop = iop; + args.command = command; + args.buffer = buffer; + + status = rtems_io_control( + np->major, + np->minor, + (void *) &args + ); + + if ( status ) + return rtems_deviceio_errno(status); + + return args.ioctl_return; +} + diff --git a/cpukit/libfs/src/devfs/devopen.c b/cpukit/libfs/src/devfs/devopen.c new file mode 100644 index 0000000000..67be3678e1 --- /dev/null +++ b/cpukit/libfs/src/devfs/devopen.c @@ -0,0 +1,42 @@ +/* + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems.h> +#include <rtems/io.h> + +#include "devfs.h" + +int devFS_open( + rtems_libio_t *iop, + const char *pathname, + uint32_t flag, + uint32_t mode +) +{ + rtems_libio_open_close_args_t args; + rtems_status_code status; + rtems_device_name_t *np; + + np = (rtems_device_name_t *)iop->pathinfo.node_access; + + args.iop = iop; + args.flags = iop->flags; + args.mode = mode; + + status = rtems_io_open( + np->major, + np->minor, + (void *) &args + ); + + return rtems_deviceio_errno(status); +} diff --git a/cpukit/libfs/src/devfs/devread.c b/cpukit/libfs/src/devfs/devread.c new file mode 100644 index 0000000000..10f74e81c9 --- /dev/null +++ b/cpukit/libfs/src/devfs/devread.c @@ -0,0 +1,48 @@ +/* + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems.h> +#include <rtems/io.h> + +#include "devfs.h" + +ssize_t devFS_read( + rtems_libio_t *iop, + void *buffer, + size_t count +) +{ + rtems_libio_rw_args_t args; + rtems_status_code status; + rtems_device_name_t *np; + + np = (rtems_device_name_t *)iop->pathinfo.node_access; + + args.iop = iop; + args.offset = iop->offset; + args.buffer = buffer; + args.count = count; + args.flags = iop->flags; + args.bytes_moved = 0; + + status = rtems_io_read( + np->major, + np->minor, + (void *) &args + ); + + if ( status ) + return rtems_deviceio_errno(status); + + return (ssize_t) args.bytes_moved; +} + diff --git a/cpukit/libfs/src/devfs/devstat.c b/cpukit/libfs/src/devfs/devstat.c new file mode 100644 index 0000000000..db17595621 --- /dev/null +++ b/cpukit/libfs/src/devfs/devstat.c @@ -0,0 +1,46 @@ +/* + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems.h> +#include <rtems/io.h> +#include <rtems/seterr.h> +#include <rtems/libio.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include "devfs.h" + +int devFS_stat( + rtems_filesystem_location_info_t *loc, + struct stat *buf +) +{ + rtems_device_name_t *the_dev; + + the_dev = (rtems_device_name_t *)loc->node_access; + + /* + * stat() invokes devFS_evaluate_path() which checks that node_access + * is not NULL. So this should NEVER be NULL unless someone breaks + * other code in this filesystem. + */ + #if defined(RTEMS_DEBUG) + if (!the_dev) + rtems_set_errno_and_return_minus_one( EFAULT ); + #endif + + buf->st_rdev = rtems_filesystem_make_dev_t( the_dev->major, the_dev->minor ); + buf->st_mode = the_dev->mode; + return 0; +} + + diff --git a/cpukit/libfs/src/devfs/devwrite.c b/cpukit/libfs/src/devfs/devwrite.c new file mode 100644 index 0000000000..5389c69bc5 --- /dev/null +++ b/cpukit/libfs/src/devfs/devwrite.c @@ -0,0 +1,48 @@ +/* + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems.h> +#include <rtems/io.h> + +#include "devfs.h" + +ssize_t devFS_write( + rtems_libio_t *iop, + const void *buffer, + size_t count +) +{ + rtems_libio_rw_args_t args; + rtems_status_code status; + rtems_device_name_t *np; + + np = (rtems_device_name_t *)iop->pathinfo.node_access; + + args.iop = iop; + args.offset = iop->offset; + args.buffer = (void *) buffer; + args.count = count; + args.flags = iop->flags; + args.bytes_moved = 0; + + status = rtems_io_write( + np->major, + np->minor, + (void *) &args + ); + + if ( status ) + return rtems_deviceio_errno(status); + + return (ssize_t) args.bytes_moved; +} + |