arch: xtensa/esp32: Add esp32_himem_chardev.c

Summary:
- It is applicable to esp32 products and uses the himem part
  of 8M psram by creating character devices.

Impact:
- None

Testing:
- Use esp32-wrover series products for more than 1000 functional verifications.
This commit is contained in:
ssssenai 2022-12-03 02:09:58 +08:00 committed by Xiang Xiao
parent 22348c890b
commit 077ad5b45f
3 changed files with 516 additions and 0 deletions

View File

@ -0,0 +1,102 @@
/****************************************************************************
* arch/xtensa/include/esp32/esp32_himem_chardev.h
*
* 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.
*
****************************************************************************/
/****************************************************************************
* Public Function Prototypes
****************************************************************************/
#ifndef __ARCH_XTENSA_INCLUDE_ESP32_ESP32_HIMEM_CHARDEV_H
#define __ARCH_XTENSA_INCLUDE_ESP32_ESP32_HIMEM_CHARDEV_H
#ifdef __cplusplus
extern "C"
{
#endif
/****************************************************************************
* Name: himem_chardev_init
*
* Description:
* Himem_Cdev Initializes the operation.
*
* Input Parameters:
* None
*
* Returned Value:
* Returns 0 on success and non-zero on failure.
*
****************************************************************************/
int himem_chardev_init(void);
/****************************************************************************
* Name: himem_chardev_exit
*
* Description:
* Himem_Cdev exits and releases the operation.
*
* Input Parameters:
* None
*
* Returned Value:
* Returns 0 on success and non-zero on failure.
*
****************************************************************************/
int himem_chardev_exit(void);
/****************************************************************************
* Name: himem_chardev_register
*
* Description:
* Himem_Cdev indicates the registration operation.
*
* Input Parameters:
* name - Himem_Cdev indicates the registration name.
* size - Himem_Cdev registers the device size.
*
* Returned Value:
* Returns 0 on success and non-zero on failure.
*
****************************************************************************/
int himem_chardev_register(char *name, size_t size);
/****************************************************************************
* Name: himem_chardev_unregister
*
* Description:
* Himem_Cdev cancels the registration
*
* Input Parameters:
* name - Himem_Cdev indicates the registration name.
*
* Returned Value:
* Returns 0 on success and non-zero on failure.
*
****************************************************************************/
int himem_chardev_unregister(char *name);
#ifdef __cplusplus
}
#endif
#endif /* __ARCH_XTENSA_INCLUDE_ESP32_ESP32_HIMEM_CHARDEV_H */

View File

@ -105,6 +105,7 @@ ifeq ($(CONFIG_ESP32_SPIRAM),y)
CHIP_CSRCS += esp32_spiram.c
CHIP_CSRCS += esp32_psram.c
CHIP_CSRCS += esp32_himem.c
CHIP_CSRCS += esp32_himem_chardev.c
endif
ifeq ($(CONFIG_ESP32_EFUSE),y)

View File

