diff --git a/Documentation/components/drivers/special/devmem.rst b/Documentation/components/drivers/special/devmem.rst new file mode 100644 index 00000000000..be58ce0dca3 --- /dev/null +++ b/Documentation/components/drivers/special/devmem.rst @@ -0,0 +1,26 @@ +============== +DEVMEM Drivers +============== + +The `devmem` driver provides an interface for accessing memory-mapped +I/O in an embedded system. This driver allows for reading, writing, and +memory mapping of specific memory regions or device registers. + +``read()``: This function reads data from the memory-mapped I/O address + space into the buffer provided by the caller. The first byte read + corresponds to the address specified by the device's "current memory + address". The addresses for subsequent bytes depend on the auto-increment + behavior of the specific device. + +``write()``: This function transfers data from the provided data buffer by + the caller to the memory-mapped I/O address space. The first byte written + corresponds to the address specified by the device's "current memory address". + +``mmap()``: The `mmap()` function provides a mechanism to map a device's + memory region into the user space, allowing direct access to device + registers or memory regions. The mapped region can be accessed using + normal memory operations. + + The function requires a base address and size for the memory region + to be mapped. If successful, it returns a pointer to the mapped region. + If mapping fails, it returns `EINVAL` and `errno` is set appropriately. diff --git a/Documentation/components/drivers/special/index.rst b/Documentation/components/drivers/special/index.rst index 5b28073f066..0682006370d 100644 --- a/Documentation/components/drivers/special/index.rst +++ b/Documentation/components/drivers/special/index.rst @@ -25,6 +25,7 @@ following section. audio.rst clk.rst devicetree.rst + devmem.rst dma.rst framebuffer.rst i2c.rst diff --git a/drivers/drivers_initialize.c b/drivers/drivers_initialize.c index 36b59b4c1f2..d87281c6223 100644 --- a/drivers/drivers_initialize.c +++ b/drivers/drivers_initialize.c @@ -129,6 +129,10 @@ void drivers_initialize(void) devzero_register(); /* Standard /dev/zero */ #endif +#ifdef CONFIG_DEV_MEM + devmem_register(); +#endif + #if defined(CONFIG_DEV_LOOP) loop_register(); /* Standard /dev/loop */ #endif diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 05b5e6a53dc..8658c3aa918 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -15,6 +15,13 @@ config DEV_ZERO bool "Enable /dev/zero" default n +config DEV_MEM + bool "Enable /dev/mem" + default n + ---help--- + It is a full image of physical memory and can be used to + access physical memory. + config DEV_ASCII bool "Enable /dev/ascii" default n diff --git a/drivers/misc/Make.defs b/drivers/misc/Make.defs index 55ba2ae92d5..beb0e0f60f0 100644 --- a/drivers/misc/Make.defs +++ b/drivers/misc/Make.defs @@ -30,6 +30,10 @@ ifeq ($(CONFIG_DEV_ZERO),y) CSRCS += dev_zero.c endif +ifeq ($(CONFIG_DEV_MEM),y) + CSRCS += dev_mem.c +endif + ifeq ($(CONFIG_DEV_ASCII),y) CSRCS += dev_ascii.c endif diff --git a/drivers/misc/dev_mem.c b/drivers/misc/dev_mem.c new file mode 100644 index 00000000000..91c5812ce24 --- /dev/null +++ b/drivers/misc/dev_mem.c @@ -0,0 +1,262 @@ +/**************************************************************************** + * drivers/misc/dev_mem.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define DEVMEM_REGION 8 + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern uint8_t _stext[]; /* Start of .text */ +extern uint8_t _etext[]; /* End_1 of .text + .rodata */ +extern uint8_t _sdata[]; /* Start of .data */ +extern uint8_t _edata[]; /* End+1 of .data */ +extern uint8_t _sbss[]; /* Start of .bss */ +extern uint8_t _ebss[]; /* End+1 of .bss */ + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* Character driver methods */ + +static ssize_t devmem_read(FAR struct file *filep, FAR char *buffer, + size_t buflen); +static ssize_t devmem_write(FAR struct file *filep, FAR const char *buffer, + size_t buflen); +static int devmem_mmap(FAR struct file *filep, + FAR struct mm_map_entry_s *map); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct file_operations g_devmem_fops = +{ + NULL, /* open */ + NULL, /* close */ + devmem_read, /* read */ + devmem_write, /* write */ + NULL, /* seek */ + NULL, /* ioctl */ + devmem_mmap, /* mmap */ +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: devmem_read + ****************************************************************************/ + +static ssize_t devmem_read(FAR struct file *filep, FAR char *buffer, + size_t buflen) +{ + FAR struct memory_region_s *region = filep->f_inode->i_private; + uintptr_t src = filep->f_pos; + uintptr_t start; + uintptr_t end; + ssize_t len; + int i; + + DEBUGASSERT(region && src); + + for (i = 0; i < DEVMEM_REGION; i++) + { + if (region[i].start == 0 && region[i].end == 0) + { + break; + } + + start = MAX(src, region[i].start); + end = MIN(src + buflen, region[i].end); + len = end - start; + if (len > 0 && (region[i].flags & PROT_READ)) + { + memcpy(buffer, (FAR const void *)start, len); + return len; + } + } + + return -EINVAL; +} + +/**************************************************************************** + * Name: devmem_write + ****************************************************************************/ + +static ssize_t devmem_write(FAR struct file *filep, FAR const char *buffer, + size_t buflen) +{ + FAR struct memory_region_s *region = filep->f_inode->i_private; + uintptr_t dest = filep->f_pos; + uintptr_t start; + uintptr_t end; + ssize_t len; + int i; + + DEBUGASSERT(region && dest); + + for (i = 0; i < DEVMEM_REGION; i++) + { + if (region[i].start == 0 && region[i].end == 0) + { + break; + } + + start = MAX(dest, region[i].start); + end = MIN(dest + buflen, region[i].end); + len = end - start; + if (len > 0 && (region[i].flags & PROT_WRITE)) + { + memcpy((FAR void *)start, buffer, len); + return len; + } + } + + return -EINVAL; +} + +/**************************************************************************** + * Name: devmem_mmap + ****************************************************************************/ + +static int devmem_mmap(FAR struct file *filep, + FAR struct mm_map_entry_s *map) +{ + FAR struct memory_region_s *region = filep->f_inode->i_private; + uintptr_t start; + uintptr_t end; + int i; + + DEBUGASSERT(region); + + if (map->offset < 0) + { + return -EINVAL; + } + + start = map->offset; + end = start + map->length; + + for (i = 0; i < DEVMEM_REGION; i++) + { + if (region[i].start == 0 && region[i].end == 0) + { + break; + } + + if (start >= region[i].start && end <= region[i].end) + { + map->vaddr = (FAR void *)start; + return 0; + } + } + + return -EINVAL; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: devmem_register + * + * Description: + * Create an MEM driver. + * + * Returned Value: + * Zero (OK) on success; A negated errno value on failure. + * + ****************************************************************************/ + +int devmem_register(void) +{ + FAR struct memory_region_s *region; + bool merge = (_edata == _sbss); + ssize_t len = 0; + int ret; + + region = kmm_calloc(DEVMEM_REGION, sizeof(*region)); + if (region == NULL) + { + return -ENOMEM; + } + +#ifdef CONFIG_BOARD_MEMORY_RANGE + len = parse_memory_region(CONFIG_BOARD_MEMORY_RANGE, region, + DEVMEM_REGION - 1); + if (len < 0) + { + kmm_free(region); + return len; + } +#endif + + if (len + (4 - merge) > DEVMEM_REGION) + { + len = DEVMEM_REGION - (4 - merge); + } + + region[len].flags = PROT_EXEC | PROT_READ; + region[len].start = (uintptr_t)_stext; + region[len++].end = (uintptr_t)_etext; + region[len].flags = PROT_WRITE | PROT_READ; + region[len].start = (uintptr_t)_sdata; + region[len++].end = (uintptr_t)_edata; + + if (merge) + { + region[len - 1].end = (uintptr_t)_ebss; + } + else + { + region[len].flags = PROT_WRITE | PROT_READ; + region[len].start = (uintptr_t)_sbss; + region[len++].end = (uintptr_t)_ebss; + } + + /* register the new MEM driver */ + + ret = register_driver("/dev/mem", &g_devmem_fops, 0666, region); + if (ret < 0) + { + kmm_free(region); + return -ENOMEM; + } + + return ret; +} diff --git a/include/nuttx/drivers/drivers.h b/include/nuttx/drivers/drivers.h index 3dec1802258..b8eb6f4cb2e 100644 --- a/include/nuttx/drivers/drivers.h +++ b/include/nuttx/drivers/drivers.h @@ -144,6 +144,18 @@ void devurandom_register(void); void devcrypto_register(void); +/**************************************************************************** + * Name: devmem_register + * + * Description: + * Register devmem driver + * + ****************************************************************************/ + +#ifdef CONFIG_DEV_MEM +int devmem_register(void); +#endif + /**************************************************************************** * Name: devzero_register *