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:
parent
22348c890b
commit
077ad5b45f
102
arch/xtensa/include/esp32/esp32_himem_chardev.h
Normal file
102
arch/xtensa/include/esp32/esp32_himem_chardev.h
Normal 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 */
|
@ -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)
|
||||
|
413
arch/xtensa/src/esp32/esp32_himem_chardev.c
Normal file
413
arch/xtensa/src/esp32/esp32_himem_chardev.c
Normal 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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user