@ -0,0 +1,413 @@
/****************************************************************************
* arch/xtensa/src/esp32/esp32_himem_chardev.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 <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <debug.h>
#include <sys/types.h>
#include <nuttx/list.h>
#include <nuttx/kmalloc.h>
#include <nuttx/himem/himem.h>
#include <nuttx/mutex.h>
#include <nuttx/fs/fs.h>
#include <arch/esp32/esp32_himem_chardev.h>
#include "esp32_himem.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define HIMEM_UNMAPPED (-1)
struct himem_chardev_s
{
struct list_node node;
size_t size;
esp_himem_handle_t mem_handle;
char name[32];
};
struct himem_chardev_priv_s
{
off_t offset; /* himem offset[byte]. update by seek(), read(), write(). */
};
static struct list_node g_himem_chardev_list
= LIST_INITIAL_VALUE(g_himem_chardev_list);
static esp_himem_rangehandle_t g_range_handle;
static mutex_t lock;
static size_t g_ram_offset; /* used by himem map/unmap */
static void *g_himem_ptr; /* mapped himem pointer */
static struct file *g_mapped_filep; /* for multi device */
/****************************************************************************
* Priavte Functions
****************************************************************************/
static int himem_chardev_open(struct file *filep)
{
struct himem_chardev_priv_s *priv;
priv = kmm_malloc(sizeof(struct himem_chardev_priv_s));
if (priv == NULL)
{
merr("Failed to malloc.\n");
return -1;
}
priv->offset = 0;
filep->f_priv = priv;
return 0;
}
static int himem_chardev_close(struct file *filep)
{
struct himem_chardev_priv_s *priv = filep->f_priv;
kmm_free(priv);
return 0;
}
static int himem_chardev_read_write(int is_write, struct file *filep,
char *buffer, size_t length)
{
struct himem_chardev_s *dev = filep->f_inode->i_private;
struct himem_chardev_priv_s *priv = filep->f_priv;
size_t mmap_offset = 0;
int ret = 0;
size_t i = 0;
size_t copy_size = 0;
void *himem_ptr;
size_t copy_range = 0;
ret = nxmutex_lock(&lock);
if (ret != 0)
{
merr("Failed to lock semaphore.\n");
return -1;
}
while (i < length)
{
mmap_offset = priv->offset / ESP_HIMEM_BLKSZ;
if (mmap_offset > (dev->size / ESP_HIMEM_BLKSZ))
{
merr("copy size(%d) over himem phy mem size(%d).\n",
length, dev->size);
goto err;
}
if ((mmap_offset != g_ram_offset) || (g_mapped_filep != filep))
{
if (g_ram_offset != HIMEM_UNMAPPED)
{
/* already mapped another page */
ret = esp_himem_unmap(g_range_handle,
g_himem_ptr, ESP_HIMEM_BLKSZ);
if (ret != 0)
{
merr("Failed to unmap himem.\n");
goto err;
}
g_ram_offset = HIMEM_UNMAPPED;
g_mapped_filep = NULL;
}
ret = esp_himem_map(dev->mem_handle, g_range_handle,
mmap_offset * ESP_HIMEM_BLKSZ,
0, ESP_HIMEM_BLKSZ, 0, &g_himem_ptr);
if (ret != 0)
{
merr("Failed to map himem.\n");
goto err;
}
g_ram_offset = mmap_offset;
g_mapped_filep = filep;
}
himem_ptr = g_himem_ptr + priv->offset % ESP_HIMEM_BLKSZ;
copy_range = ESP_HIMEM_BLKSZ - priv->offset % ESP_HIMEM_BLKSZ;
/* The case of crossing a phy mem page boundary */
if (copy_range < (length - i))
{
if (is_write)
{
memcpy(himem_ptr, buffer + i, copy_range);
}
else
{
memcpy(buffer + i, himem_ptr, copy_range);
}
priv->offset += copy_range;
copy_size += copy_range;
i += copy_range;
}
else
{
if (is_write)
{
memcpy(himem_ptr, buffer + i, length - i);
}
else
{
memcpy(buffer + i, himem_ptr, length - i);
}
priv->offset += length - i;
copy_size += length - i;
i += length - i;
}
}
nxmutex_unlock(&lock);
return (int)(copy_size & INT_MAX);
err:
nxmutex_unlock(&lock);
return copy_size > 0 ? (int)(copy_size & INT_MAX) : -1;
}
static int himem_chardev_read(struct file *filep,
char *dst, size_t length)
{
return himem_chardev_read_write(0, filep, dst, length);
}
static int himem_chardev_write(struct file *filep,
const char *src, size_t length)
{
return himem_chardev_read_write(1, filep, (char *)src, length);
}
static off_t himem_chardev_seek(struct file *filep,
off_t offset, int whence)
{
struct himem_chardev_priv_s *priv = filep->f_priv;
struct himem_chardev_s *dev = filep->f_inode->i_private;
nxmutex_lock(&lock);
switch (whence)
{
case SEEK_SET:
priv->offset = offset;
break;
case SEEK_CUR:
priv->offset += offset;
break;
case SEEK_END:
priv->offset = dev->size + offset;
break;
default:
merr("invalid parameter: whence:%d\n", whence);
nxmutex_unlock(&lock);
return -1;
}
filep->f_pos = priv->offset;
nxmutex_unlock(&lock);
return priv->offset;
}
static int himem_chardev_ioctl(struct file *filep,
int cmd, unsigned long arg)
{
return 0;
}
static const struct file_operations fops =
{
.open = himem_chardev_open,
.close = himem_chardev_close,
.read = himem_chardev_read,
.write = himem_chardev_write,
.seek = himem_chardev_seek,
.ioctl = himem_chardev_ioctl,
};
/****************************************************************************
* Public Functions
****************************************************************************/
int himem_chardev_init(void)
{
int ret = 0;
ret = esp_himem_init();
if (ret != 0)
{
merr("Failed to himem init.\n");
return ret;
}
ret = esp_himem_alloc_map_range(ESP_HIMEM_BLKSZ, &g_range_handle); /* 32KB */
if (ret != 0)
{
merr("Failed to alloc himem map.\n");
return ret;
}
ret = nxmutex_init(&lock);
if (ret != 0)
{
merr("Failed to init semaphore.\n");
esp_himem_free_map_range(g_range_handle);
return ret;
}
g_ram_offset = HIMEM_UNMAPPED;
g_mapped_filep = NULL;
return ret;
}
int himem_chardev_exit(void)
{
int ret = 0;
ret = nxmutex_destroy(&lock);
if (ret != 0)
{
merr("Failed to destroy semaphore.\n");
esp_himem_free_map_range(g_range_handle);
return ret;
}
ret = esp_himem_free_map_range(g_range_handle);
return ret;
}
int himem_chardev_register(char *name, size_t size)
{
int ret = 0;
struct himem_chardev_s *dev;
dev = (struct himem_chardev_s *)kmm_malloc(sizeof(struct himem_chardev_s));
if (dev == NULL)
{
merr("Failed to malloc.\n");
return -1;
}
/* 32KB Alignment */
size_t mod = size % ESP_HIMEM_BLKSZ;
if (mod != 0)
{
size += (ESP_HIMEM_BLKSZ - mod);
}
dev->size = size;
strncpy(dev->name, name, 32);
ret = esp_himem_alloc(dev->size, &dev->mem_handle);
if (ret != 0)
{
merr("Failed to alloc himem. size=%d\n", dev->size);
kmm_free(dev);
return ret;
}
ret = register_driver(dev->name, &fops, 0666, dev);
if (ret != 0)
{
merr("Failed to register driver. dev=%s\n", dev->name);
esp_himem_free(dev->mem_handle);
kmm_free(dev);
return ret;
}
ret = nxmutex_lock(&lock);
if (ret != 0)
{
merr("Failed to lock semaphore.\n");
unregister_driver(dev->name);
esp_himem_free(dev->mem_handle);
kmm_free(dev);
return ret;
}
list_add_tail(&g_himem_chardev_list, &dev->node);
ret = nxmutex_unlock(&lock);
if (ret != 0)
{
merr("Failed to unlock semaphore.\n");
list_delete(&dev->node);
unregister_driver(dev->name);
esp_himem_free(dev->mem_handle);
kmm_free(dev);
return ret;
}
return 0;
}
int himem_chardev_unregister(char *name)
{
int ret = 0;
int successed = 0;
struct himem_chardev_s *dev;
struct himem_chardev_s *tmp;
nxmutex_lock(&lock);
list_for_every_entry_safe(&g_himem_chardev_list, dev,
tmp, struct himem_chardev_s, node)
{
if (strcmp(name, dev->name) == 0)
{
if (g_ram_offset != HIMEM_UNMAPPED)
{
/* driver is mapping some page */
ret = esp_himem_unmap(g_range_handle,
g_himem_ptr, ESP_HIMEM_BLKSZ);
if (ret != 0)
{
merr("Failed to unmap himem.\n");
successed = -1;
}
g_ram_offset = HIMEM_UNMAPPED;
g_mapped_filep = NULL;
}
ret = esp_himem_free(dev->mem_handle);
if (ret != 0)
{
merr("Failed to free himem.\n");
successed = -1;
}
ret = unregister_driver(dev->name);
if (ret != 0)
{
merr("Failed to unregister driver. dev=%s\n", dev->name);
successed = -1;
}
list_delete(&dev->node);
kmm_free(dev);
nxmutex_unlock(&lock);
return successed;
}
}
nxmutex_unlock(&lock);
merr("dev=%s is not registerd.\n", name);
return -1;
}