mirror of
https://github.com/HEYAHONG/rt-thread-no-bsp.git
synced 2025-05-08 15:55:48 +08:00
update by jenkins(20250201001522)
This commit is contained in:
parent
504d73aa19
commit
a17817ebb8
@ -32,7 +32,7 @@ permissions:
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
name: Tools
|
||||
if: github.repository_owner == 'RT-Thread'
|
||||
strategy:
|
||||
|
@ -26,7 +26,7 @@ permissions:
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
name: ${{ matrix.legs.UTEST }}
|
||||
if: github.repository_owner == 'RT-Thread'
|
||||
strategy:
|
||||
|
@ -20,6 +20,6 @@ jobs:
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/labeler@v5
|
@ -30,7 +30,7 @@ permissions:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
name: ${{ matrix.legs.RTT_BSP }}
|
||||
if: github.repository_owner == 'RT-Thread'
|
||||
strategy:
|
||||
@ -101,6 +101,7 @@ jobs:
|
||||
- "rm48x50"
|
||||
- "ht32/ht32f52352"
|
||||
- "ht32/ht32f12366"
|
||||
- "ht32/ht32f53252"
|
||||
- "w60x"
|
||||
- "essemi/es32f0654"
|
||||
- "essemi/es32f365x"
|
||||
@ -509,4 +510,4 @@ jobs:
|
||||
run: |
|
||||
curl -X POST -H "Authorization: token ${{ secrets.RTTHREAD_GITHUB_TOKEN }}" \
|
||||
-d '{"body":"@${{ github.actor }}, Thank you for your contribution, but there was an error with the action. Could you please help check the BSP compilation issue? Thank you."}' \
|
||||
"https://api.github.com/repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/comments"
|
||||
"https://api.github.com/repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/comments"
|
||||
|
@ -33,7 +33,7 @@ permissions:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
name: BSP Compilation with More Drivers
|
||||
|
||||
steps:
|
||||
|
43
rt-thread-no-bsp/.github/workflows/doxygen.yml
vendored
43
rt-thread-no-bsp/.github/workflows/doxygen.yml
vendored
@ -11,18 +11,35 @@ on:
|
||||
- 'components/dfs/dfs_v2/include/**'
|
||||
- 'components/dfs/dfs_v2/src/**'
|
||||
- 'components/finsh/**'
|
||||
- '.github/workflows/doxygen.yml'
|
||||
# Runs at 16:00 UTC (BeiJing 00:00) on the 30st of every month
|
||||
push:
|
||||
branches: [master]
|
||||
paths:
|
||||
- 'documentation/doxygen/**'
|
||||
- 'src/**'
|
||||
- 'include/**'
|
||||
- 'components/drivers/include/drivers/**'
|
||||
- 'components/dfs/dfs_v2/include/**'
|
||||
- 'components/dfs/dfs_v2/src/**'
|
||||
- 'components/finsh/**'
|
||||
- '.github/workflows/doxygen.yml'
|
||||
schedule:
|
||||
- cron: '0 16 30 * *'
|
||||
workflow_dispatch:
|
||||
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pages: write
|
||||
id-token: write
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-22.04
|
||||
name: doxygen_doc generate
|
||||
if: github.repository_owner == 'RT-Thread'
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@main
|
||||
with:
|
||||
submodules: 'recursive'
|
||||
- name: Install Tools
|
||||
@ -30,9 +47,27 @@ jobs:
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get -qq install doxygen graphviz
|
||||
- name: generat doxygen html
|
||||
- name: generate doxygen html
|
||||
shell: bash
|
||||
run: |
|
||||
cd documentation/doxygen
|
||||
cd documentation
|
||||
doxygen Doxyfile
|
||||
cat Doxyfile
|
||||
|
||||
- name: Upload static files as artifact
|
||||
id: deployment
|
||||
uses: actions/upload-pages-artifact@main # or specific "vX.X.X" version tag for this action
|
||||
with:
|
||||
path: documentation/html/
|
||||
|
||||
deploy:
|
||||
if: github.event_name == 'push'
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
runs-on: ubuntu-22.04
|
||||
needs: build
|
||||
steps:
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@main
|
||||
|
@ -11,7 +11,7 @@ on:
|
||||
|
||||
jobs:
|
||||
scancode_job:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
name: Scan code format and license
|
||||
if: github.repository_owner == 'RT-Thread'
|
||||
steps:
|
||||
|
@ -51,7 +51,7 @@ permissions:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
name: ${{ github.event.inputs.bsp_options }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
@ -50,7 +50,7 @@ permissions:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
name: ${{ github.event.inputs.bsp_options }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
@ -32,7 +32,7 @@ permissions:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
name: ${{ matrix.legs.RTT_BSP }}
|
||||
if: github.repository_owner == 'RT-Thread'
|
||||
strategy:
|
||||
|
@ -34,7 +34,7 @@ permissions:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
name: ${{ matrix.legs.RTT_BSP }}
|
||||
if: github.repository_owner == 'RT-Thread'
|
||||
strategy:
|
||||
|
@ -34,7 +34,7 @@ permissions:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
name: ${{ matrix.legs.RTT_BSP }}
|
||||
if: github.repository_owner == 'RT-Thread'
|
||||
strategy:
|
||||
|
@ -40,7 +40,7 @@ permissions:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
name: update and create pull request
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
@ -9,7 +9,7 @@ on:
|
||||
jobs:
|
||||
spelling:
|
||||
name: Check Spelling
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
if: github.repository_owner == 'RT-Thread'
|
||||
steps:
|
||||
- name: output ignore words info
|
||||
|
@ -21,7 +21,7 @@ on:
|
||||
|
||||
jobs:
|
||||
scancode_job:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
name: Static code analysis
|
||||
if: github.repository_owner == 'RT-Thread'
|
||||
steps:
|
||||
|
7
rt-thread-no-bsp/.gitignore
vendored
7
rt-thread-no-bsp/.gitignore
vendored
@ -43,6 +43,7 @@ ncscope.*
|
||||
tags
|
||||
|
||||
.idea
|
||||
**/.cache/
|
||||
.vscode
|
||||
*.code-workspace
|
||||
*.eide.*
|
||||
@ -57,4 +58,8 @@ vdso.lds
|
||||
|
||||
# cherryusb libraries
|
||||
!components/drivers/usb/cherryusb/port/pusb2/*.a
|
||||
!components/drivers/usb/cherryusb/port/xhci/phytium/*.a
|
||||
!components/drivers/usb/cherryusb/port/xhci/phytium/*.a
|
||||
|
||||
# stm32cubemx
|
||||
**/CubeMX_Config/Drivers/
|
||||
**/CubeMX_Config/MDK-ARM/
|
||||
|
@ -123,7 +123,7 @@ Based on [STM32F103 BluePill](https://github.com/RT-Thread/rt-thread/tree/master
|
||||
## Simulator
|
||||
|
||||
RT-Thread BSP can be compiled directly and downloaded to the corresponding development board for use. In addition, RT-Thread also provides qemu-vexpress-a9 BSP, which can be used without hardware platform. See the getting started guide below for details. Getting Started of QEMU with Env:
|
||||
[Windows](documentation/quick-start/quick_start_qemu/quick_start_qemu.md) | [Linux Ubuntu](documentation/quick-start/quick_start_qemu/quick_start_qemu_linux.md) | [Mac OS](documentation/quick-start/quick_start_qemu/quick_start_qemu_macos.md)
|
||||
[Windows](documentation/2.quick-start/quick_start_qemu/quick_start_qemu_windows.md) | [Linux Ubuntu](documentation/2.quick-start/quick_start_qemu/quick_start_qemu_linux.md) | [Mac OS](documentation/2.quick-start/quick_start_qemu/quick_start_qemu_macos.md)
|
||||
|
||||
# License
|
||||
|
||||
|
@ -122,7 +122,7 @@ Basierend auf [STM32F103 BluePill](https://github.com/RT-Thread/rt-thread/tree/m
|
||||
## Simulator
|
||||
|
||||
Das RT-Thread BSP kann direkt kompiliert und zur Verwendung auf das entsprechende Entwicklungsboard heruntergeladen werden. Darüber hinaus bietet RT-Thread auch das qemu-vexpress-a9 BSP, das ohne Hardware-Plattform verwendet werden kann. Weitere Informationen finden Sie in der Anleitung für die ersten Schritte unten.
|
||||
[Windows](documentation/quick-start/quick_start_qemu/quick_start_qemu.md) | [Linux Ubuntu](documentation/quick-start/quick_start_qemu/quick_start_qemu_linux.md) | [Mac OS](documentation/quick-start/quick_start_qemu/quick_start_qemu_macos.md)
|
||||
[Windows](documentation/2.quick-start/quick_start_qemu/quick_start_qemu_windows.md) | [Linux Ubuntu](documentation/2.quick-start/quick_start_qemu/quick_start_qemu_linux.md) | [Mac OS](documentation/2.quick-start/quick_start_qemu/quick_start_qemu_macos.md)
|
||||
|
||||
# Lizenz
|
||||
|
||||
|
@ -121,7 +121,7 @@ Basado en [STM32F103 BluePill](https://github.com/RT-Thread/rt-thread/tree/maste
|
||||
|
||||
## Simulator
|
||||
|
||||
El BSP de RT-Thread puede compilarse directamente y descargarse en la placa de desarrollo correspondiente para su uso. Además, RT-Thread también proporciona el BSP qemu-vexpress-a9, que puede utilizarse sin plataforma de hardware. Consulte la guía de inicio más abajo para más detalles. [Windows](documentation/quick-start/quick_start_qemu/quick_start_qemu.md) | [Linux Ubuntu](documentation/quick-start/quick_start_qemu/quick_start_qemu_linux.md) | [Mac OS](documentation/quick-start/quick_start_qemu/quick_start_qemu_macos.md)
|
||||
El BSP de RT-Thread puede compilarse directamente y descargarse en la placa de desarrollo correspondiente para su uso. Además, RT-Thread también proporciona el BSP qemu-vexpress-a9, que puede utilizarse sin plataforma de hardware. Consulte la guía de inicio más abajo para más detalles. [Windows](documentation/2.quick-start/quick_start_qemu/quick_start_qemu_windows.md) | [Linux Ubuntu](documentation/2.quick-start/quick_start_qemu/quick_start_qemu_linux.md) | [Mac OS](documentation/2.quick-start/quick_start_qemu/quick_start_qemu_macos.md)
|
||||
|
||||
# Licencia
|
||||
|
||||
|
@ -124,9 +124,9 @@ RT-Thread Studio演示:
|
||||
|
||||
RT-Thread BSP可以直接编译并下载到相应的开发板使用。此外,RT-Thread还提供 qemu-vexpress-a9 BSP,无需硬件平台即可使用。有关详细信息,请参阅下面的入门指南。
|
||||
|
||||
[QEMU 入门指南(Windows)](documentation/quick-start/quick_start_qemu/quick_start_qemu.md)
|
||||
[QEMU 入门指南(Windows)](documentation/2.quick-start/quick_start_qemu/quick_start_qemu_windows.md)
|
||||
|
||||
[QEMU 入门指南(Ubuntu)](documentation/quick-start/quick_start_qemu/quick_start_qemu_linux.md)
|
||||
[QEMU 入门指南(Ubuntu)](documentation/2.quick-start/quick_start_qemu/quick_start_qemu_linux.md)
|
||||
|
||||
|
||||
## 文档
|
||||
|
@ -169,20 +169,25 @@ if RT_USING_DFS_V1
|
||||
default n
|
||||
endif
|
||||
|
||||
config RT_USING_DFS_ROMFS
|
||||
menuconfig RT_USING_DFS_ROMFS
|
||||
bool "Enable ReadOnly file system on flash"
|
||||
default n
|
||||
|
||||
config RT_USING_DFS_ROMFS_USER_ROOT
|
||||
bool "Use user's romfs root"
|
||||
depends on RT_USING_DFS_ROMFS
|
||||
default n
|
||||
if RT_USING_DFS_ROMFS
|
||||
config RT_USING_DFS_ROMFS_USER_ROOT
|
||||
bool "Use user's romfs root"
|
||||
depends on RT_USING_DFS_V1
|
||||
default n
|
||||
endif
|
||||
|
||||
if RT_USING_SMART
|
||||
config RT_USING_DFS_PTYFS
|
||||
bool "Using Pseudo-Teletype Filesystem (UNIX98 PTY)"
|
||||
depends on RT_USING_DFS_DEVFS
|
||||
default y
|
||||
config RT_USING_DFS_PROCFS
|
||||
bool "Enable proc file system"
|
||||
default n
|
||||
endif
|
||||
|
||||
config RT_USING_DFS_CROMFS
|
||||
|
@ -77,6 +77,7 @@ static int dfs_devfs_open(struct dfs_file *file)
|
||||
}
|
||||
}
|
||||
}
|
||||
rt_free(device_name);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include <dfs_dentry.h>
|
||||
#include <dfs_file.h>
|
||||
#include <dfs_mnt.h>
|
||||
#include <dfs_vfs.h>
|
||||
#include <devfs.h>
|
||||
#include <unistd.h>
|
||||
|
||||
@ -34,10 +35,9 @@ struct devtmpfs_file
|
||||
char name[DIRENT_NAME_MAX]; /* file name */
|
||||
|
||||
rt_uint32_t type; /* file type */
|
||||
rt_list_t subdirs; /* file subdir list */
|
||||
rt_list_t sibling; /* file sibling list */
|
||||
struct dfs_vfs_node node; /* file node in the devtmpfs */
|
||||
|
||||
struct devtmpfs_sb *sb; /* superblock ptr */
|
||||
struct devtmpfs_sb *sb; /* superblock ptr */
|
||||
|
||||
rt_uint32_t mode;
|
||||
char *link;
|
||||
@ -48,7 +48,6 @@ struct devtmpfs_sb
|
||||
rt_uint32_t magic; /* TMPFS_MAGIC */
|
||||
struct devtmpfs_file root; /* root dir */
|
||||
rt_size_t df_size; /* df size */
|
||||
rt_list_t sibling; /* sb sibling list */
|
||||
struct rt_spinlock lock; /* tmpfs lock */
|
||||
};
|
||||
|
||||
@ -111,15 +110,13 @@ static int _get_subdir(const char *path, char *name)
|
||||
#if 0
|
||||
static int _free_subdir(struct devtmpfs_file *dfile)
|
||||
{
|
||||
struct devtmpfs_file *file;
|
||||
rt_list_t *list, *temp_list;
|
||||
struct devtmpfs_file *file, *tmp;
|
||||
struct devtmpfs_sb *superblock;
|
||||
|
||||
RT_ASSERT(dfile->type == TMPFS_TYPE_DIR);
|
||||
|
||||
rt_list_for_each_safe(list, temp_list, &dfile->subdirs)
|
||||
dfs_vfs_for_each_subnode(file, tmp, dfile, node)
|
||||
{
|
||||
file = rt_list_entry(list, struct devtmpfs_file, sibling);
|
||||
if (file->type == TMPFS_TYPE_DIR)
|
||||
{
|
||||
_free_subdir(file);
|
||||
@ -134,7 +131,7 @@ static int _free_subdir(struct devtmpfs_file *dfile)
|
||||
RT_ASSERT(superblock);
|
||||
|
||||
rt_spin_lock(&superblock->lock);
|
||||
rt_list_remove(&(file->sibling));
|
||||
dfs_vfs_remove_node(&file->node);
|
||||
rt_spin_unlock(&superblock->lock);
|
||||
|
||||
rt_free(file);
|
||||
@ -152,14 +149,12 @@ static int devtmpfs_mount(struct dfs_mnt *mnt, unsigned long rwflag, const void
|
||||
{
|
||||
superblock->df_size = sizeof(struct devtmpfs_sb);
|
||||
superblock->magic = TMPFS_MAGIC;
|
||||
rt_list_init(&superblock->sibling);
|
||||
|
||||
superblock->root.name[0] = '/';
|
||||
superblock->root.sb = superblock;
|
||||
superblock->root.type = TMPFS_TYPE_DIR;
|
||||
superblock->root.mode = S_IFDIR | (S_IRUSR | S_IRGRP | S_IROTH) | (S_IXUSR | S_IXGRP | S_IXOTH);
|
||||
rt_list_init(&superblock->root.sibling);
|
||||
rt_list_init(&superblock->root.subdirs);
|
||||
dfs_vfs_init_node(&superblock->root.node);
|
||||
|
||||
rt_spin_lock_init(&superblock->lock);
|
||||
|
||||
@ -193,8 +188,7 @@ static struct devtmpfs_file *devtmpfs_file_lookup(struct devtmpfs_sb *superblock
|
||||
{
|
||||
const char *subpath, *curpath, *filename = RT_NULL;
|
||||
char subdir_name[DIRENT_NAME_MAX];
|
||||
struct devtmpfs_file *file, *curfile;
|
||||
rt_list_t *list;
|
||||
struct devtmpfs_file *file, *curfile, *tmp;
|
||||
|
||||
subpath = path;
|
||||
while (*subpath == '/' && *subpath)
|
||||
@ -222,9 +216,8 @@ find_subpath:
|
||||
|
||||
rt_spin_lock(&superblock->lock);
|
||||
|
||||
rt_list_for_each(list, &curfile->subdirs)
|
||||
dfs_vfs_for_each_subnode(file, tmp, curfile, node)
|
||||
{
|
||||
file = rt_list_entry(list, struct devtmpfs_file, sibling);
|
||||
if (filename) /* find file */
|
||||
{
|
||||
if (rt_strcmp(file->name, filename) == 0)
|
||||
@ -293,7 +286,9 @@ static int devtmpfs_stat(struct dfs_dentry *dentry, struct stat *st)
|
||||
|
||||
static int devtmpfs_getdents(struct dfs_file *file, struct dirent *dirp, uint32_t count)
|
||||
{
|
||||
struct devtmpfs_file *d_file;
|
||||
rt_size_t index, end;
|
||||
struct dirent *d;
|
||||
struct devtmpfs_file *d_file, *n_file = RT_NULL, *tmp;
|
||||
struct devtmpfs_sb *superblock;
|
||||
|
||||
RT_ASSERT(file);
|
||||
@ -306,11 +301,6 @@ static int devtmpfs_getdents(struct dfs_file *file, struct dirent *dirp, uint32_
|
||||
d_file = devtmpfs_file_lookup(superblock, file->dentry->pathname);
|
||||
if (d_file)
|
||||
{
|
||||
rt_size_t index, end;
|
||||
struct dirent *d;
|
||||
struct devtmpfs_file *n_file;
|
||||
rt_list_t *list;
|
||||
|
||||
/* make integer count */
|
||||
count = (count / sizeof(struct dirent));
|
||||
if (count == 0)
|
||||
@ -322,12 +312,10 @@ static int devtmpfs_getdents(struct dfs_file *file, struct dirent *dirp, uint32_
|
||||
index = 0;
|
||||
count = 0;
|
||||
|
||||
rt_list_for_each(list, &d_file->subdirs)
|
||||
dfs_vfs_for_each_subnode(n_file, tmp, d_file, node)
|
||||
{
|
||||
if (index >= (rt_size_t)file->fpos)
|
||||
{
|
||||
n_file = rt_list_entry(list, struct devtmpfs_file, sibling);
|
||||
|
||||
d = dirp + count;
|
||||
if (n_file->type == TMPFS_TYPE_FILE)
|
||||
{
|
||||
@ -378,8 +366,7 @@ static int devtmpfs_symlink(struct dfs_dentry *parent_dentry, const char *target
|
||||
|
||||
strncpy(l_file->name, linkpath, DIRENT_NAME_MAX - 1);
|
||||
|
||||
rt_list_init(&(l_file->subdirs));
|
||||
rt_list_init(&(l_file->sibling));
|
||||
dfs_vfs_init_node(&l_file->node);
|
||||
l_file->sb = superblock;
|
||||
l_file->type = TMPFS_TYPE_FILE;
|
||||
l_file->mode = p_file->mode;
|
||||
@ -388,7 +375,7 @@ static int devtmpfs_symlink(struct dfs_dentry *parent_dentry, const char *target
|
||||
l_file->link = rt_strdup(target);
|
||||
|
||||
rt_spin_lock(&superblock->lock);
|
||||
rt_list_insert_after(&(p_file->subdirs), &(l_file->sibling));
|
||||
dfs_vfs_append_node(&p_file->node, &l_file->node);
|
||||
rt_spin_unlock(&superblock->lock);
|
||||
}
|
||||
}
|
||||
@ -460,7 +447,7 @@ static int devtmpfs_unlink(struct dfs_dentry *dentry)
|
||||
}
|
||||
|
||||
rt_spin_lock(&superblock->lock);
|
||||
rt_list_remove(&(d_file->sibling));
|
||||
dfs_vfs_remove_node(&d_file->node);
|
||||
rt_spin_unlock(&superblock->lock);
|
||||
|
||||
rt_free(d_file);
|
||||
@ -537,8 +524,7 @@ static struct dfs_vnode *devtmpfs_create_vnode(struct dfs_dentry *dentry, int ty
|
||||
|
||||
strncpy(d_file->name, file_name, DIRENT_NAME_MAX);
|
||||
|
||||
rt_list_init(&(d_file->subdirs));
|
||||
rt_list_init(&(d_file->sibling));
|
||||
dfs_vfs_init_node(&d_file->node);
|
||||
d_file->sb = superblock;
|
||||
|
||||
vnode->nlink = 1;
|
||||
@ -563,7 +549,7 @@ static struct dfs_vnode *devtmpfs_create_vnode(struct dfs_dentry *dentry, int ty
|
||||
d_file->mode = vnode->mode;
|
||||
|
||||
rt_spin_lock(&superblock->lock);
|
||||
rt_list_insert_after(&(p_file->subdirs), &(d_file->sibling));
|
||||
dfs_vfs_append_node(&p_file->node, &d_file->node);
|
||||
rt_spin_unlock(&superblock->lock);
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,166 @@
|
||||
# 进程文件系统 (procfs)
|
||||
|
||||
## 数据结构
|
||||
|
||||
```c
|
||||
struct proc_dentry
|
||||
{
|
||||
rt_uint32_t mode;
|
||||
rt_atomic_t ref_count;
|
||||
|
||||
struct proc_dentry *parent;
|
||||
struct dfs_vfs_node node;
|
||||
|
||||
const struct dfs_file_ops *fops;
|
||||
const struct proc_ops *ops;
|
||||
|
||||
char *name;
|
||||
void *data;
|
||||
};
|
||||
```
|
||||
|
||||
```log
|
||||
root { mode: S_IFDIR, ref_count: 1, parent: root, name: /, child->next: file1->node }
|
||||
|
|
||||
|—— file1 { mode: S_IFREG, ref_count: 1, parent: root, name: file1, node->next: link1->node }
|
||||
|—— link1 { mode: S_IFLNK, ref_count: 1, parent: root, name: link1, data: fullpath, node->next: dir1->node }
|
||||
|—— dir1 { mode: S_IFDIR, ref_count: 1, parent: root, name: dir1, node->next: file3->node, child->next: file2->node }
|
||||
| |
|
||||
| |—— dir2 { mode: S_IFDIR, ref_count: 1, parent: dir1, name: dir2, node->next: link2->node }
|
||||
| |—— link2 { mode: S_IFLNK, ref_count: 1, parent: dir1, name: link2, data: fullpath, node->next: file2->node }
|
||||
| |—— file2 { mode: S_IFREG, ref_count: 1, parent: dir1, name: file2 }
|
||||
|
|
||||
|—— file3 { mode: S_IFREG, ref_count: 1, parent: root, name: file3 }
|
||||
```
|
||||
|
||||
## API 介绍
|
||||
|
||||
```c
|
||||
struct proc_dentry *dfs_proc_find(const char *name);
|
||||
|
||||
struct proc_dentry *proc_mkdir_data(const char *name, mode_t mode, struct proc_dentry *parent,
|
||||
const struct dfs_file_ops *fops, void *data);
|
||||
struct proc_dentry *proc_mkdir_mode(const char *name, mode_t mode, struct proc_dentry *parent);
|
||||
struct proc_dentry *proc_mkdir(const char *name, struct proc_dentry *parent);
|
||||
|
||||
struct proc_dentry *proc_create_data(const char *name, mode_t mode, struct proc_dentry *parent,
|
||||
const struct dfs_file_ops *fops, void *data);
|
||||
|
||||
struct proc_dentry *proc_symlink(const char *name, struct proc_dentry *parent, const char *dest);
|
||||
|
||||
struct proc_dentry *proc_acquire(struct proc_dentry *dentry);
|
||||
void proc_release(struct proc_dentry *dentry);
|
||||
|
||||
void proc_remove(struct proc_dentry *dentry);
|
||||
```
|
||||
|
||||
- dfs_proc_find
|
||||
|
||||
查找指定节点,并返回节点数据指针
|
||||
|
||||
| 入参 | 说明 |
|
||||
| ---- | ---------------------------------------------------- |
|
||||
| name | 从 procfs 的 root 起始的完整路径,比如 “/dir1/file2” |
|
||||
|
||||
- proc_mkdir_data
|
||||
|
||||
创建一个目录,并返回节点数据指针
|
||||
|
||||
| 入参 | 说明 |
|
||||
| ------ | ------------------------------------------------------------ |
|
||||
| name | 从 procfs 的 root 起始的完整路径,比如 “/dir1/file2”<br />从 parent 起始的完整路径 |
|
||||
| mode | 权限配置 |
|
||||
| parent | 指定创建目录的起始节点 |
|
||||
| fops | 文件操作接口配置 |
|
||||
| data | 私有数据 |
|
||||
|
||||
- proc_mkdir_mode
|
||||
|
||||
创建一个目录,并返回节点数据指针
|
||||
|
||||
| 入参 | 说明 |
|
||||
| ------ | ------------------------------------------------------------ |
|
||||
| name | 从 procfs 的 root 起始的完整路径,比如 “/dir1/file2”<br />从 parent 起始的完整路径 |
|
||||
| mode | 权限配置 |
|
||||
| parent | 指定创建目录的起始节点 |
|
||||
|
||||
- proc_mkdir
|
||||
|
||||
创建一个目录,并返回节点数据指针
|
||||
|
||||
| 入参 | 说明 |
|
||||
| ---- | ------------------------------------------------------------ |
|
||||
| name | 从 procfs 的 root 起始的完整路径,比如 “/dir1/file2”<br />从 parent 起始的完整路径 |
|
||||
| mode | 权限配置 |
|
||||
|
||||
- proc_create_data
|
||||
|
||||
创建一个文件,并返回节点数据指针
|
||||
|
||||
| 入参 | 说明 |
|
||||
| ------ | ------------------------------------------------------------ |
|
||||
| name | 从 procfs 的 root 起始的完整路径,比如 “/dir1/file2”<br />从 parent 起始的完整路径 |
|
||||
| mode | 权限配置 |
|
||||
| parent | 指定创建文件的起始节点 |
|
||||
| fops | 文件操作接口配置 |
|
||||
| data | 私有数据 |
|
||||
|
||||
- proc_symlink
|
||||
|
||||
创建一个符号链接,并返回节点数据指针
|
||||
|
||||
| 入参 | 说明 |
|
||||
| ------ | ------------------------------------------------------------ |
|
||||
| name | 从 procfs 的 root 起始的完整路径,比如 “/dir1/file2”<br />从 parent 起始的完整路径 |
|
||||
| parent | 指定创建文件的起始节点 |
|
||||
| dest | 链接的目标文件完整路径 |
|
||||
|
||||
- proc_acquire
|
||||
|
||||
引用一个节点,并返回节点数据指针
|
||||
|
||||
| 入参 | 说明 |
|
||||
| ------ | -------------- |
|
||||
| dentry | 需要引用的节点 |
|
||||
|
||||
- proc_release
|
||||
|
||||
释放一个节点
|
||||
|
||||
| 入参 | 说明 |
|
||||
| ------ | -------------- |
|
||||
| dentry | 需要释放的节点 |
|
||||
|
||||
- proc_remove
|
||||
|
||||
删除一个节点包含子节点
|
||||
|
||||
| 入参 | 说明 |
|
||||
| ------ | -------------- |
|
||||
| dentry | 需要删除的节点 |
|
||||
|
||||
## msh 调试命令
|
||||
|
||||
- proc_dump
|
||||
|
||||
遍历打印指定节点含子节点的信息(名称、引用计数),比如 `proc_dump /dir1` 或者 `proc_dump`
|
||||
|
||||
- proc_remove
|
||||
|
||||
删除指定节点含子节点,比如 `proc_remove /dir1` 或者 `proc_remove /file3`
|
||||
|
||||
- proc_symlink
|
||||
|
||||
创建一个符号链接,`proc_symlink /link3 /mnt`
|
||||
|
||||
- proc_echo
|
||||
|
||||
创建一个空文件,`proc_echo /file4`
|
||||
|
||||
- proc_mkdir
|
||||
|
||||
创建一个空目录,`proc_mkdir /dir3`
|
||||
|
||||
- proc_pid
|
||||
|
||||
创建一个 pid 目录,`proc_pid /101`
|
@ -0,0 +1,11 @@
|
||||
# RT-Thread building script for component
|
||||
|
||||
from building import *
|
||||
|
||||
cwd = GetCurrentDir()
|
||||
src = Glob('*.c')
|
||||
CPPPATH = [cwd]
|
||||
|
||||
group = DefineGroup('Filesystem', src, depend = ['RT_USING_DFS', 'RT_USING_DFS_PROCFS'], CPPPATH = CPPPATH)
|
||||
|
||||
Return('group')
|
733
rt-thread-no-bsp/components/dfs/dfs_v2/filesystems/procfs/proc.c
Normal file
733
rt-thread-no-bsp/components/dfs/dfs_v2/filesystems/procfs/proc.c
Normal file
@ -0,0 +1,733 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2023, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
*/
|
||||
|
||||
#include "proc.h"
|
||||
#include "procfs.h"
|
||||
|
||||
#include <rthw.h>
|
||||
#include <rtdbg.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
/*
|
||||
* This is the root in the proc tree..
|
||||
*/
|
||||
static struct proc_dentry _proc_root = {
|
||||
.mode = S_IFDIR | (S_IRUSR | S_IRGRP | S_IROTH) | (S_IXUSR | S_IXGRP | S_IXOTH),
|
||||
.ref_count = 1,
|
||||
|
||||
.parent = &_proc_root,
|
||||
.node.sibling = RT_LIST_OBJECT_INIT(_proc_root.node.sibling),
|
||||
.node.subnode = RT_LIST_OBJECT_INIT(_proc_root.node.subnode),
|
||||
|
||||
.fops = RT_NULL,
|
||||
|
||||
.name = "/",
|
||||
.data = RT_NULL,
|
||||
};
|
||||
|
||||
static int _proc_find(struct proc_dentry **parent, const char *name)
|
||||
{
|
||||
struct proc_dentry *dentry = RT_NULL, *tmp;
|
||||
|
||||
dfs_vfs_for_each_subnode(dentry, tmp, (*parent), node)
|
||||
{
|
||||
if (dentry == RT_NULL)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (rt_strcmp(dentry->name, name) == 0)
|
||||
{
|
||||
*parent = dentry;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int proc_find(struct proc_dentry **parent, const char **name, rt_bool_t force_lookup)
|
||||
{
|
||||
int ret = 0;
|
||||
char *tmp = RT_NULL;
|
||||
|
||||
if (!(*parent))
|
||||
{
|
||||
*parent = &_proc_root;
|
||||
}
|
||||
|
||||
tmp = rt_strdup(*name);
|
||||
if (tmp)
|
||||
{
|
||||
char *begin = tmp, *end = RT_NULL;
|
||||
if (*begin == '/')
|
||||
{
|
||||
begin++;
|
||||
if (*begin == '\0')
|
||||
{
|
||||
rt_free(tmp);
|
||||
*parent = proc_acquire(*parent);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
while (1)
|
||||
{
|
||||
end = rt_strstr(begin, "/");
|
||||
if (end)
|
||||
{
|
||||
*end = '\0';
|
||||
ret = _proc_find(parent, begin);
|
||||
if (ret < 0 || !S_ISDIR((*parent)->mode))
|
||||
{
|
||||
*parent = RT_NULL;
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
begin = end + 1;
|
||||
}
|
||||
else if (force_lookup)
|
||||
{
|
||||
ret = _proc_find(parent, begin);
|
||||
if (ret < 0)
|
||||
{
|
||||
if ((*parent)->ops && (*parent)->ops->lookup)
|
||||
{
|
||||
*parent = (*parent)->ops->lookup(*parent, begin);
|
||||
if (*parent == RT_NULL)
|
||||
{
|
||||
ret = -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
*parent = RT_NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
*parent = proc_acquire(*parent);
|
||||
}
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
*parent = proc_acquire(*parent);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
*name = *name + (begin - tmp);
|
||||
|
||||
rt_free(tmp);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void *single_start(struct dfs_seq_file *seq, off_t *index)
|
||||
{
|
||||
return NULL + (*index == 0);
|
||||
}
|
||||
|
||||
static void *single_next(struct dfs_seq_file *seq, void *data, off_t *index)
|
||||
{
|
||||
++*index;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void single_stop(struct dfs_seq_file *seq, void *data)
|
||||
{
|
||||
}
|
||||
|
||||
static int proc_open(struct dfs_file *file)
|
||||
{
|
||||
struct proc_dentry *entry = (struct proc_dentry *)file->vnode->data;
|
||||
|
||||
if (entry->single_show)
|
||||
{
|
||||
struct dfs_seq_ops *seq_ops = (struct dfs_seq_ops *)rt_calloc(1, sizeof(struct dfs_seq_ops));
|
||||
if (seq_ops)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
seq_ops->start = single_start;
|
||||
seq_ops->next = single_next;
|
||||
seq_ops->stop = single_stop;
|
||||
seq_ops->show = entry->single_show;
|
||||
|
||||
ret = dfs_seq_open(file, seq_ops);
|
||||
if (ret != 0)
|
||||
{
|
||||
rt_free(seq_ops);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return dfs_seq_open(file, entry->seq_ops);
|
||||
}
|
||||
|
||||
static int proc_close(struct dfs_file *file)
|
||||
{
|
||||
struct dfs_seq_file *seq = file->data;
|
||||
struct proc_dentry *entry = (struct proc_dentry *)file->vnode->data;
|
||||
|
||||
if (seq && entry->single_show && seq->ops)
|
||||
{
|
||||
rt_free((void *)seq->ops);
|
||||
seq->ops = RT_NULL;
|
||||
}
|
||||
|
||||
return dfs_seq_release(file);
|
||||
}
|
||||
|
||||
static const struct dfs_file_ops proc_file_ops = {
|
||||
.open = proc_open,
|
||||
.read = dfs_seq_read,
|
||||
.lseek = dfs_seq_lseek,
|
||||
.close = proc_close,
|
||||
};
|
||||
|
||||
static struct proc_dentry *proc_create(struct proc_dentry **parent, const char *name, mode_t mode)
|
||||
{
|
||||
int ret = 0;
|
||||
struct proc_dentry *dentry = RT_NULL;
|
||||
|
||||
ret = proc_find(parent, &name, 0);
|
||||
if (ret >= 0)
|
||||
{
|
||||
dentry = *parent;
|
||||
ret = proc_find(&dentry, &name, 1);
|
||||
if (ret < 0)
|
||||
{
|
||||
dentry = rt_calloc(1, sizeof(struct proc_dentry));
|
||||
if (dentry)
|
||||
{
|
||||
dentry->mode = mode;
|
||||
dentry->ref_count = 1;
|
||||
dentry->name = rt_strdup(name);
|
||||
dfs_vfs_init_node(&dentry->node);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
proc_release(dentry);
|
||||
dentry = RT_NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return dentry;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The dentry reference count is incremented by one
|
||||
*
|
||||
* @param dentry
|
||||
*
|
||||
* @return dentry
|
||||
*/
|
||||
struct proc_dentry *proc_acquire(struct proc_dentry *dentry)
|
||||
{
|
||||
if (dentry)
|
||||
{
|
||||
dentry->ref_count += 1;
|
||||
}
|
||||
|
||||
return dentry;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The dentry reference count is minus one, or release
|
||||
*
|
||||
* @param dentry
|
||||
*
|
||||
* @return none
|
||||
*/
|
||||
void proc_release(struct proc_dentry *dentry)
|
||||
{
|
||||
if (dentry)
|
||||
{
|
||||
if (dentry->ref_count == 1)
|
||||
{
|
||||
if (dentry->name)
|
||||
{
|
||||
rt_free(dentry->name);
|
||||
}
|
||||
|
||||
if (S_ISLNK(dentry->mode) && dentry->data)
|
||||
{
|
||||
rt_free(dentry->data);
|
||||
}
|
||||
|
||||
rt_free(dentry);
|
||||
}
|
||||
else
|
||||
{
|
||||
dentry->ref_count -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static struct proc_dentry *proc_register(struct proc_dentry *parent, struct proc_dentry *child)
|
||||
{
|
||||
child->parent = parent;
|
||||
dfs_vfs_append_node(&parent->node, &child->node);
|
||||
child->ref_count += 1;
|
||||
child->pid = parent->pid;
|
||||
|
||||
return child;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Make a dir
|
||||
*
|
||||
* @param name fullpath based on _proc_root or parent
|
||||
* @param mode permission configuration
|
||||
* @param parent can be empty
|
||||
* @param fops
|
||||
* @param data
|
||||
*
|
||||
* @return dentry
|
||||
*/
|
||||
struct proc_dentry *proc_mkdir_data(const char *name, mode_t mode, struct proc_dentry *parent,
|
||||
const struct dfs_file_ops *fops, void *data)
|
||||
{
|
||||
struct proc_dentry *dentry, *_parent = parent;
|
||||
|
||||
if (mode == 0)
|
||||
mode = (S_IRUSR | S_IRGRP | S_IROTH) | (S_IXUSR | S_IXGRP | S_IXOTH);
|
||||
|
||||
dentry = proc_create(&_parent, name, S_IFDIR | mode);
|
||||
if (dentry)
|
||||
{
|
||||
dentry->fops = fops;
|
||||
dentry->data = data;
|
||||
|
||||
dentry = proc_register(_parent, dentry);
|
||||
}
|
||||
proc_release(_parent);
|
||||
|
||||
return dentry;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Make a dir
|
||||
*
|
||||
* @param name fullpath based on _proc_root or parent
|
||||
* @param mode permission configuration
|
||||
* @param parent can be empty
|
||||
*
|
||||
* @return dentry
|
||||
*/
|
||||
struct proc_dentry *proc_mkdir_mode(const char *name, mode_t mode, struct proc_dentry *parent)
|
||||
{
|
||||
return proc_mkdir_data(name, mode, parent, NULL, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Make a dir
|
||||
*
|
||||
* @param name fullpath based on _proc_root or parent
|
||||
* @param parent can be empty
|
||||
*
|
||||
* @return dentry
|
||||
*/
|
||||
struct proc_dentry *proc_mkdir(const char *name, struct proc_dentry *parent)
|
||||
{
|
||||
return proc_mkdir_data(name, 0, parent, NULL, NULL);
|
||||
}
|
||||
|
||||
static struct proc_dentry *proc_create_reg(const char *name, mode_t mode, struct proc_dentry **parent)
|
||||
{
|
||||
struct proc_dentry *dentry = RT_NULL;
|
||||
|
||||
if ((mode & S_IFMT) == 0)
|
||||
mode |= S_IFREG;
|
||||
if ((mode & (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO)) == 0)
|
||||
mode |= S_IRUSR | S_IRGRP | S_IROTH;
|
||||
|
||||
if (!S_ISREG(mode))
|
||||
{
|
||||
*parent = RT_NULL;
|
||||
return dentry;
|
||||
}
|
||||
|
||||
return proc_create(parent, name, mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Make a file
|
||||
*
|
||||
* @param name fullpath based on _proc_root or parent
|
||||
* @param mode permission configuration
|
||||
* @param parent can be empty
|
||||
* @param fops
|
||||
* @param data
|
||||
*
|
||||
* @return dentry
|
||||
*/
|
||||
struct proc_dentry *proc_create_data(const char *name, mode_t mode, struct proc_dentry *parent,
|
||||
const struct dfs_file_ops *fops, void *data)
|
||||
{
|
||||
struct proc_dentry *dentry, *_parent = parent;
|
||||
|
||||
dentry = proc_create_reg(name, mode, &_parent);
|
||||
if (dentry)
|
||||
{
|
||||
dentry->fops = fops ? fops : &proc_file_ops;
|
||||
dentry->data = data;
|
||||
|
||||
dentry = proc_register(_parent, dentry);
|
||||
}
|
||||
proc_release(_parent);
|
||||
|
||||
return dentry;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Make a file
|
||||
*
|
||||
* @param name fullpath based on _proc_root or parent
|
||||
* @param mode permission configuration
|
||||
* @param parent can be empty
|
||||
* @param show
|
||||
* @param data
|
||||
*
|
||||
* @return dentry
|
||||
*/
|
||||
struct proc_dentry *proc_create_single_data(const char *name, mode_t mode, struct proc_dentry *parent,
|
||||
int (*show)(struct dfs_seq_file *, void *), void *data)
|
||||
{
|
||||
struct proc_dentry *dentry, *_parent = parent;
|
||||
|
||||
dentry = proc_create_reg(name, mode, &_parent);
|
||||
if (dentry)
|
||||
{
|
||||
dentry->fops = &proc_file_ops;
|
||||
dentry->single_show = show;
|
||||
dentry->data = data;
|
||||
|
||||
dentry = proc_register(_parent, dentry);
|
||||
}
|
||||
proc_release(_parent);
|
||||
|
||||
return dentry;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Make a symlink
|
||||
*
|
||||
* @param name fullpath based on _proc_root or parent
|
||||
* @param parent can be empty
|
||||
* @param dest link file fullpath
|
||||
*
|
||||
* @return dentry
|
||||
*/
|
||||
struct proc_dentry *proc_symlink(const char *name, struct proc_dentry *parent, const char *dest)
|
||||
{
|
||||
struct proc_dentry *dentry, *_parent = parent;
|
||||
|
||||
dentry = proc_create(&_parent, name, (S_IFLNK | (S_IRUSR | S_IRGRP | S_IROTH)
|
||||
| (S_IWUSR | S_IWGRP | S_IWOTH) | (S_IXUSR | S_IXGRP | S_IXOTH)));
|
||||
if (dentry)
|
||||
{
|
||||
dentry->data = (void *)rt_strdup(dest);
|
||||
if (dentry->data)
|
||||
{
|
||||
dentry = proc_register(_parent, dentry);
|
||||
}
|
||||
else
|
||||
{
|
||||
proc_release(dentry);
|
||||
dentry = NULL;
|
||||
}
|
||||
}
|
||||
proc_release(_parent);
|
||||
|
||||
return dentry;
|
||||
}
|
||||
|
||||
static void remove_proc_subtree(struct proc_dentry *dentry)
|
||||
{
|
||||
struct proc_dentry *iter = RT_NULL, *iter_tmp, *tmp = RT_NULL;
|
||||
|
||||
dfs_vfs_for_each_subnode(iter, iter_tmp, dentry, node)
|
||||
{
|
||||
if (iter == RT_NULL)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (tmp)
|
||||
{
|
||||
proc_release(tmp);
|
||||
tmp = RT_NULL;
|
||||
}
|
||||
|
||||
tmp = iter;
|
||||
|
||||
if (S_ISDIR(dentry->mode))
|
||||
{
|
||||
remove_proc_subtree(iter);
|
||||
}
|
||||
}
|
||||
|
||||
if (tmp)
|
||||
{
|
||||
proc_release(tmp);
|
||||
tmp = RT_NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief remove a dentry
|
||||
*
|
||||
* @param dentry
|
||||
*
|
||||
* @return none
|
||||
*/
|
||||
void proc_remove(struct proc_dentry *dentry)
|
||||
{
|
||||
if (dentry && dentry != &_proc_root)
|
||||
{
|
||||
if (S_ISDIR(dentry->mode))
|
||||
{
|
||||
remove_proc_subtree(dentry);
|
||||
}
|
||||
|
||||
dfs_vfs_remove_node(&dentry->node);
|
||||
proc_release(dentry);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief find dentry exist
|
||||
*
|
||||
* @param name fullpath based on _proc_root
|
||||
*
|
||||
* @return dentry
|
||||
*/
|
||||
struct proc_dentry *dfs_proc_find(const char *name)
|
||||
{
|
||||
struct proc_dentry *dentry = RT_NULL;
|
||||
|
||||
proc_find(&dentry, &name, 1);
|
||||
|
||||
return dentry;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief remove a dentry on parent
|
||||
*
|
||||
* @param name fullpath based on parent
|
||||
* @param parent
|
||||
*
|
||||
* @return none
|
||||
*/
|
||||
void proc_remove_dentry(const char *name, struct proc_dentry *parent)
|
||||
{
|
||||
struct proc_dentry *dentry = parent;
|
||||
|
||||
if (proc_find(&dentry, &name, 1) >= 0)
|
||||
{
|
||||
proc_remove(dentry);
|
||||
proc_release(dentry);
|
||||
}
|
||||
}
|
||||
|
||||
#define _COLOR_RED "\033[31m"
|
||||
#define _COLOR_GREEN "\033[32m"
|
||||
#define _COLOR_BLUE "\033[34m"
|
||||
#define _COLOR_CYAN "\033[36m"
|
||||
#define _COLOR_WHITE "\033[37m"
|
||||
#define _COLOR_NORMAL "\033[0m"
|
||||
|
||||
static void dump_proc_subtree(struct proc_dentry *dentry, int tab)
|
||||
{
|
||||
struct proc_dentry *iter = RT_NULL, *tmp;
|
||||
|
||||
dfs_vfs_for_each_subnode(iter, tmp, dentry, node)
|
||||
{
|
||||
if (iter == RT_NULL)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
for(int i = 0; i < tab; i ++)
|
||||
{
|
||||
rt_kprintf("%-4s", i + 1 >= tab ? "|-" : " ");
|
||||
}
|
||||
|
||||
if (S_ISDIR(iter->mode))
|
||||
{
|
||||
rt_kprintf(_COLOR_BLUE "%-20s" _COLOR_NORMAL " %d\n", iter->name, iter->ref_count);
|
||||
dump_proc_subtree(iter, tab + 1);
|
||||
}
|
||||
else if (S_ISLNK(iter->mode))
|
||||
{
|
||||
rt_kprintf(_COLOR_CYAN "%-20s" _COLOR_NORMAL " %d\n", iter->name, iter->ref_count);
|
||||
}
|
||||
else
|
||||
{
|
||||
rt_kprintf("%-20s %d\n", iter->name, iter->ref_count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void proc_dump(struct proc_dentry *dentry)
|
||||
{
|
||||
if (dentry)
|
||||
{
|
||||
if (S_ISDIR(dentry->mode))
|
||||
{
|
||||
rt_kprintf(_COLOR_BLUE "%-20s" _COLOR_NORMAL " %d\n", dentry->name, dentry->ref_count);
|
||||
dump_proc_subtree(dentry, 1);
|
||||
}
|
||||
else if (S_ISLNK(dentry->mode))
|
||||
{
|
||||
rt_kprintf(_COLOR_CYAN "%-20s" _COLOR_NORMAL " %d\n", dentry->name, dentry->ref_count);
|
||||
}
|
||||
else
|
||||
{
|
||||
rt_kprintf("%-20s %d\n", dentry->name, dentry->ref_count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int msh_proc_dump(int argc, char** argv)
|
||||
{
|
||||
const char *name = argc > 1 ? argv[1] : "/";
|
||||
struct proc_dentry *dentry = RT_NULL;
|
||||
|
||||
int ret = proc_find(&dentry, &name, 1);
|
||||
if (ret >= 0)
|
||||
{
|
||||
proc_dump(dentry);
|
||||
}
|
||||
proc_release(dentry);
|
||||
|
||||
return 0;
|
||||
}
|
||||
MSH_CMD_EXPORT_ALIAS(msh_proc_dump, proc_dump, proc dump);
|
||||
|
||||
static int msh_proc_remove(int argc, char** argv)
|
||||
{
|
||||
if (argc > 1)
|
||||
{
|
||||
const char *name = argv[1];
|
||||
struct proc_dentry *dentry = RT_NULL;
|
||||
|
||||
int ret = proc_find(&dentry, &name, 1);
|
||||
if (ret >= 0)
|
||||
{
|
||||
if (dentry != &_proc_root)
|
||||
{
|
||||
proc_remove(dentry);
|
||||
}
|
||||
else
|
||||
{
|
||||
struct proc_dentry *iter = RT_NULL, *iter_tmp, *tmp = RT_NULL;
|
||||
|
||||
dfs_vfs_for_each_subnode(iter, iter_tmp, dentry, node)
|
||||
{
|
||||
if (iter == RT_NULL)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (tmp)
|
||||
{
|
||||
proc_remove(tmp);
|
||||
}
|
||||
|
||||
tmp = iter;
|
||||
}
|
||||
|
||||
if (tmp)
|
||||
{
|
||||
proc_remove(tmp);
|
||||
}
|
||||
}
|
||||
}
|
||||
proc_release(dentry);
|
||||
}
|
||||
else
|
||||
{
|
||||
rt_kprintf("proc_remove path\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
MSH_CMD_EXPORT_ALIAS(msh_proc_remove, proc_remove, proc remove);
|
||||
|
||||
static int msh_proc_symlink(int argc, char** argv)
|
||||
{
|
||||
if (argc > 2)
|
||||
{
|
||||
struct proc_dentry *entry = proc_symlink(argv[1], 0, argv[2]);
|
||||
if (entry)
|
||||
{
|
||||
proc_release(entry);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
rt_kprintf("proc_symlink path dest\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
MSH_CMD_EXPORT_ALIAS(msh_proc_symlink, proc_symlink, proc symlink);
|
||||
|
||||
static int msh_proc_echo(int argc, char** argv)
|
||||
{
|
||||
if (argc > 1)
|
||||
{
|
||||
for(int i = 1; i <= argc - 1; i ++)
|
||||
{
|
||||
struct proc_dentry *entry = proc_create_data(argv[i], 0, 0, 0, 0);
|
||||
if (entry)
|
||||
{
|
||||
proc_release(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
rt_kprintf("proc_echo path\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
MSH_CMD_EXPORT_ALIAS(msh_proc_echo, proc_echo, proc echo);
|
||||
|
||||
static int msh_proc_mkdir(int argc, char** argv)
|
||||
{
|
||||
if (argc > 1)
|
||||
{
|
||||
for(int i = 1; i <= argc - 1; i ++)
|
||||
{
|
||||
struct proc_dentry *entry = proc_mkdir(argv[i], 0);
|
||||
if (entry)
|
||||
{
|
||||
proc_release(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
rt_kprintf("proc_mkdir path\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
MSH_CMD_EXPORT_ALIAS(msh_proc_mkdir, proc_mkdir, proc mkdir);
|
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2023, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
*/
|
||||
|
||||
#ifndef __PROC_H__
|
||||
#define __PROC_H__
|
||||
|
||||
#include <dfs_file.h>
|
||||
#include <dfs_seq_file.h>
|
||||
#include <dfs_vfs.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
struct proc_dentry;
|
||||
|
||||
struct proc_ops
|
||||
{
|
||||
struct proc_dentry *(*lookup)(struct proc_dentry *parent, const char *name);
|
||||
int (*readlink)(struct proc_dentry *dentry, char *buf, int len);
|
||||
};
|
||||
|
||||
struct proc_dentry
|
||||
{
|
||||
rt_uint32_t mode;
|
||||
rt_atomic_t ref_count;
|
||||
|
||||
struct proc_dentry *parent;
|
||||
struct dfs_vfs_node node;
|
||||
|
||||
const struct dfs_file_ops *fops;
|
||||
const struct proc_ops *ops;
|
||||
const struct dfs_seq_ops *seq_ops;
|
||||
int (*single_show)(struct dfs_seq_file *seq, void *data);
|
||||
|
||||
int pid;
|
||||
|
||||
char *name;
|
||||
void *data;
|
||||
};
|
||||
|
||||
struct proc_dentry *dfs_proc_find(const char *name);
|
||||
|
||||
struct proc_dentry *proc_mkdir_data(const char *name, mode_t mode, struct proc_dentry *parent,
|
||||
const struct dfs_file_ops *fops, void *data);
|
||||
struct proc_dentry *proc_mkdir_mode(const char *name, mode_t mode, struct proc_dentry *parent);
|
||||
struct proc_dentry *proc_mkdir(const char *name, struct proc_dentry *parent);
|
||||
|
||||
struct proc_dentry *proc_create_data(const char *name, mode_t mode, struct proc_dentry *parent,
|
||||
const struct dfs_file_ops *fops, void *data);
|
||||
struct proc_dentry *proc_create_single_data(const char *name, mode_t mode, struct proc_dentry *parent,
|
||||
int (*show)(struct dfs_seq_file *, void *), void *data);
|
||||
|
||||
struct proc_dentry *proc_symlink(const char *name, struct proc_dentry *parent, const char *dest);
|
||||
|
||||
struct proc_dentry *proc_acquire(struct proc_dentry *dentry);
|
||||
void proc_release(struct proc_dentry *dentry);
|
||||
|
||||
void proc_remove(struct proc_dentry *dentry);
|
||||
void proc_remove_dentry(const char *name, struct proc_dentry *parent);
|
||||
|
||||
int proc_pid(int pid);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2023, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
*/
|
||||
|
||||
#include "proc.h"
|
||||
#include "procfs.h"
|
||||
|
||||
#include <rthw.h>
|
||||
#include <rtdbg.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <dfs_dentry.h>
|
||||
|
||||
static char *__proc_cmdline = NULL;
|
||||
|
||||
int proc_cmdline_save(const char *cmdline)
|
||||
{
|
||||
if (__proc_cmdline)
|
||||
{
|
||||
free(__proc_cmdline);
|
||||
__proc_cmdline = NULL;
|
||||
}
|
||||
|
||||
__proc_cmdline = strdup(cmdline);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int single_show(struct dfs_seq_file *seq, void *data)
|
||||
{
|
||||
if (__proc_cmdline)
|
||||
{
|
||||
dfs_seq_puts(seq, __proc_cmdline);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int proc_cmdline_init(void)
|
||||
{
|
||||
struct proc_dentry *dentry = proc_create_single_data("cmdline", 0, NULL, single_show, NULL);
|
||||
proc_release(dentry);
|
||||
|
||||
return 0;
|
||||
}
|
||||
INIT_ENV_EXPORT(proc_cmdline_init);
|
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2023, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
*/
|
||||
|
||||
#include "proc.h"
|
||||
#include "procfs.h"
|
||||
|
||||
#include <rthw.h>
|
||||
#include <rtdbg.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <dfs_dentry.h>
|
||||
|
||||
static void *seq_start(struct dfs_seq_file *seq, off_t *index)
|
||||
{
|
||||
off_t i = *index; // seq->index
|
||||
|
||||
return NULL + (i == 0);
|
||||
}
|
||||
|
||||
static void seq_stop(struct dfs_seq_file *seq, void *data)
|
||||
{
|
||||
}
|
||||
|
||||
static void *seq_next(struct dfs_seq_file *seq, void *data, off_t *index)
|
||||
{
|
||||
/* data: The return value of the start or next*/
|
||||
off_t i = *index + 1; // seq->index
|
||||
|
||||
*index = i;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int seq_show(struct dfs_seq_file *seq, void *data)
|
||||
{
|
||||
/* data: The return value of the start or next*/
|
||||
dfs_seq_puts(seq, "rt_weak const struct dfs_seq_ops *cpuinfo_get_seq_ops(void)\n--need your own function--\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dfs_seq_ops seq_ops = {
|
||||
.start = seq_start,
|
||||
.stop = seq_stop,
|
||||
.next = seq_next,
|
||||
.show = seq_show,
|
||||
};
|
||||
|
||||
rt_weak const struct dfs_seq_ops *cpuinfo_get_seq_ops(void)
|
||||
{
|
||||
return &seq_ops;
|
||||
}
|
||||
|
||||
static int proc_open(struct dfs_file *file)
|
||||
{
|
||||
return dfs_seq_open(file, cpuinfo_get_seq_ops());
|
||||
}
|
||||
|
||||
static int proc_close(struct dfs_file *file)
|
||||
{
|
||||
return dfs_seq_release(file);
|
||||
}
|
||||
|
||||
static const struct dfs_file_ops file_ops = {
|
||||
.open = proc_open,
|
||||
.read = dfs_seq_read,
|
||||
.lseek = dfs_seq_lseek,
|
||||
.close = proc_close,
|
||||
};
|
||||
|
||||
int proc_cpuinfo_init(void)
|
||||
{
|
||||
struct proc_dentry *dentry = proc_create_data("cpuinfo", 0, NULL, &file_ops, NULL);
|
||||
proc_release(dentry);
|
||||
|
||||
return 0;
|
||||
}
|
||||
INIT_ENV_EXPORT(proc_cpuinfo_init);
|
@ -0,0 +1,307 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2023, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
*/
|
||||
|
||||
#include "proc.h"
|
||||
#include "procfs.h"
|
||||
|
||||
#include <rthw.h>
|
||||
#include <rtdbg.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <dfs_dentry.h>
|
||||
#include <rthw.h>
|
||||
#include <rtthread.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
#define LIST_FIND_OBJ_NR 8
|
||||
|
||||
struct device_show
|
||||
{
|
||||
char *buf;
|
||||
int size;
|
||||
int len;
|
||||
int index;
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
rt_list_t *list;
|
||||
rt_list_t **array;
|
||||
rt_uint8_t type;
|
||||
int nr; /* input: max nr, can't be 0 */
|
||||
int nr_out; /* out: got nr */
|
||||
} list_get_next_t;
|
||||
|
||||
static void list_find_init(list_get_next_t *p, rt_uint8_t type, rt_list_t **array, int nr)
|
||||
{
|
||||
struct rt_object_information *info;
|
||||
rt_list_t *list;
|
||||
|
||||
info = rt_object_get_information((enum rt_object_class_type)type);
|
||||
list = &info->object_list;
|
||||
|
||||
p->list = list;
|
||||
p->type = type;
|
||||
p->array = array;
|
||||
p->nr = nr;
|
||||
p->nr_out = 0;
|
||||
}
|
||||
|
||||
static rt_list_t *list_get_next(rt_list_t *current, list_get_next_t *arg)
|
||||
{
|
||||
int first_flag = 0;
|
||||
rt_base_t level;
|
||||
rt_list_t *node, *list;
|
||||
rt_list_t **array;
|
||||
struct rt_object_information *info;
|
||||
int nr;
|
||||
|
||||
arg->nr_out = 0;
|
||||
|
||||
if (!arg->nr || !arg->type)
|
||||
{
|
||||
return (rt_list_t *)RT_NULL;
|
||||
}
|
||||
|
||||
list = arg->list;
|
||||
info = rt_list_entry(list, struct rt_object_information, object_list);
|
||||
|
||||
if (!current) /* find first */
|
||||
{
|
||||
node = list;
|
||||
first_flag = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
node = current;
|
||||
}
|
||||
|
||||
level = rt_spin_lock_irqsave(&info->spinlock);
|
||||
|
||||
if (!first_flag)
|
||||
{
|
||||
struct rt_object *obj;
|
||||
/* The node in the list? */
|
||||
obj = rt_list_entry(node, struct rt_object, list);
|
||||
if ((obj->type & ~RT_Object_Class_Static) != arg->type)
|
||||
{
|
||||
rt_spin_unlock_irqrestore(&info->spinlock, level);
|
||||
return (rt_list_t *)RT_NULL;
|
||||
}
|
||||
}
|
||||
|
||||
nr = 0;
|
||||
array = arg->array;
|
||||
while (1)
|
||||
{
|
||||
node = node->next;
|
||||
|
||||
if (node == list)
|
||||
{
|
||||
node = (rt_list_t *)RT_NULL;
|
||||
break;
|
||||
}
|
||||
nr++;
|
||||
*array++ = node;
|
||||
if (nr == arg->nr)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
rt_spin_unlock_irqrestore(&info->spinlock, level);
|
||||
arg->nr_out = nr;
|
||||
return node;
|
||||
}
|
||||
|
||||
static char *const device_type_str[RT_Device_Class_Unknown] =
|
||||
{
|
||||
"Character Device",
|
||||
"Block Device",
|
||||
"Network Interface",
|
||||
"MTD Device",
|
||||
"CAN Device",
|
||||
"RTC",
|
||||
"Sound Device",
|
||||
"Graphic Device",
|
||||
"I2C Bus",
|
||||
"USB Slave Device",
|
||||
"USB Host Bus",
|
||||
"USB OTG Bus",
|
||||
"SPI Bus",
|
||||
"SPI Device",
|
||||
"SDIO Bus",
|
||||
"PM Pseudo Device",
|
||||
"Pipe",
|
||||
"Portal Device",
|
||||
"Timer Device",
|
||||
"Miscellaneous Device",
|
||||
"Sensor Device",
|
||||
"Touch Device",
|
||||
"Phy Device",
|
||||
"Security Device",
|
||||
"WLAN Device",
|
||||
"Pin Device",
|
||||
"ADC Device",
|
||||
"DAC Device",
|
||||
"WDT Device",
|
||||
"PWM Device",
|
||||
"Bus Device",
|
||||
};
|
||||
|
||||
static void save_info(struct device_show *dev, char *dev_name)
|
||||
{
|
||||
char tmp[256] = {0};
|
||||
int len;
|
||||
|
||||
dev->index ++;
|
||||
|
||||
rt_snprintf(tmp, 256, "%d %s\n", dev->index, dev_name);
|
||||
tmp[255] = 0;
|
||||
|
||||
len = rt_strlen(tmp);
|
||||
if (dev->size > dev->len + len)
|
||||
{
|
||||
strcat(dev->buf, tmp);
|
||||
dev->len += len;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (dev->buf == RT_NULL)
|
||||
{
|
||||
dev->buf = rt_calloc(1, 4096);
|
||||
}
|
||||
else
|
||||
{
|
||||
dev->buf = rt_realloc(dev->buf, dev->size + 4096);
|
||||
}
|
||||
if (dev->buf)
|
||||
{
|
||||
dev->size += 4096;
|
||||
strcat(dev->buf, tmp);
|
||||
dev->len += len;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void list_device(struct device_show *dev)
|
||||
{
|
||||
rt_base_t level;
|
||||
list_get_next_t find_arg;
|
||||
struct rt_object_information *info;
|
||||
rt_list_t *obj_list[LIST_FIND_OBJ_NR];
|
||||
rt_list_t *next = (rt_list_t *)RT_NULL;
|
||||
|
||||
list_find_init(&find_arg, RT_Object_Class_Device, obj_list, sizeof(obj_list) / sizeof(obj_list[0]));
|
||||
info = rt_list_entry(find_arg.list, struct rt_object_information, object_list);
|
||||
|
||||
do
|
||||
{
|
||||
next = list_get_next(next, &find_arg);
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < find_arg.nr_out; i++)
|
||||
{
|
||||
struct rt_object *obj;
|
||||
struct rt_device *device;
|
||||
|
||||
obj = rt_list_entry(obj_list[i], struct rt_object, list);
|
||||
level = rt_spin_lock_irqsave(&info->spinlock);
|
||||
if ((obj->type & ~RT_Object_Class_Static) != find_arg.type)
|
||||
{
|
||||
rt_spin_unlock_irqrestore(&info->spinlock, level);
|
||||
continue;
|
||||
}
|
||||
|
||||
rt_spin_unlock_irqrestore(&info->spinlock, level);
|
||||
|
||||
device = (struct rt_device *)obj;
|
||||
|
||||
if (device->type < RT_Device_Class_Unknown)
|
||||
{
|
||||
save_info(dev + device->type, device->parent.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
while (next != (rt_list_t *)RT_NULL);
|
||||
}
|
||||
|
||||
static int show_info(struct dfs_seq_file *seq)
|
||||
{
|
||||
struct device_show _show[RT_Device_Class_Unknown] = {0};
|
||||
|
||||
list_device(_show);
|
||||
|
||||
for (int i = 0; i < RT_Device_Class_Unknown; i++)
|
||||
{
|
||||
if (_show[i].buf)
|
||||
{
|
||||
dfs_seq_printf(seq, "%s:\n", device_type_str[i]);
|
||||
dfs_seq_write(seq, _show[i].buf, _show[i].len);
|
||||
dfs_seq_putc(seq, '\n');
|
||||
|
||||
rt_free(_show[i].buf);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *seq_start(struct dfs_seq_file *seq, off_t *index)
|
||||
{
|
||||
off_t i = *index; // seq->index
|
||||
|
||||
return NULL + (i == 0);
|
||||
}
|
||||
|
||||
static void seq_stop(struct dfs_seq_file *seq, void *data)
|
||||
{
|
||||
}
|
||||
|
||||
static void *seq_next(struct dfs_seq_file *seq, void *data, off_t *index)
|
||||
{
|
||||
/* data: The return value of the start or next*/
|
||||
off_t i = *index + 1; // seq->index
|
||||
|
||||
*index = i;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int seq_show(struct dfs_seq_file *seq, void *data)
|
||||
{
|
||||
/* data: The return value of the start or next*/
|
||||
show_info(seq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dfs_seq_ops seq_ops = {
|
||||
.start = seq_start,
|
||||
.stop = seq_stop,
|
||||
.next = seq_next,
|
||||
.show = seq_show,
|
||||
};
|
||||
|
||||
int proc_devices_init(void)
|
||||
{
|
||||
struct proc_dentry *dentry = proc_create_data("devices", 0, NULL, NULL, NULL);
|
||||
if (dentry)
|
||||
{
|
||||
dentry->seq_ops = &seq_ops;
|
||||
}
|
||||
proc_release(dentry);
|
||||
|
||||
return 0;
|
||||
}
|
||||
INIT_ENV_EXPORT(proc_devices_init);
|
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2023, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
*/
|
||||
|
||||
#include "proc.h"
|
||||
#include "procfs.h"
|
||||
|
||||
#include <rthw.h>
|
||||
#include <rtdbg.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <dfs_dentry.h>
|
||||
#include <dfs_fs.h>
|
||||
|
||||
static void *seq_start(struct dfs_seq_file *seq, off_t *index)
|
||||
{
|
||||
off_t i = *index; // seq->index
|
||||
struct dfs_filesystem_type *fs = dfs_filesystems();
|
||||
|
||||
if (fs)
|
||||
{
|
||||
while (i--)
|
||||
{
|
||||
fs = fs->next;
|
||||
if (!fs)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return fs;
|
||||
}
|
||||
|
||||
static void seq_stop(struct dfs_seq_file *seq, void *data)
|
||||
{
|
||||
}
|
||||
|
||||
static void *seq_next(struct dfs_seq_file *seq, void *data, off_t *index)
|
||||
{
|
||||
/* data: The return value of the start or next*/
|
||||
off_t i = *index + 1; // seq->index
|
||||
struct dfs_filesystem_type *fs = (struct dfs_filesystem_type *)data;
|
||||
|
||||
*index = i;
|
||||
|
||||
return fs->next;
|
||||
}
|
||||
|
||||
static int seq_show(struct dfs_seq_file *seq, void *data)
|
||||
{
|
||||
/* data: The return value of the start or next*/
|
||||
struct dfs_filesystem_type *fs = (struct dfs_filesystem_type *)data;
|
||||
|
||||
dfs_seq_printf(seq, "%-9s%s\n", (fs->fs_ops->flags == FS_NEED_DEVICE) ? "" : "nodev", fs->fs_ops->name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dfs_seq_ops seq_ops = {
|
||||
.start = seq_start,
|
||||
.stop = seq_stop,
|
||||
.next = seq_next,
|
||||
.show = seq_show,
|
||||
};
|
||||
|
||||
int proc_filesystems_init(void)
|
||||
{
|
||||
struct proc_dentry *dentry = proc_create_data("filesystems", 0, NULL, NULL, NULL);
|
||||
if (dentry)
|
||||
{
|
||||
dentry->seq_ops = &seq_ops;
|
||||
}
|
||||
proc_release(dentry);
|
||||
|
||||
return 0;
|
||||
}
|
||||
INIT_ENV_EXPORT(proc_filesystems_init);
|
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2023, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
*/
|
||||
|
||||
#include "proc.h"
|
||||
#include "procfs.h"
|
||||
|
||||
#include <rthw.h>
|
||||
#include <rtdbg.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <dfs_dentry.h>
|
||||
#include <mm_page.h>
|
||||
|
||||
|
||||
extern void rt_memory_info(rt_size_t *total,
|
||||
rt_size_t *used,
|
||||
rt_size_t *max_used);
|
||||
|
||||
static int single_show(struct dfs_seq_file *seq, void *data)
|
||||
{
|
||||
dfs_seq_printf(seq, "0.13 0.16 0.17 1/1035 380436\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int proc_loadavg_init(void)
|
||||
{
|
||||
struct proc_dentry *dentry = proc_create_single_data("loadavg", 0, NULL, single_show, NULL);
|
||||
proc_release(dentry);
|
||||
|
||||
return 0;
|
||||
}
|
||||
INIT_ENV_EXPORT(proc_loadavg_init);
|
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2023, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
*/
|
||||
|
||||
#include "proc.h"
|
||||
#include "procfs.h"
|
||||
|
||||
#include <rthw.h>
|
||||
#include <rtdbg.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <dfs_dentry.h>
|
||||
#include <mm_page.h>
|
||||
|
||||
|
||||
extern void rt_memory_info(rt_size_t *total,
|
||||
rt_size_t *used,
|
||||
rt_size_t *max_used);
|
||||
|
||||
static int single_show(struct dfs_seq_file *seq, void *data)
|
||||
{
|
||||
rt_size_t total, used, max_used, freed;
|
||||
rt_size_t total_sum = 0;
|
||||
rt_size_t total_freed = 0;
|
||||
|
||||
rt_memory_info(&total, &used, &max_used);
|
||||
total_sum = total_sum + total;
|
||||
total_freed = total_freed + total - used;
|
||||
|
||||
dfs_seq_printf(seq, "%-16s%8d KB\n", "MemMaxUsed:", max_used / 1024);
|
||||
dfs_seq_printf(seq, "%-16s%8d KB\n", "MemAvailable:", (total - used) / 1024);
|
||||
dfs_seq_printf(seq, "%-16s%8d KB\n", "Cached:", 0);
|
||||
dfs_seq_printf(seq, "%-16s%8d KB\n", "SReclaimable:", 0);
|
||||
|
||||
rt_page_get_info(&total, &freed);
|
||||
total_sum = total_sum + total * RT_MM_PAGE_SIZE;
|
||||
total_freed = total_freed + freed * RT_MM_PAGE_SIZE;
|
||||
|
||||
dfs_seq_printf(seq, "%-16s%8d KB\n", "MemTotal:", total_sum / 1024);
|
||||
dfs_seq_printf(seq, "%-16s%8d KB\n", "MemFree:", total_freed / 1024);
|
||||
dfs_seq_printf(seq, "%-16s%8d KB\n", "LowPageTotal:", total * RT_MM_PAGE_SIZE / 1024);
|
||||
dfs_seq_printf(seq, "%-16s%8d KB\n", "lowPageFree:", freed * RT_MM_PAGE_SIZE/ 1024);
|
||||
|
||||
rt_page_high_get_info(&total, &freed);
|
||||
|
||||
dfs_seq_printf(seq, "%-16s%8d KB\n", "HighPageTotal:", total * RT_MM_PAGE_SIZE / 1024);
|
||||
dfs_seq_printf(seq, "%-16s%8d KB\n", "HighPageFree:", freed * RT_MM_PAGE_SIZE / 1024);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int proc_meminfo_init(void)
|
||||
{
|
||||
struct proc_dentry *dentry = proc_create_single_data("meminfo", 0, NULL, single_show, NULL);
|
||||
proc_release(dentry);
|
||||
|
||||
return 0;
|
||||
}
|
||||
INIT_ENV_EXPORT(proc_meminfo_init);
|
@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2023, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
*/
|
||||
|
||||
#include "proc.h"
|
||||
#include "procfs.h"
|
||||
|
||||
#include <rthw.h>
|
||||
#include <rtdbg.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <dfs_dentry.h>
|
||||
#include <dfs_mnt.h>
|
||||
|
||||
|
||||
const char *mnt_flag(int flag)
|
||||
{
|
||||
/*if (flag & MNT_READONLY)
|
||||
{
|
||||
return "ro";
|
||||
}*/
|
||||
|
||||
return "rw";
|
||||
}
|
||||
|
||||
static struct dfs_mnt* mnt_show(struct dfs_mnt *mnt, void *parameter)
|
||||
{
|
||||
struct dfs_seq_file *seq = (struct dfs_seq_file *)parameter;
|
||||
|
||||
if (mnt)
|
||||
{
|
||||
if (mnt->dev_id)
|
||||
{
|
||||
dfs_seq_printf(seq, "%s %s %s %s 0 0\n", mnt->dev_id->parent.name, mnt->fullpath,
|
||||
mnt->fs_ops->name, mnt_flag(mnt->flags));
|
||||
}
|
||||
else
|
||||
{
|
||||
dfs_seq_printf(seq, "%s %s %s %s 0 0\n", mnt->fs_ops->name, mnt->fullpath,
|
||||
mnt->fs_ops->name, mnt_flag(mnt->flags));
|
||||
}
|
||||
}
|
||||
|
||||
return RT_NULL;
|
||||
}
|
||||
|
||||
static void *seq_start(struct dfs_seq_file *seq, off_t *index)
|
||||
{
|
||||
off_t i = *index; // seq->index
|
||||
|
||||
return NULL + (i == 0);
|
||||
}
|
||||
|
||||
static void seq_stop(struct dfs_seq_file *seq, void *data)
|
||||
{
|
||||
}
|
||||
|
||||
static void *seq_next(struct dfs_seq_file *seq, void *data, off_t *index)
|
||||
{
|
||||
/* data: The return value of the start or next*/
|
||||
off_t i = *index + 1; // seq->index
|
||||
|
||||
*index = i;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int seq_show(struct dfs_seq_file *seq, void *data)
|
||||
{
|
||||
/* data: The return value of the start or next*/
|
||||
dfs_mnt_foreach(mnt_show, seq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dfs_seq_ops seq_ops = {
|
||||
.start = seq_start,
|
||||
.stop = seq_stop,
|
||||
.next = seq_next,
|
||||
.show = seq_show,
|
||||
};
|
||||
|
||||
int proc_mounts_init(void)
|
||||
{
|
||||
struct proc_dentry *dentry = proc_create_data("mounts", 0, NULL, NULL, NULL);
|
||||
if (dentry)
|
||||
{
|
||||
dentry->seq_ops = &seq_ops;
|
||||
}
|
||||
proc_release(dentry);
|
||||
|
||||
return 0;
|
||||
}
|
||||
INIT_ENV_EXPORT(proc_mounts_init);
|
@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2023, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
*/
|
||||
|
||||
#include "proc.h"
|
||||
#include "procfs.h"
|
||||
|
||||
#include <rthw.h>
|
||||
#include <rtdbg.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <dfs_dentry.h>
|
||||
|
||||
#ifdef RT_USING_LWIP
|
||||
#include "lwip/opt.h"
|
||||
#endif
|
||||
|
||||
#if LWIP_ROUTE
|
||||
extern int inet_route_foreach(void (*func)(const char *name, uint32_t ip_addr, uint32_t netmask, void *parameter), void *parameter);
|
||||
#endif
|
||||
|
||||
static void *seq_start(struct dfs_seq_file *seq, off_t *index)
|
||||
{
|
||||
off_t i = *index; // seq->index
|
||||
|
||||
return NULL + (i == 0);
|
||||
}
|
||||
|
||||
static void seq_stop(struct dfs_seq_file *seq, void *data)
|
||||
{
|
||||
}
|
||||
|
||||
static void *seq_next(struct dfs_seq_file *seq, void *data, off_t *index)
|
||||
{
|
||||
/* data: The return value of the start or next*/
|
||||
off_t i = *index + 1; // seq->index
|
||||
|
||||
*index = i;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void route_show(const char *name, uint32_t ip_addr, uint32_t netmask, void *parameter)
|
||||
{
|
||||
struct dfs_seq_file *seq = (struct dfs_seq_file *)parameter;
|
||||
/* "Iface\tDestination\tGateway "
|
||||
"\tFlags\tRefCnt\tUse\tMetric\tMask\t\tMTU"
|
||||
"\tWindow\tIRTT"); */
|
||||
/* "%63s%lx%lx%X%d%d%d%lx%d%d%d\n" */
|
||||
dfs_seq_printf(seq, "%s ", name);
|
||||
dfs_seq_printf(seq, "%lx ", ip_addr);
|
||||
dfs_seq_printf(seq, "%lx ", 0);
|
||||
dfs_seq_printf(seq, "%X ", 1);
|
||||
dfs_seq_printf(seq, "%d ", 0);
|
||||
dfs_seq_printf(seq, "%d ", 0);
|
||||
dfs_seq_printf(seq, "%d ", 0);
|
||||
dfs_seq_printf(seq, "%lx ", netmask);
|
||||
dfs_seq_printf(seq, "%d ", 0);
|
||||
dfs_seq_printf(seq, "%d ", 0);
|
||||
dfs_seq_printf(seq, "%d\n", 0);
|
||||
}
|
||||
|
||||
static int seq_show(struct dfs_seq_file *seq, void *data)
|
||||
{
|
||||
/* data: The return value of the start or next*/
|
||||
dfs_seq_printf(seq, "\n");
|
||||
#if LWIP_ROUTE
|
||||
inet_route_foreach(route_show, seq);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dfs_seq_ops seq_ops = {
|
||||
.start = seq_start,
|
||||
.stop = seq_stop,
|
||||
.next = seq_next,
|
||||
.show = seq_show,
|
||||
};
|
||||
|
||||
int proc_net_init(void)
|
||||
{
|
||||
struct proc_dentry *dentry;
|
||||
|
||||
dentry = proc_mkdir("net", NULL);
|
||||
if (!dentry)
|
||||
return -1;
|
||||
|
||||
proc_release(dentry);
|
||||
|
||||
dentry = proc_create_data("net/route", 0, NULL, NULL, NULL);
|
||||
if (dentry)
|
||||
{
|
||||
dentry->seq_ops = &seq_ops;
|
||||
}
|
||||
proc_release(dentry);
|
||||
|
||||
return 0;
|
||||
}
|
||||
INIT_ENV_EXPORT(proc_net_init);
|
@ -0,0 +1,215 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2023, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
*/
|
||||
|
||||
#include "proc.h"
|
||||
#include "procfs.h"
|
||||
|
||||
#include <rthw.h>
|
||||
#include <rtdbg.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <dfs_dentry.h>
|
||||
#include <rthw.h>
|
||||
#include <rtthread.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
#define LIST_FIND_OBJ_NR 8
|
||||
|
||||
typedef struct
|
||||
{
|
||||
rt_list_t *list;
|
||||
rt_list_t **array;
|
||||
rt_uint8_t type;
|
||||
int nr; /* input: max nr, can't be 0 */
|
||||
int nr_out; /* out: got nr */
|
||||
} list_get_next_t;
|
||||
|
||||
static void list_find_init(list_get_next_t *p, rt_uint8_t type, rt_list_t **array, int nr)
|
||||
{
|
||||
struct rt_object_information *info;
|
||||
rt_list_t *list;
|
||||
|
||||
info = rt_object_get_information((enum rt_object_class_type)type);
|
||||
list = &info->object_list;
|
||||
|
||||
p->list = list;
|
||||
p->type = type;
|
||||
p->array = array;
|
||||
p->nr = nr;
|
||||
p->nr_out = 0;
|
||||
}
|
||||
|
||||
static rt_list_t *list_get_next(rt_list_t *current, list_get_next_t *arg)
|
||||
{
|
||||
int first_flag = 0;
|
||||
rt_base_t level;
|
||||
rt_list_t *node, *list;
|
||||
rt_list_t **array;
|
||||
struct rt_object_information *info;
|
||||
int nr;
|
||||
|
||||
arg->nr_out = 0;
|
||||
|
||||
if (!arg->nr || !arg->type)
|
||||
{
|
||||
return (rt_list_t *)RT_NULL;
|
||||
}
|
||||
|
||||
list = arg->list;
|
||||
info = rt_list_entry(list, struct rt_object_information, object_list);
|
||||
|
||||
if (!current) /* find first */
|
||||
{
|
||||
node = list;
|
||||
first_flag = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
node = current;
|
||||
}
|
||||
|
||||
level = rt_spin_lock_irqsave(&info->spinlock);
|
||||
|
||||
if (!first_flag)
|
||||
{
|
||||
struct rt_object *obj;
|
||||
/* The node in the list? */
|
||||
obj = rt_list_entry(node, struct rt_object, list);
|
||||
if ((obj->type & ~RT_Object_Class_Static) != arg->type)
|
||||
{
|
||||
rt_spin_unlock_irqrestore(&info->spinlock, level);
|
||||
return (rt_list_t *)RT_NULL;
|
||||
}
|
||||
}
|
||||
|
||||
nr = 0;
|
||||
array = arg->array;
|
||||
while (1)
|
||||
{
|
||||
node = node->next;
|
||||
|
||||
if (node == list)
|
||||
{
|
||||
node = (rt_list_t *)RT_NULL;
|
||||
break;
|
||||
}
|
||||
nr++;
|
||||
*array++ = node;
|
||||
if (nr == arg->nr)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
rt_spin_unlock_irqrestore(&info->spinlock, level);
|
||||
arg->nr_out = nr;
|
||||
return node;
|
||||
}
|
||||
|
||||
static int show_info(struct dfs_seq_file *seq)
|
||||
{
|
||||
rt_base_t level;
|
||||
list_get_next_t find_arg;
|
||||
struct rt_object_information *info;
|
||||
rt_list_t *obj_list[LIST_FIND_OBJ_NR];
|
||||
rt_list_t *next = (rt_list_t *)RT_NULL;
|
||||
|
||||
list_find_init(&find_arg, RT_Object_Class_Device, obj_list, sizeof(obj_list) / sizeof(obj_list[0]));
|
||||
info = rt_list_entry(find_arg.list, struct rt_object_information, object_list);
|
||||
|
||||
do
|
||||
{
|
||||
next = list_get_next(next, &find_arg);
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < find_arg.nr_out; i++)
|
||||
{
|
||||
struct rt_object *obj;
|
||||
struct rt_device *device;
|
||||
|
||||
obj = rt_list_entry(obj_list[i], struct rt_object, list);
|
||||
level = rt_spin_lock_irqsave(&info->spinlock);
|
||||
if ((obj->type & ~RT_Object_Class_Static) != find_arg.type)
|
||||
{
|
||||
rt_spin_unlock_irqrestore(&info->spinlock, level);
|
||||
continue;
|
||||
}
|
||||
|
||||
rt_spin_unlock_irqrestore(&info->spinlock, level);
|
||||
|
||||
device = (struct rt_device *)obj;
|
||||
|
||||
if (device->type == RT_Device_Class_Block)
|
||||
{
|
||||
struct rt_device_blk_geometry geometry = { 0 };
|
||||
|
||||
rt_device_control(device, RT_DEVICE_CTRL_BLK_GETGEOME, &geometry);
|
||||
|
||||
dfs_seq_printf(seq, "%4d %7d %14llu %s\n", 0, 0,
|
||||
geometry.sector_count, device->parent.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (next != (rt_list_t *)RT_NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *seq_start(struct dfs_seq_file *seq, off_t *index)
|
||||
{
|
||||
off_t i = *index; // seq->index
|
||||
|
||||
return NULL + (i == 0);
|
||||
}
|
||||
|
||||
static void seq_stop(struct dfs_seq_file *seq, void *data)
|
||||
{
|
||||
}
|
||||
|
||||
static void *seq_next(struct dfs_seq_file *seq, void *data, off_t *index)
|
||||
{
|
||||
/* data: The return value of the start or next*/
|
||||
off_t i = *index + 1; // seq->index
|
||||
|
||||
*index = i;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int seq_show(struct dfs_seq_file *seq, void *data)
|
||||
{
|
||||
dfs_seq_puts(seq, "major minor #blocks name\n\n");
|
||||
/* data: The return value of the start or next*/
|
||||
show_info(seq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dfs_seq_ops seq_ops = {
|
||||
.start = seq_start,
|
||||
.stop = seq_stop,
|
||||
.next = seq_next,
|
||||
.show = seq_show,
|
||||
};
|
||||
|
||||
int proc_partitions_init(void)
|
||||
{
|
||||
struct proc_dentry *dentry = proc_create_data("partitions", 0, NULL, NULL, NULL);
|
||||
if (dentry)
|
||||
{
|
||||
dentry->seq_ops = &seq_ops;
|
||||
}
|
||||
proc_release(dentry);
|
||||
|
||||
return 0;
|
||||
}
|
||||
INIT_ENV_EXPORT(proc_partitions_init);
|
@ -0,0 +1,449 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2023, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
*/
|
||||
#define __RT_IPC_SOURCE__
|
||||
|
||||
#include "proc.h"
|
||||
#include "procfs.h"
|
||||
|
||||
#include <rthw.h>
|
||||
#include <rtdbg.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "lwp_internal.h"
|
||||
#include <dfs_dentry.h>
|
||||
#include "lwp_internal.h"
|
||||
|
||||
#if defined(RT_USING_SMART)
|
||||
|
||||
#include "lwp.h"
|
||||
#include "lwp_pid.h"
|
||||
#include <lwp_user_mm.h>
|
||||
|
||||
struct pid_dentry
|
||||
{
|
||||
const char *name;
|
||||
mode_t mode;
|
||||
const struct dfs_file_ops *fops;
|
||||
const struct proc_ops *ops;
|
||||
const struct dfs_seq_ops *seq_ops;
|
||||
int (*single_show)(struct dfs_seq_file *seq, void *data);
|
||||
void *data;
|
||||
};
|
||||
|
||||
static char stat_transform(int __stat)
|
||||
{
|
||||
switch (__stat)
|
||||
{
|
||||
case RT_THREAD_RUNNING:
|
||||
return 'R';
|
||||
default:
|
||||
return 'T';
|
||||
}
|
||||
}
|
||||
|
||||
static int stat_single_show(struct dfs_seq_file *seq, void *data)
|
||||
{
|
||||
struct proc_dentry *dentry = (struct proc_dentry *)seq->file->vnode->data;
|
||||
rt_list_t *list;
|
||||
int mask = 0;
|
||||
rt_thread_t thread;
|
||||
rt_uint64_t user_time_lwp = 0;
|
||||
rt_uint64_t system_time_lwp = 0;
|
||||
int lwp_oncpu = RT_CPUS_NR;
|
||||
int lwp_oncpu_ok = 0;
|
||||
struct rt_lwp *lwp = RT_NULL;
|
||||
char** argv = RT_NULL;
|
||||
char *filename = RT_NULL;
|
||||
char *dot = RT_NULL;
|
||||
|
||||
lwp_pid_lock_take();
|
||||
|
||||
lwp = lwp_from_pid_locked(dentry->pid);
|
||||
argv = lwp_get_command_line_args(lwp);
|
||||
|
||||
if (lwp)
|
||||
{
|
||||
dfs_seq_printf(seq,"%d ",dentry->pid);
|
||||
if (argv)
|
||||
{
|
||||
filename = strrchr(argv[0], '/');
|
||||
dot = strchr(argv[0], '.');
|
||||
|
||||
if (filename != NULL)
|
||||
{
|
||||
filename++;
|
||||
}
|
||||
else
|
||||
{
|
||||
filename = argv[0];
|
||||
}
|
||||
|
||||
if (dot != NULL)
|
||||
{
|
||||
*dot = '\0';
|
||||
}
|
||||
|
||||
if (filename != NULL)
|
||||
{
|
||||
dfs_seq_printf(seq,"(%s) ", filename);
|
||||
}
|
||||
else
|
||||
{
|
||||
dfs_seq_printf(seq,"(%s) ", argv[0]);
|
||||
}
|
||||
|
||||
lwp_free_command_line_args(argv);
|
||||
}
|
||||
else
|
||||
{
|
||||
dfs_seq_printf(seq,"(%s) ", "");
|
||||
}
|
||||
|
||||
if (lwp->terminated)
|
||||
{
|
||||
dfs_seq_printf(seq,"%c ",'Z');
|
||||
}
|
||||
else
|
||||
{
|
||||
list = lwp->t_grp.next;
|
||||
while (list != &lwp->t_grp)
|
||||
{
|
||||
thread = rt_list_entry(list, struct rt_thread, sibling);
|
||||
user_time_lwp = user_time_lwp + thread->user_time;
|
||||
system_time_lwp = system_time_lwp + thread->system_time;
|
||||
|
||||
#if RT_CPUS_NR > 1
|
||||
#define ONCPU(thread) RT_SCHED_CTX(thread).oncpu
|
||||
#else
|
||||
#define ONCPU(thread) 0
|
||||
#endif
|
||||
if (lwp_oncpu_ok == 0)
|
||||
{
|
||||
lwp_oncpu = ONCPU(thread);
|
||||
lwp_oncpu_ok = 1;
|
||||
}
|
||||
if (stat_transform(RT_SCHED_CTX(thread).stat) == 'R')
|
||||
{
|
||||
lwp_oncpu = ONCPU(thread);
|
||||
mask = 1;
|
||||
}
|
||||
list = list->next;
|
||||
}
|
||||
|
||||
if (mask == 1)
|
||||
{
|
||||
dfs_seq_printf(seq,"%c ",'R');
|
||||
}
|
||||
else
|
||||
{
|
||||
dfs_seq_printf(seq,"%c ",'S');
|
||||
}
|
||||
}
|
||||
lwp_pid_lock_release();
|
||||
|
||||
if (lwp->parent != NULL)
|
||||
dfs_seq_printf(seq,"%d ",lwp->parent->pid);
|
||||
else
|
||||
dfs_seq_printf(seq,"0 ");
|
||||
|
||||
dfs_seq_printf(seq, "1 1 0 -1 4194560 48245 133976064 732 425574 ");
|
||||
dfs_seq_printf(seq,"%llu ",user_time_lwp);//utime
|
||||
dfs_seq_printf(seq,"%llu ",system_time_lwp);//stime
|
||||
dfs_seq_printf(seq, "1204291 518742 20 0 1 0 50 ");
|
||||
dfs_seq_printf(seq, "%d ",rt_aspace_count_vsz(lwp->aspace));//VSZ
|
||||
dfs_seq_printf(seq, "1422 18446744073709551615 ");
|
||||
dfs_seq_printf(seq, "1 1 0 0 0 0 671173123 4096 1260 0 0 0 17 ");
|
||||
dfs_seq_printf(seq, "%d ", lwp_oncpu);//CPU
|
||||
dfs_seq_printf(seq, "0 0 0 0 0 0 0 0 0 0 0 0 0");
|
||||
dfs_seq_printf(seq,"\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
lwp_pid_lock_release();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmdline_single_show(struct dfs_seq_file *seq, void *data)
|
||||
{
|
||||
struct proc_dentry *dentry = (struct proc_dentry *)seq->file->vnode->data;
|
||||
struct rt_lwp *lwp;
|
||||
char** argv;
|
||||
|
||||
lwp_pid_lock_take();
|
||||
lwp = lwp_from_pid_locked(dentry->pid);
|
||||
argv = lwp_get_command_line_args(lwp);
|
||||
lwp_pid_lock_release();
|
||||
|
||||
if (argv)
|
||||
{
|
||||
for (int i = 0; argv[i] != NULL; i++)
|
||||
{
|
||||
dfs_seq_printf(seq, "%s ", argv[i]);
|
||||
}
|
||||
dfs_seq_puts(seq, "\n");
|
||||
|
||||
lwp_free_command_line_args(argv);
|
||||
}
|
||||
else
|
||||
{
|
||||
dfs_seq_puts(seq, "error\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct proc_dentry *proc_pid_fd_lookup(struct proc_dentry *parent, const char *name)
|
||||
{
|
||||
struct proc_dentry *dentry = RT_NULL;
|
||||
char num[DIRENT_NAME_MAX];
|
||||
struct rt_lwp *lwp;
|
||||
struct dfs_fdtable *table;
|
||||
|
||||
lwp_pid_lock_take();
|
||||
lwp = lwp_from_pid_locked(parent->pid);
|
||||
table = lwp ? &lwp->fdt : RT_NULL;
|
||||
lwp_pid_lock_release();
|
||||
|
||||
if (!table)
|
||||
{
|
||||
return RT_NULL;
|
||||
}
|
||||
|
||||
dfs_file_lock();
|
||||
for (int i = 0; i < table->maxfd; i++)
|
||||
{
|
||||
struct dfs_file *file = table->fds[i];
|
||||
if (file)
|
||||
{
|
||||
rt_snprintf(num, DIRENT_NAME_MAX, "%d", i);
|
||||
if (rt_strcmp(num, name) == 0)
|
||||
{
|
||||
dentry = rt_calloc(1, sizeof(struct proc_dentry));
|
||||
if (dentry)
|
||||
{
|
||||
dentry->mode = (S_IFLNK | (S_IRUSR | S_IRGRP | S_IROTH) | (S_IWUSR | S_IWGRP | S_IWOTH) | (S_IXUSR | S_IXGRP | S_IXOTH));
|
||||
dentry->ref_count = 1;
|
||||
dentry->name = rt_strdup(name);
|
||||
dentry->data = (void *)dfs_dentry_full_path(file->dentry);
|
||||
|
||||
if (dentry->data == RT_NULL)
|
||||
{
|
||||
//todo add vnode->data
|
||||
if (file->vnode->type == FT_SOCKET)
|
||||
dentry->data = (void *)rt_strdup("socket");
|
||||
else if (file->vnode->type == FT_USER)
|
||||
dentry->data = (void *)rt_strdup("user");
|
||||
else if (file->vnode->type == FT_DEVICE)
|
||||
dentry->data = (void *)rt_strdup("device");
|
||||
else
|
||||
dentry->data = (void *)rt_strdup("unknown");
|
||||
}
|
||||
|
||||
dentry->pid = parent->pid;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
dfs_file_unlock();
|
||||
|
||||
return dentry;
|
||||
}
|
||||
|
||||
int proc_pid_fd_getdents(struct dfs_file *file, struct dirent *dirp, uint32_t count)
|
||||
{
|
||||
int ret = 0, index = 0;
|
||||
struct proc_dentry *entry = (struct proc_dentry *)file->vnode->data;
|
||||
struct rt_lwp *lwp;
|
||||
struct dfs_fdtable *table;
|
||||
|
||||
lwp_pid_lock_take();
|
||||
lwp = lwp_from_pid_locked(entry->pid);
|
||||
LWP_LOCK(lwp);
|
||||
table = lwp ? &lwp->fdt : RT_NULL;
|
||||
|
||||
if (!table->fds)
|
||||
{
|
||||
LWP_UNLOCK(lwp);
|
||||
lwp_pid_lock_release();
|
||||
return 0;
|
||||
}
|
||||
|
||||
count = (count / sizeof(struct dirent));
|
||||
if (count == 0)
|
||||
{
|
||||
LWP_UNLOCK(lwp);
|
||||
lwp_pid_lock_release();
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dfs_file_lock();
|
||||
for (int i = 0; i < table->maxfd; i++)
|
||||
{
|
||||
struct dfs_file *df = table->fds[i];
|
||||
if (df)
|
||||
{
|
||||
if (index >= file->fpos)
|
||||
{
|
||||
struct dirent *d = dirp + index - file->fpos;
|
||||
|
||||
d->d_type = DT_SYMLINK;
|
||||
d->d_reclen = (rt_uint16_t)sizeof(struct dirent);
|
||||
rt_snprintf(d->d_name, DIRENT_NAME_MAX, "%d", i);
|
||||
d->d_namlen = rt_strlen(d->d_name);
|
||||
|
||||
ret++;
|
||||
}
|
||||
|
||||
index++;
|
||||
if (index - file->fpos >= count)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
dfs_file_unlock();
|
||||
LWP_UNLOCK(lwp);
|
||||
lwp_pid_lock_release();
|
||||
|
||||
if (ret > 0)
|
||||
{
|
||||
file->fpos = index;
|
||||
ret = ret * sizeof(struct dirent);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct proc_ops proc_pid_fd_ops = {
|
||||
.lookup = proc_pid_fd_lookup,
|
||||
};
|
||||
|
||||
static const struct dfs_file_ops proc_pid_fd_fops = {
|
||||
.getdents = proc_pid_fd_getdents,
|
||||
};
|
||||
|
||||
int proc_pid_exe_readlink(struct proc_dentry *dentry, char *buf, int len)
|
||||
{
|
||||
struct rt_lwp *lwp;
|
||||
|
||||
lwp = lwp_self();
|
||||
len = rt_snprintf(buf, len, "%s", lwp ? lwp->exe_file : "null");
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static const struct proc_ops proc_pid_exe_ops = {
|
||||
.readlink = proc_pid_exe_readlink,
|
||||
};
|
||||
|
||||
int proc_pid_cwd_readlink(struct proc_dentry *dentry, char *buf, int len)
|
||||
{
|
||||
struct rt_lwp *lwp;
|
||||
|
||||
lwp = lwp_self();
|
||||
len = rt_snprintf(buf, len, "%s", lwp ? lwp->working_directory : "null");
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static const struct proc_ops proc_pid_cwd_ops = {
|
||||
.readlink = proc_pid_cwd_readlink,
|
||||
};
|
||||
|
||||
static struct pid_dentry pid_dentry_base[] = {
|
||||
{"cmdline", S_IFREG | S_IRUSR | S_IRGRP | S_IROTH, 0, 0, 0, cmdline_single_show, 0},
|
||||
{"cwd", S_IFLNK | S_IRUSR | S_IXUSR, 0, &proc_pid_cwd_ops, 0, 0},
|
||||
{"exe", S_IFLNK | S_IRUSR | S_IXUSR, 0, &proc_pid_exe_ops, 0, 0},
|
||||
{"fd", S_IFDIR | S_IRUSR | S_IXUSR, &proc_pid_fd_fops, &proc_pid_fd_ops, 0, 0, 0},
|
||||
{"mounts", S_IFLNK | S_IRUSR | S_IXUSR, 0, 0, 0, 0, "/proc/mounts"},
|
||||
{"stat", S_IFREG | S_IRUSR | S_IRGRP | S_IROTH, 0, 0, 0, stat_single_show, 0},
|
||||
};
|
||||
|
||||
int proc_pid(int pid)
|
||||
{
|
||||
char pid_str[64] = {0};
|
||||
struct proc_dentry *dentry;
|
||||
|
||||
rt_snprintf(pid_str, 64, "%d", pid);
|
||||
pid_str[63] = 0;
|
||||
|
||||
dentry = proc_mkdir(pid_str, 0);
|
||||
if (dentry)
|
||||
{
|
||||
struct proc_dentry *ent;
|
||||
|
||||
dentry->pid = pid;
|
||||
for (int j = 0; j < sizeof(pid_dentry_base) / sizeof(struct pid_dentry); j++)
|
||||
{
|
||||
if (S_ISDIR(pid_dentry_base[j].mode))
|
||||
{
|
||||
ent = proc_mkdir_data(pid_dentry_base[j].name, pid_dentry_base[j].mode, dentry,
|
||||
pid_dentry_base[j].fops, pid_dentry_base[j].data);
|
||||
}
|
||||
else if (S_ISLNK(pid_dentry_base[j].mode))
|
||||
{
|
||||
if (pid_dentry_base[j].data == RT_NULL)
|
||||
{
|
||||
pid_dentry_base[j].data = "NULL";
|
||||
}
|
||||
|
||||
ent = proc_symlink(pid_dentry_base[j].name, dentry, pid_dentry_base[j].data);
|
||||
}
|
||||
else
|
||||
{
|
||||
ent = proc_create_data(pid_dentry_base[j].name, pid_dentry_base[j].mode, dentry,
|
||||
pid_dentry_base[j].fops, pid_dentry_base[j].data);
|
||||
}
|
||||
|
||||
if (ent)
|
||||
{
|
||||
if (pid_dentry_base[j].ops)
|
||||
{
|
||||
ent->ops = pid_dentry_base[j].ops;
|
||||
}
|
||||
|
||||
if (pid_dentry_base[j].seq_ops)
|
||||
{
|
||||
ent->seq_ops = pid_dentry_base[j].seq_ops;
|
||||
}
|
||||
|
||||
if (pid_dentry_base[j].single_show)
|
||||
{
|
||||
ent->single_show = pid_dentry_base[j].single_show;
|
||||
}
|
||||
|
||||
proc_release(ent);
|
||||
}
|
||||
}
|
||||
proc_release(dentry);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int msh_proc_pid(int argc, char **argv)
|
||||
{
|
||||
if (argc > 1)
|
||||
{
|
||||
for (int i = 1; i <= argc - 1; i++)
|
||||
{
|
||||
proc_pid(atoi(argv[i]));
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
MSH_CMD_EXPORT_ALIAS(msh_proc_pid, proc_pid, proc pid);
|
||||
|
||||
#endif
|
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2023, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
*/
|
||||
|
||||
#include "proc.h"
|
||||
#include "procfs.h"
|
||||
|
||||
#include <rthw.h>
|
||||
#include <rtdbg.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <dfs_dentry.h>
|
||||
|
||||
#if defined(RT_USING_SMART)
|
||||
|
||||
#include <lwp.h>
|
||||
|
||||
|
||||
int proc_self_readlink(struct proc_dentry *dentry, char *buf, int len)
|
||||
{
|
||||
struct rt_lwp *lwp = RT_NULL;
|
||||
|
||||
lwp = lwp_self();
|
||||
if (lwp)
|
||||
{
|
||||
rt_snprintf(buf, len, "%d", lwp_to_pid(lwp));
|
||||
buf[len - 1] = 0;
|
||||
return rt_strlen(buf);
|
||||
}
|
||||
else
|
||||
{
|
||||
rt_snprintf(buf, len, "null");
|
||||
buf[len - 1] = 0;
|
||||
return rt_strlen(buf);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static const struct proc_ops proc_pid_fd_ops = {
|
||||
.readlink = proc_self_readlink,
|
||||
};
|
||||
|
||||
int proc_self_init(void)
|
||||
{
|
||||
struct proc_dentry *ent;
|
||||
|
||||
ent = proc_symlink("self", NULL, "NULL");
|
||||
if (ent)
|
||||
{
|
||||
ent->ops = &proc_pid_fd_ops;
|
||||
}
|
||||
proc_release(ent);
|
||||
|
||||
return 0;
|
||||
}
|
||||
INIT_ENV_EXPORT(proc_self_init);
|
||||
|
||||
#endif
|
@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2023, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
*/
|
||||
|
||||
#include "proc.h"
|
||||
#include "procfs.h"
|
||||
|
||||
#include <rthw.h>
|
||||
#include <rtdbg.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <dfs_dentry.h>
|
||||
|
||||
|
||||
static void *seq_start(struct dfs_seq_file *seq, off_t *index)
|
||||
{
|
||||
off_t i = *index; // seq->index
|
||||
|
||||
return NULL + (i == 0);
|
||||
}
|
||||
|
||||
static void seq_stop(struct dfs_seq_file *seq, void *data)
|
||||
{
|
||||
}
|
||||
|
||||
static void *seq_next(struct dfs_seq_file *seq, void *data, off_t *index)
|
||||
{
|
||||
/* data: The return value of the start or next*/
|
||||
off_t i = *index + 1; // seq->index
|
||||
|
||||
*index = i;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int seq_show(struct dfs_seq_file *seq, void *data)
|
||||
{
|
||||
int i;
|
||||
rt_cpu_t pcpu;
|
||||
rt_uint64_t user_total = 0;
|
||||
rt_uint64_t system_total = 0;
|
||||
rt_uint64_t idle_total = 0;
|
||||
|
||||
for (i = 0; i < RT_CPUS_NR; i++)
|
||||
{
|
||||
pcpu = rt_cpu_index(i);
|
||||
user_total = user_total + pcpu->cpu_stat.user;
|
||||
system_total = system_total + pcpu->cpu_stat.system;
|
||||
idle_total = idle_total + pcpu->cpu_stat.idle;
|
||||
}
|
||||
dfs_seq_printf(seq, "cpu %llu 0 %llu %llu 0 0 0 0 0 0\n", user_total, system_total, idle_total);
|
||||
|
||||
for (i = 0; i < RT_CPUS_NR; i++)
|
||||
{
|
||||
pcpu = rt_cpu_index(i);
|
||||
dfs_seq_printf(seq, "cpu%d ",i);
|
||||
dfs_seq_printf(seq, "%llu ",pcpu->cpu_stat.user);//user
|
||||
dfs_seq_printf(seq, "0 ");//nice
|
||||
dfs_seq_printf(seq, "%llu ",pcpu->cpu_stat.system);//system
|
||||
dfs_seq_printf(seq, "%llu ",pcpu->cpu_stat.idle);//idle
|
||||
dfs_seq_printf(seq, "0 ");//iowait
|
||||
dfs_seq_printf(seq, "0 ");//irq
|
||||
dfs_seq_printf(seq, "0 ");//softirq
|
||||
dfs_seq_printf(seq, "0 0 0\n");//steal,guest,guest_nice
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dfs_seq_ops seq_ops = {
|
||||
.start = seq_start,
|
||||
.stop = seq_stop,
|
||||
.next = seq_next,
|
||||
.show = seq_show,
|
||||
};
|
||||
|
||||
rt_weak const struct dfs_seq_ops *stat_get_seq_ops(void)
|
||||
{
|
||||
return &seq_ops;
|
||||
}
|
||||
|
||||
static int proc_open(struct dfs_file *file)
|
||||
{
|
||||
return dfs_seq_open(file, stat_get_seq_ops());
|
||||
}
|
||||
|
||||
static int proc_close(struct dfs_file *file)
|
||||
{
|
||||
return dfs_seq_release(file);
|
||||
}
|
||||
|
||||
static const struct dfs_file_ops file_ops = {
|
||||
.open = proc_open,
|
||||
.read = dfs_seq_read,
|
||||
.lseek = dfs_seq_lseek,
|
||||
.close = proc_close,
|
||||
};
|
||||
|
||||
int proc_stat_init(void)
|
||||
{
|
||||
struct proc_dentry *dentry = proc_create_data("stat", 0, NULL, &file_ops, NULL);
|
||||
proc_release(dentry);
|
||||
|
||||
return 0;
|
||||
}
|
||||
INIT_ENV_EXPORT(proc_stat_init);
|
@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2023, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
*/
|
||||
|
||||
#include "proc.h"
|
||||
#include "procfs.h"
|
||||
|
||||
#include <rthw.h>
|
||||
#include <rtdbg.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <dfs_dentry.h>
|
||||
|
||||
|
||||
static void *seq_start(struct dfs_seq_file *seq, off_t *index)
|
||||
{
|
||||
off_t i = *index; // seq->index
|
||||
|
||||
return NULL + (i == 0);
|
||||
}
|
||||
|
||||
static void seq_stop(struct dfs_seq_file *seq, void *data)
|
||||
{
|
||||
}
|
||||
|
||||
static void *seq_next(struct dfs_seq_file *seq, void *data, off_t *index)
|
||||
{
|
||||
/* data: The return value of the start or next*/
|
||||
off_t i = *index + 1; // seq->index
|
||||
|
||||
*index = i;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int seq_show(struct dfs_seq_file *seq, void *data)
|
||||
{
|
||||
/* data: The return value of the start or next*/
|
||||
dfs_seq_puts(seq, "todo\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dfs_seq_ops seq_ops = {
|
||||
.start = seq_start,
|
||||
.stop = seq_stop,
|
||||
.next = seq_next,
|
||||
.show = seq_show,
|
||||
};
|
||||
|
||||
void proc_tty_register_driver(void *driver)
|
||||
{
|
||||
//todo
|
||||
}
|
||||
|
||||
void proc_tty_unregister_driver(void *driver)
|
||||
{
|
||||
//todo
|
||||
}
|
||||
|
||||
int proc_tty_init(void)
|
||||
{
|
||||
struct proc_dentry *dentry;
|
||||
|
||||
dentry = proc_mkdir("tty", NULL);
|
||||
if (!dentry)
|
||||
return -1;
|
||||
|
||||
proc_release(dentry);
|
||||
|
||||
dentry = proc_mkdir("tty/ldisc", NULL);
|
||||
proc_release(dentry);
|
||||
|
||||
dentry = proc_mkdir_mode("tty/driver", S_IRUSR|S_IXUSR, NULL);
|
||||
proc_release(dentry);
|
||||
|
||||
dentry = proc_create_data("tty/ldiscs", 0, NULL, NULL, NULL);
|
||||
if (dentry)
|
||||
{
|
||||
dentry->seq_ops = &seq_ops;
|
||||
}
|
||||
proc_release(dentry);
|
||||
|
||||
dentry = proc_create_data("tty/drivers", 0, NULL, NULL, NULL);
|
||||
if (dentry)
|
||||
{
|
||||
dentry->seq_ops = &seq_ops;
|
||||
}
|
||||
proc_release(dentry);
|
||||
|
||||
return 0;
|
||||
}
|
||||
INIT_ENV_EXPORT(proc_tty_init);
|
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2023, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
*/
|
||||
|
||||
#include "proc.h"
|
||||
#include "procfs.h"
|
||||
|
||||
#include <rthw.h>
|
||||
#include <rtdbg.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <dfs_dentry.h>
|
||||
|
||||
|
||||
static int single_show(struct dfs_seq_file *seq, void *data)
|
||||
{
|
||||
dfs_seq_printf(seq, "%lu.%02lu %lu.%02lu\n",
|
||||
(unsigned long)rt_tick_get_millisecond() / 1000, (unsigned long)(rt_tick_get_millisecond() % 1000) / 100,
|
||||
(unsigned long)rt_tick_get_millisecond() / 1000, (unsigned long)(rt_tick_get_millisecond() % 1000) / 100);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int proc_uptime_init(void)
|
||||
{
|
||||
struct proc_dentry *dentry = proc_create_single_data("uptime", 0, NULL, single_show, NULL);
|
||||
proc_release(dentry);
|
||||
|
||||
return 0;
|
||||
}
|
||||
INIT_ENV_EXPORT(proc_uptime_init);
|
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2023, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
*/
|
||||
|
||||
#include "proc.h"
|
||||
#include "procfs.h"
|
||||
|
||||
#include <rthw.h>
|
||||
#include <rtdbg.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <dfs_dentry.h>
|
||||
|
||||
|
||||
static int single_show(struct dfs_seq_file *seq, void *data)
|
||||
{
|
||||
dfs_seq_puts(seq, "\n \\ | /\n");
|
||||
#ifdef RT_USING_SMART
|
||||
dfs_seq_puts(seq, "- RT - Thread Smart Operating System\n");
|
||||
#else
|
||||
dfs_seq_puts(seq, "- RT - Thread Operating System\n");
|
||||
#endif
|
||||
dfs_seq_printf(seq, " / | \\ %d.%d.%d build %s %s\n",
|
||||
(rt_int32_t)RT_VERSION_MAJOR, (rt_int32_t)RT_VERSION_MINOR, (rt_int32_t)RT_VERSION_PATCH,
|
||||
__DATE__, __TIME__);
|
||||
dfs_seq_puts(seq, " 2006 - 2022 Copyright by RT-Thread team\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int proc_version_init(void)
|
||||
{
|
||||
struct proc_dentry *dentry = proc_create_single_data("version", 0, NULL, single_show, NULL);
|
||||
proc_release(dentry);
|
||||
|
||||
return 0;
|
||||
}
|
||||
INIT_ENV_EXPORT(proc_version_init);
|
@ -0,0 +1,447 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2023, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
*/
|
||||
|
||||
#include <rthw.h>
|
||||
#include <rtdbg.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <dfs.h>
|
||||
#include <dfs_fs.h>
|
||||
#include <dfs_file.h>
|
||||
#include <dfs_posix.h>
|
||||
#include <dfs_mnt.h>
|
||||
#include <dfs_dentry.h>
|
||||
|
||||
#include "proc.h"
|
||||
#include "procfs.h"
|
||||
|
||||
#define PROC_DEBUG(...) //rt_kprintf
|
||||
|
||||
static int dfs_procfs_open(struct dfs_file *file)
|
||||
{
|
||||
rt_err_t ret = RT_EOK;
|
||||
struct proc_dentry *entry = (struct proc_dentry *)file->vnode->data;
|
||||
|
||||
|
||||
RT_ASSERT(file->ref_count > 0);
|
||||
|
||||
// this file is opened and in an fdtable
|
||||
if (file->ref_count > 1)
|
||||
{
|
||||
file->fpos = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (entry->fops && entry->fops->open)
|
||||
{
|
||||
ret = entry->fops->open(file);
|
||||
}
|
||||
|
||||
PROC_DEBUG(" %s %d >> %s ret: %d\n", __func__, __LINE__, file->dentry->pathname, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dfs_procfs_close(struct dfs_file *file)
|
||||
{
|
||||
rt_err_t ret = RT_EOK;
|
||||
struct proc_dentry *entry = (struct proc_dentry *)file->vnode->data;
|
||||
|
||||
RT_ASSERT(file->vnode->ref_count > 0);
|
||||
if (file->vnode->ref_count > 1)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (entry && entry->fops && entry->fops->close)
|
||||
{
|
||||
ret = entry->fops->close(file);
|
||||
}
|
||||
|
||||
PROC_DEBUG(" %s %d >> %s ret: %d\n", __func__, __LINE__, file->dentry->pathname, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t dfs_procfs_read(struct dfs_file *file, void *buf, size_t count, off_t *pos)
|
||||
{
|
||||
ssize_t ret = -RT_ERROR;
|
||||
struct proc_dentry *entry = (struct proc_dentry *)file->vnode->data;
|
||||
|
||||
if (entry && entry->fops && entry->fops->read)
|
||||
{
|
||||
ret = entry->fops->read(file, buf, count, pos);
|
||||
}
|
||||
|
||||
PROC_DEBUG(" %s %d >> %s ret: %d\n", __func__, __LINE__, file->dentry->pathname, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t dfs_procfs_write(struct dfs_file *file, const void *buf, size_t count, off_t *pos)
|
||||
{
|
||||
ssize_t ret = -RT_ERROR;
|
||||
struct proc_dentry *entry = (struct proc_dentry *)file->vnode->data;
|
||||
|
||||
if (entry && entry->fops && entry->fops->write)
|
||||
{
|
||||
ret = entry->fops->write(file, buf, count, pos);
|
||||
}
|
||||
|
||||
PROC_DEBUG(" %s %d >> %s ret: %d\n", __func__, __LINE__, file->dentry->pathname, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dfs_procfs_ioctl(struct dfs_file *file, int cmd, void *args)
|
||||
{
|
||||
int ret = -RT_ERROR;
|
||||
struct proc_dentry *entry = (struct proc_dentry *)file->vnode->data;
|
||||
|
||||
if (entry && entry->fops && entry->fops->ioctl)
|
||||
{
|
||||
ret = entry->fops->ioctl(file, cmd, args);
|
||||
}
|
||||
|
||||
PROC_DEBUG(" %s %d >> %s ret: %d\n", __func__, __LINE__, file->dentry->pathname, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dfs_procfs_getdents(struct dfs_file *file, struct dirent *dirp, uint32_t count)
|
||||
{
|
||||
int ret = 0;
|
||||
rt_uint32_t index = 0;
|
||||
struct dirent *d;
|
||||
struct proc_dentry *entry = (struct proc_dentry *)file->vnode->data;
|
||||
|
||||
if (entry)
|
||||
{
|
||||
struct proc_dentry *iter = RT_NULL, *tmp;
|
||||
|
||||
/* make integer count */
|
||||
count = (count / sizeof(struct dirent));
|
||||
if (count == 0)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dfs_vfs_for_each_subnode(iter, tmp, entry, node)
|
||||
{
|
||||
if (iter == RT_NULL)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (index >= file->fpos)
|
||||
{
|
||||
d = dirp + index - file->fpos;
|
||||
|
||||
if (S_ISDIR(entry->mode))
|
||||
{
|
||||
d->d_type = DT_DIR;
|
||||
}
|
||||
else if (S_ISLNK(entry->mode))
|
||||
{
|
||||
d->d_type = DT_SYMLINK;
|
||||
}
|
||||
else
|
||||
{
|
||||
d->d_type = DT_REG;
|
||||
}
|
||||
|
||||
d->d_namlen = rt_strlen(iter->name);
|
||||
d->d_reclen = (rt_uint16_t)sizeof(struct dirent);
|
||||
rt_strncpy(d->d_name, iter->name, rt_strlen(iter->name) + 1);
|
||||
|
||||
ret ++;
|
||||
}
|
||||
|
||||
index++;
|
||||
if (index - file->fpos >= count)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret > 0)
|
||||
{
|
||||
file->fpos = index;
|
||||
}
|
||||
|
||||
if (entry->fops && entry->fops->getdents && ret < count)
|
||||
{
|
||||
int r;
|
||||
|
||||
file->fpos -= index;
|
||||
|
||||
r = entry->fops->getdents(file, dirp + ret, (count - ret) * sizeof(struct dirent));
|
||||
|
||||
ret = ret * sizeof(struct dirent);
|
||||
|
||||
if (r > 0)
|
||||
{
|
||||
ret += r;
|
||||
}
|
||||
|
||||
file->fpos += index;
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = ret * sizeof(struct dirent);
|
||||
}
|
||||
}
|
||||
|
||||
PROC_DEBUG(" %s %d >> %s ret: %d\n", __func__, __LINE__, file->dentry->pathname, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dfs_procfs_poll(struct dfs_file *file, struct rt_pollreq *req)
|
||||
{
|
||||
int ret = -RT_ERROR;
|
||||
struct proc_dentry *entry = (struct proc_dentry *)file->vnode->data;
|
||||
|
||||
if (entry && entry->fops && entry->fops->poll)
|
||||
{
|
||||
ret = entry->fops->poll(file, req);
|
||||
}
|
||||
|
||||
PROC_DEBUG(" %s %d >> %s ret: %d\n", __func__, __LINE__, file->dentry->pathname, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dfs_procfs_flush(struct dfs_file *file)
|
||||
{
|
||||
int ret = -RT_ERROR;
|
||||
struct proc_dentry *entry = (struct proc_dentry *)file->vnode->data;
|
||||
|
||||
if (entry && entry->fops && entry->fops->flush)
|
||||
{
|
||||
ret = entry->fops->flush(file);
|
||||
}
|
||||
|
||||
PROC_DEBUG(" %s %d >> %s ret: %d\n", __func__, __LINE__, file->dentry->pathname, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dfs_procfs_mount(struct dfs_mnt *mnt, unsigned long rwflag, const void *data)
|
||||
{
|
||||
RT_ASSERT(mnt != RT_NULL);
|
||||
|
||||
return RT_EOK;
|
||||
}
|
||||
|
||||
static int dfs_procfs_umount(struct dfs_mnt *mnt)
|
||||
{
|
||||
RT_ASSERT(mnt != RT_NULL);
|
||||
|
||||
return RT_EOK;
|
||||
}
|
||||
|
||||
static int dfs_procfs_readlink(struct dfs_dentry *dentry, char *buf, int len)
|
||||
{
|
||||
int ret = 0;
|
||||
struct proc_dentry *entry = dfs_proc_find(dentry->pathname);
|
||||
|
||||
if (entry)
|
||||
{
|
||||
if (S_ISLNK(entry->mode) && entry->data)
|
||||
{
|
||||
if (entry->ops && entry->ops->readlink)
|
||||
{
|
||||
ret = entry->ops->readlink(entry, buf, len);
|
||||
}
|
||||
else
|
||||
{
|
||||
rt_strncpy(buf, (const char *)entry->data, len);
|
||||
buf[len - 1] = '\0';
|
||||
ret = rt_strlen(buf);
|
||||
}
|
||||
}
|
||||
|
||||
proc_release(entry);
|
||||
}
|
||||
|
||||
PROC_DEBUG(" %s %d >> %s ret: %d\n", __func__, __LINE__, dentry->pathname, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dfs_procfs_unlink(struct dfs_dentry *dentry)
|
||||
{
|
||||
PROC_DEBUG(" %s %d >> %s ret: %d\n", __func__, __LINE__, dentry->pathname, -1);
|
||||
return -RT_ERROR;
|
||||
}
|
||||
|
||||
static int dfs_procfs_stat(struct dfs_dentry *dentry, struct stat *st)
|
||||
{
|
||||
int ret = RT_EOK;
|
||||
struct dfs_vnode *vnode;
|
||||
|
||||
if (dentry && dentry->vnode)
|
||||
{
|
||||
vnode = dentry->vnode;
|
||||
|
||||
st->st_dev = (dev_t)(dentry->mnt->dev_id);
|
||||
st->st_ino = (ino_t)dfs_dentry_full_path_crc32(dentry);
|
||||
|
||||
st->st_gid = vnode->gid;
|
||||
st->st_uid = vnode->uid;
|
||||
st->st_mode = vnode->mode;
|
||||
st->st_nlink = vnode->nlink;
|
||||
st->st_size = vnode->size;
|
||||
st->st_mtim.tv_nsec = vnode->mtime.tv_nsec;
|
||||
st->st_mtim.tv_sec = vnode->mtime.tv_sec;
|
||||
st->st_ctim.tv_nsec = vnode->ctime.tv_nsec;
|
||||
st->st_ctim.tv_sec = vnode->ctime.tv_sec;
|
||||
st->st_atim.tv_nsec = vnode->atime.tv_nsec;
|
||||
st->st_atim.tv_sec = vnode->atime.tv_sec;
|
||||
}
|
||||
|
||||
PROC_DEBUG(" %s %d >> %s ret: %d\n", __func__, __LINE__, dentry->pathname, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dfs_procfs_statfs(struct dfs_mnt *mnt, struct statfs *buf)
|
||||
{
|
||||
if (mnt && buf)
|
||||
{
|
||||
buf->f_bsize = 512;
|
||||
buf->f_blocks = 2048 * 64; // 64M
|
||||
buf->f_bfree = buf->f_blocks;
|
||||
buf->f_bavail = buf->f_bfree;
|
||||
}
|
||||
|
||||
PROC_DEBUG(" %s %d\n", __func__, __LINE__);
|
||||
|
||||
return RT_EOK;
|
||||
}
|
||||
|
||||
static struct dfs_vnode *dfs_procfs_lookup(struct dfs_dentry *dentry)
|
||||
{
|
||||
struct dfs_vnode *vnode = RT_NULL;
|
||||
struct proc_dentry *entry = dfs_proc_find(dentry->pathname);
|
||||
|
||||
if (entry)
|
||||
{
|
||||
vnode = dfs_vnode_create();
|
||||
if (vnode)
|
||||
{
|
||||
vnode->nlink = 1;
|
||||
vnode->size = 0;
|
||||
if (S_ISDIR(entry->mode))
|
||||
{
|
||||
vnode->mode = entry->mode;
|
||||
vnode->type = FT_DIRECTORY;
|
||||
}
|
||||
else if (S_ISLNK(entry->mode))
|
||||
{
|
||||
vnode->mode = entry->mode;
|
||||
vnode->type = FT_SYMLINK;
|
||||
}
|
||||
else
|
||||
{
|
||||
vnode->mode = entry->mode;
|
||||
vnode->type = FT_REGULAR;
|
||||
}
|
||||
|
||||
vnode->data = entry;
|
||||
vnode->mnt = dentry->mnt;
|
||||
}
|
||||
|
||||
proc_release(entry);
|
||||
}
|
||||
|
||||
PROC_DEBUG(" %s %d >> %s\n", __func__, __LINE__, dentry->pathname);
|
||||
|
||||
return vnode;
|
||||
}
|
||||
|
||||
static struct dfs_vnode *dfs_procfs_create_vnode(struct dfs_dentry *dentry, int type, mode_t mode)
|
||||
{
|
||||
return RT_NULL;
|
||||
}
|
||||
|
||||
static int dfs_procfs_free_vnode(struct dfs_vnode *vnode)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dfs_file_ops _procfs_fops =
|
||||
{
|
||||
.open = dfs_procfs_open,
|
||||
.close = dfs_procfs_close,
|
||||
.lseek = generic_dfs_lseek,
|
||||
.read = dfs_procfs_read,
|
||||
.write = dfs_procfs_write,
|
||||
.ioctl = dfs_procfs_ioctl,
|
||||
.getdents = dfs_procfs_getdents,
|
||||
.poll = dfs_procfs_poll,
|
||||
.flush = dfs_procfs_flush,
|
||||
};
|
||||
|
||||
static const struct dfs_filesystem_ops _procfs_ops =
|
||||
{
|
||||
.name = "procfs",
|
||||
|
||||
.default_fops = &_procfs_fops,
|
||||
|
||||
.mount = dfs_procfs_mount,
|
||||
.umount = dfs_procfs_umount,
|
||||
.readlink = dfs_procfs_readlink,
|
||||
.unlink = dfs_procfs_unlink,
|
||||
.stat = dfs_procfs_stat,
|
||||
.statfs = dfs_procfs_statfs,
|
||||
.lookup = dfs_procfs_lookup,
|
||||
.create_vnode = dfs_procfs_create_vnode,
|
||||
.free_vnode = dfs_procfs_free_vnode,
|
||||
};
|
||||
|
||||
static struct dfs_filesystem_type _procfs =
|
||||
{
|
||||
.fs_ops = &_procfs_ops,
|
||||
};
|
||||
|
||||
int dfs_procfs_init(void)
|
||||
{
|
||||
/* register procfs file system */
|
||||
dfs_register(&_procfs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
INIT_COMPONENT_EXPORT(dfs_procfs_init);
|
||||
|
||||
int proc_read_data(struct dfs_file *file, void *buf, size_t count, off_t *pos)
|
||||
{
|
||||
if (file->fpos >= file->vnode->size)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (file->data)
|
||||
{
|
||||
count = file->vnode->size - file->fpos >= count ? count : file->vnode->size - file->fpos;
|
||||
rt_strncpy(buf, file->data + file->fpos, count);
|
||||
|
||||
file->fpos += count;
|
||||
*pos = file->fpos;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2023, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
*/
|
||||
|
||||
#ifndef __PROC_FS_H__
|
||||
#define __PROC_FS_H__
|
||||
|
||||
#include <dfs_file.h>
|
||||
|
||||
int dfs_procfs_init(void);
|
||||
|
||||
int proc_read_data(struct dfs_file *file, void *buf, size_t count, off_t *pos);
|
||||
|
||||
#endif
|
@ -365,6 +365,7 @@ static const struct dfs_file_ops _rom_fops =
|
||||
{
|
||||
.open = dfs_romfs_open,
|
||||
.close = dfs_romfs_close,
|
||||
.ioctl = dfs_romfs_ioctl,
|
||||
.lseek = generic_dfs_lseek,
|
||||
.read = dfs_romfs_read,
|
||||
.getdents = dfs_romfs_getdents,
|
||||
|
@ -99,15 +99,13 @@ static int _get_subdir(const char *path, char *name)
|
||||
|
||||
static int _free_subdir(struct tmpfs_file *dfile)
|
||||
{
|
||||
struct tmpfs_file *file;
|
||||
rt_list_t *list, *temp_list;
|
||||
struct tmpfs_file *file = RT_NULL, *tmp;
|
||||
struct tmpfs_sb *superblock;
|
||||
|
||||
RT_ASSERT(dfile->type == TMPFS_TYPE_DIR);
|
||||
|
||||
rt_list_for_each_safe(list, temp_list, &dfile->subdirs)
|
||||
dfs_vfs_for_each_subnode(file, tmp, dfile, node)
|
||||
{
|
||||
file = rt_list_entry(list, struct tmpfs_file, sibling);
|
||||
if (file->type == TMPFS_TYPE_DIR)
|
||||
{
|
||||
_free_subdir(file);
|
||||
@ -122,7 +120,7 @@ static int _free_subdir(struct tmpfs_file *dfile)
|
||||
RT_ASSERT(superblock != NULL);
|
||||
|
||||
rt_spin_lock(&superblock->lock);
|
||||
rt_list_remove(&(file->sibling));
|
||||
dfs_vfs_remove_node(&file->node);
|
||||
rt_spin_unlock(&superblock->lock);
|
||||
|
||||
rt_free(file);
|
||||
@ -141,13 +139,11 @@ static int dfs_tmpfs_mount(struct dfs_mnt *mnt,
|
||||
{
|
||||
superblock->df_size = sizeof(struct tmpfs_sb);
|
||||
superblock->magic = TMPFS_MAGIC;
|
||||
rt_list_init(&superblock->sibling);
|
||||
|
||||
superblock->root.name[0] = '/';
|
||||
superblock->root.sb = superblock;
|
||||
superblock->root.type = TMPFS_TYPE_DIR;
|
||||
rt_list_init(&superblock->root.sibling);
|
||||
rt_list_init(&superblock->root.subdirs);
|
||||
dfs_vfs_init_node(&superblock->root.node);
|
||||
|
||||
rt_spin_lock_init(&superblock->lock);
|
||||
|
||||
@ -236,8 +232,7 @@ struct tmpfs_file *dfs_tmpfs_lookup(struct tmpfs_sb *superblock,
|
||||
{
|
||||
const char *subpath, *curpath, *filename = RT_NULL;
|
||||
char subdir_name[TMPFS_NAME_MAX];
|
||||
struct tmpfs_file *file, *curfile;
|
||||
rt_list_t *list;
|
||||
struct tmpfs_file *file, *curfile, *tmp;
|
||||
|
||||
subpath = path;
|
||||
while (*subpath == '/' && *subpath)
|
||||
@ -265,9 +260,8 @@ find_subpath:
|
||||
|
||||
rt_spin_lock(&superblock->lock);
|
||||
|
||||
rt_list_for_each(list, &curfile->subdirs)
|
||||
dfs_vfs_for_each_subnode(file, tmp, curfile, node)
|
||||
{
|
||||
file = rt_list_entry(list, struct tmpfs_file, sibling);
|
||||
if (filename) /* find file */
|
||||
{
|
||||
if (rt_strcmp(file->name, filename) == 0)
|
||||
@ -503,8 +497,7 @@ static int dfs_tmpfs_getdents(struct dfs_file *file,
|
||||
{
|
||||
rt_size_t index, end;
|
||||
struct dirent *d;
|
||||
struct tmpfs_file *d_file, *n_file;
|
||||
rt_list_t *list;
|
||||
struct tmpfs_file *d_file, *n_file, *tmp;
|
||||
struct tmpfs_sb *superblock;
|
||||
|
||||
d_file = (struct tmpfs_file *)file->vnode->data;
|
||||
@ -527,9 +520,8 @@ static int dfs_tmpfs_getdents(struct dfs_file *file,
|
||||
index = 0;
|
||||
count = 0;
|
||||
|
||||
rt_list_for_each(list, &d_file->subdirs)
|
||||
dfs_vfs_for_each_subnode(n_file, tmp, d_file, node)
|
||||
{
|
||||
n_file = rt_list_entry(list, struct tmpfs_file, sibling);
|
||||
if (index >= (rt_size_t)file->fpos)
|
||||
{
|
||||
d = dirp + count;
|
||||
@ -573,7 +565,7 @@ static int dfs_tmpfs_unlink(struct dfs_dentry *dentry)
|
||||
return -ENOENT;
|
||||
|
||||
rt_spin_lock(&superblock->lock);
|
||||
rt_list_remove(&(d_file->sibling));
|
||||
dfs_vfs_remove_node(&d_file->node);
|
||||
rt_spin_unlock(&superblock->lock);
|
||||
|
||||
if (rt_atomic_load(&(dentry->ref_count)) == 1)
|
||||
@ -631,13 +623,13 @@ static int dfs_tmpfs_rename(struct dfs_dentry *old_dentry, struct dfs_dentry *ne
|
||||
RT_ASSERT(p_file != NULL);
|
||||
|
||||
rt_spin_lock(&superblock->lock);
|
||||
rt_list_remove(&(d_file->sibling));
|
||||
dfs_vfs_remove_node(&d_file->node);
|
||||
rt_spin_unlock(&superblock->lock);
|
||||
|
||||
strncpy(d_file->name, file_name, TMPFS_NAME_MAX);
|
||||
|
||||
rt_spin_lock(&superblock->lock);
|
||||
rt_list_insert_after(&(p_file->subdirs), &(d_file->sibling));
|
||||
dfs_vfs_append_node(&p_file->node, &d_file->node);
|
||||
rt_spin_unlock(&superblock->lock);
|
||||
|
||||
rt_free(parent_path);
|
||||
@ -745,8 +737,7 @@ static struct dfs_vnode *dfs_tmpfs_create_vnode(struct dfs_dentry *dentry, int t
|
||||
|
||||
strncpy(d_file->name, file_name, TMPFS_NAME_MAX);
|
||||
|
||||
rt_list_init(&(d_file->subdirs));
|
||||
rt_list_init(&(d_file->sibling));
|
||||
dfs_vfs_init_node(&d_file->node);
|
||||
d_file->data = NULL;
|
||||
d_file->size = 0;
|
||||
d_file->sb = superblock;
|
||||
@ -767,7 +758,7 @@ static struct dfs_vnode *dfs_tmpfs_create_vnode(struct dfs_dentry *dentry, int t
|
||||
#endif
|
||||
}
|
||||
rt_spin_lock(&superblock->lock);
|
||||
rt_list_insert_after(&(p_file->subdirs), &(d_file->sibling));
|
||||
dfs_vfs_append_node(&p_file->node, &d_file->node);
|
||||
rt_spin_unlock(&superblock->lock);
|
||||
|
||||
vnode->mnt = dentry->mnt;
|
||||
|
@ -12,6 +12,7 @@
|
||||
#define __DFS_TMPFS_H__
|
||||
|
||||
#include <rtthread.h>
|
||||
#include <dfs_vfs.h>
|
||||
|
||||
#define TMPFS_NAME_MAX 32
|
||||
#define TMPFS_MAGIC 0x0B0B0B0B
|
||||
@ -25,8 +26,7 @@ struct tmpfs_file
|
||||
{
|
||||
rt_uint32_t type; /* file type */
|
||||
char name[TMPFS_NAME_MAX]; /* file name */
|
||||
rt_list_t subdirs; /* file subdir list */
|
||||
rt_list_t sibling; /* file sibling list */
|
||||
struct dfs_vfs_node node; /* file node in the tmpfs */
|
||||
struct tmpfs_sb *sb; /* superblock ptr */
|
||||
rt_uint8_t *data; /* file date ptr */
|
||||
rt_size_t size; /* file size */
|
||||
|
@ -20,6 +20,38 @@ extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#define MS_RDONLY 1
|
||||
#define MS_NOSUID 2
|
||||
#define MS_NODEV 4
|
||||
#define MS_NOEXEC 8
|
||||
#define MS_SYNCHRONOUS 16
|
||||
#define MS_REMOUNT 32
|
||||
#define MS_MANDLOCK 64
|
||||
#define MS_DIRSYNC 128
|
||||
#define MS_NOATIME 1024
|
||||
#define MS_NODIRATIME 2048
|
||||
#define MS_BIND 4096
|
||||
#define MS_MOVE 8192
|
||||
#define MS_REC 16384
|
||||
#define MS_SILENT 32768
|
||||
#define MS_POSIXACL (1<<16)
|
||||
#define MS_UNBINDABLE (1<<17)
|
||||
#define MS_PRIVATE (1<<18)
|
||||
#define MS_SLAVE (1<<19)
|
||||
#define MS_SHARED (1<<20)
|
||||
#define MS_RELATIME (1<<21)
|
||||
#define MS_KERNMOUNT (1<<22)
|
||||
#define MS_I_VERSION (1<<23)
|
||||
#define MS_STRICTATIME (1<<24)
|
||||
#define MS_LAZYTIME (1<<25)
|
||||
#define MS_NOREMOTELOCK (1<<27)
|
||||
#define MS_NOSEC (1<<28)
|
||||
#define MS_BORN (1<<29)
|
||||
#define MS_ACTIVE (1<<30)
|
||||
#define MS_NOUSER (1U<<31)
|
||||
|
||||
#define MS_RMT_MASK (MS_RDONLY|MS_SYNCHRONOUS|MS_MANDLOCK|MS_I_VERSION|MS_LAZYTIME)
|
||||
|
||||
/* file system partition table */
|
||||
struct dfs_partition
|
||||
{
|
||||
@ -87,6 +119,7 @@ int dfs_unregister(struct dfs_filesystem_type *fs);
|
||||
int dfs_register(struct dfs_filesystem_type *fs);
|
||||
const char *dfs_filesystem_get_mounted_path(struct rt_device *device);
|
||||
|
||||
int dfs_remount(const char *path, rt_ubase_t flags, void *data);
|
||||
int dfs_mount(const char *device_name,
|
||||
const char *path,
|
||||
const char *filesystemtype,
|
||||
|
@ -39,6 +39,8 @@ struct dfs_mnt
|
||||
#define MNT_IS_UMOUNT 0x8 /* the mnt is unmount */
|
||||
#define MNT_IS_LOCKED 0x10 /* the mnt is locked */
|
||||
#define MNT_FORCE 0x20 /* the mnt force unmount */
|
||||
#define MNT_LAZY_UMNT 0x40 /* the mnt has pending umount */
|
||||
#define MNT_RDONLY 0x80 /* the mnt is read only */
|
||||
|
||||
rt_atomic_t ref_count; /* reference count */
|
||||
|
||||
@ -60,9 +62,16 @@ const char *dfs_mnt_get_mounted_path(struct rt_device *device);
|
||||
struct dfs_mnt* dfs_mnt_ref(struct dfs_mnt* mnt);
|
||||
int dfs_mnt_unref(struct dfs_mnt* mnt);
|
||||
|
||||
int dfs_mnt_umount(struct dfs_mnt *mnt, int flags);
|
||||
int dfs_mnt_setflags(struct dfs_mnt *mnt, int flags);
|
||||
|
||||
rt_bool_t dfs_mnt_has_child_mnt(struct dfs_mnt *mnt, const char* fullpath);
|
||||
|
||||
int dfs_mnt_foreach(struct dfs_mnt* (*func)(struct dfs_mnt *mnt, void *parameter), void *parameter);
|
||||
int dfs_mnt_umount_iter(rt_bool_t (*filter)(struct dfs_mnt *mnt, void *parameter), void *parameter);
|
||||
|
||||
typedef void (*dfs_mnt_umnt_cb_t)(struct dfs_mnt *mnt);
|
||||
RT_OBJECT_HOOKLIST_DECLARE(dfs_mnt_umnt_cb_t, dfs_mnt_umnt);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -118,6 +118,7 @@ int dfs_aspace_mmap_write(struct dfs_file *file, struct rt_varea *varea, void *d
|
||||
|
||||
void dfs_pcache_release(size_t count);
|
||||
void dfs_pcache_unmount(struct dfs_mnt *mnt);
|
||||
void dfs_pcache_clean(struct dfs_mnt *mnt);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
50
rt-thread-no-bsp/components/dfs/dfs_v2/include/dfs_vfs.h
Normal file
50
rt-thread-no-bsp/components/dfs/dfs_v2/include/dfs_vfs.h
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2024, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
*/
|
||||
|
||||
#ifndef __DFS_VFS_H__
|
||||
#define __DFS_VFS_H__
|
||||
|
||||
#include "dfs_file.h"
|
||||
#include "dfs_fs.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
struct dfs_vfs_node
|
||||
{
|
||||
rt_list_t subnode; /* file subnode list */
|
||||
rt_list_t sibling; /* file sibling list */
|
||||
};
|
||||
|
||||
rt_inline void dfs_vfs_init_node(struct dfs_vfs_node *node)
|
||||
{
|
||||
rt_list_init(&node->subnode);
|
||||
rt_list_init(&node->sibling);
|
||||
}
|
||||
|
||||
rt_inline void dfs_vfs_append_node(struct dfs_vfs_node *dir, struct dfs_vfs_node *node)
|
||||
{
|
||||
rt_list_insert_after(&(dir->subnode), &(node->sibling));
|
||||
}
|
||||
|
||||
rt_inline void dfs_vfs_remove_node(struct dfs_vfs_node *node)
|
||||
{
|
||||
rt_list_remove(&(node->sibling));
|
||||
}
|
||||
|
||||
#define dfs_vfs_for_each_subnode(node, tmp, dir, member) \
|
||||
rt_list_for_each_entry_safe(node, tmp, &dir->member.subnode, member.sibling)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /*__DFS_VFS_H__*/
|
@ -95,6 +95,52 @@ int dfs_unregister(struct dfs_filesystem_type *fs)
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define REMNT_UNSUPP_FLAGS (~(MS_REMOUNT | MS_RMT_MASK))
|
||||
int dfs_remount(const char *path, rt_ubase_t flags, void *data)
|
||||
{
|
||||
int rc = 0;
|
||||
char *fullpath = RT_NULL;
|
||||
struct dfs_mnt *mnt = RT_NULL;
|
||||
|
||||
if (flags & REMNT_UNSUPP_FLAGS)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
fullpath = dfs_normalize_path(RT_NULL, path);
|
||||
if (!fullpath)
|
||||
{
|
||||
rc = -ENOENT;
|
||||
}
|
||||
else
|
||||
{
|
||||
DLOG(msg, "dfs", "mnt", DLOG_MSG, "mnt = dfs_mnt_lookup(%s)", fullpath);
|
||||
mnt = dfs_mnt_lookup(fullpath);
|
||||
if (mnt)
|
||||
{
|
||||
dfs_lock();
|
||||
dfs_mnt_setflags(mnt, flags);
|
||||
dfs_unlock();
|
||||
}
|
||||
else
|
||||
{
|
||||
struct stat buf = {0};
|
||||
if (dfs_file_stat(fullpath, &buf) == 0 && S_ISBLK(buf.st_mode))
|
||||
{
|
||||
/* path was not already mounted on target */
|
||||
rc = -EINVAL;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* path is not a directory */
|
||||
rc = -ENOTDIR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* parent(mount path)
|
||||
* mnt_parent <- - - - - - - +
|
||||
@ -300,7 +346,7 @@ int dfs_mount(const char *device_name,
|
||||
|
||||
int dfs_umount(const char *specialfile, int flags)
|
||||
{
|
||||
int ret = -RT_ERROR;
|
||||
int ret = -1;
|
||||
char *fullpath = RT_NULL;
|
||||
struct dfs_mnt *mnt = RT_NULL;
|
||||
|
||||
@ -314,7 +360,7 @@ int dfs_umount(const char *specialfile, int flags)
|
||||
if (strcmp(mnt->fullpath, fullpath) == 0)
|
||||
{
|
||||
/* is the mount point */
|
||||
rt_atomic_t ref_count = rt_atomic_load(&(mnt->ref_count));
|
||||
rt_base_t ref_count = rt_atomic_load(&(mnt->ref_count));
|
||||
|
||||
if (!(mnt->flags & MNT_IS_LOCKED) && rt_list_isempty(&mnt->child) && (ref_count == 1 || (flags & MNT_FORCE)))
|
||||
{
|
||||
@ -327,17 +373,19 @@ int dfs_umount(const char *specialfile, int flags)
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_E("the file system is busy!");
|
||||
LOG_I("the file system is busy!");
|
||||
ret = -EBUSY;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_E("the path:%s is not a mountpoint!", fullpath);
|
||||
LOG_I("the path:%s is not a mountpoint!", fullpath);
|
||||
ret = -EINVAL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_E("no filesystem found.");
|
||||
LOG_I("no filesystem found.");
|
||||
}
|
||||
rt_free(fullpath);
|
||||
}
|
||||
|
@ -10,17 +10,21 @@
|
||||
|
||||
#include <rtthread.h>
|
||||
|
||||
#include "dfs.h"
|
||||
#include "dfs_mnt.h"
|
||||
#include "dfs_dentry.h"
|
||||
#include "dfs_private.h"
|
||||
|
||||
#include <dfs.h>
|
||||
#include <dfs_dentry.h>
|
||||
#include <dfs_mnt.h>
|
||||
#include <dfs_pcache.h>
|
||||
|
||||
#define DBG_TAG "DFS.mnt"
|
||||
#define DBG_LVL DBG_WARNING
|
||||
#include <rtdbg.h>
|
||||
|
||||
static struct dfs_mnt *_root_mnt = RT_NULL;
|
||||
|
||||
RT_OBJECT_HOOKLIST_DEFINE(dfs_mnt_umnt);
|
||||
|
||||
/*
|
||||
* mnt tree structure
|
||||
*
|
||||
@ -75,6 +79,7 @@ int dfs_mnt_insert(struct dfs_mnt* mnt, struct dfs_mnt* child)
|
||||
child = _root_mnt;
|
||||
rt_atomic_sub(&(_root_mnt->parent->ref_count), 1);
|
||||
rt_atomic_sub(&(_root_mnt->ref_count), 1);
|
||||
_root_mnt->flags &= ~MNT_IS_LOCKED;
|
||||
|
||||
_root_mnt = dfs_mnt_ref(mnt);
|
||||
mnt->parent = dfs_mnt_ref(mnt);
|
||||
@ -242,21 +247,24 @@ struct dfs_mnt* dfs_mnt_ref(struct dfs_mnt* mnt)
|
||||
return mnt;
|
||||
}
|
||||
|
||||
int dfs_mnt_unref(struct dfs_mnt* mnt)
|
||||
int dfs_mnt_unref(struct dfs_mnt *mnt)
|
||||
{
|
||||
rt_err_t ret = RT_EOK;
|
||||
rt_base_t ref_count;
|
||||
|
||||
if (mnt)
|
||||
{
|
||||
rt_atomic_sub(&(mnt->ref_count), 1);
|
||||
ref_count = rt_atomic_sub(&(mnt->ref_count), 1) - 1;
|
||||
|
||||
if (rt_atomic_load(&(mnt->ref_count)) == 0)
|
||||
if (ref_count == 0)
|
||||
{
|
||||
dfs_lock();
|
||||
|
||||
if (mnt->flags & MNT_IS_UMOUNT)
|
||||
{
|
||||
mnt->fs_ops->umount(mnt);
|
||||
|
||||
RT_OBJECT_HOOKLIST_CALL(dfs_mnt_umnt, (mnt));
|
||||
}
|
||||
|
||||
/* free full path */
|
||||
@ -278,6 +286,21 @@ int dfs_mnt_unref(struct dfs_mnt* mnt)
|
||||
return ret;
|
||||
}
|
||||
|
||||
int dfs_mnt_setflags(struct dfs_mnt *mnt, int flags)
|
||||
{
|
||||
int error = 0;
|
||||
|
||||
if (flags & MS_RDONLY)
|
||||
{
|
||||
mnt->flags |= MNT_RDONLY;
|
||||
#ifdef RT_USING_PAGECACHE
|
||||
dfs_pcache_clean(mnt);
|
||||
#endif
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
int dfs_mnt_destroy(struct dfs_mnt* mnt)
|
||||
{
|
||||
rt_err_t ret = RT_EOK;
|
||||
|
@ -16,15 +16,16 @@
|
||||
#include <dfs_pcache.h>
|
||||
#include <dfs_dentry.h>
|
||||
#include <dfs_mnt.h>
|
||||
#include <mm_page.h>
|
||||
#include <mm_private.h>
|
||||
#include <mmu.h>
|
||||
#include <tlb.h>
|
||||
|
||||
#include <rthw.h>
|
||||
|
||||
#ifdef RT_USING_PAGECACHE
|
||||
|
||||
#include <mm_page.h>
|
||||
#include <mm_private.h>
|
||||
#include <mmu.h>
|
||||
#include <tlb.h>
|
||||
|
||||
#ifndef RT_PAGECACHE_COUNT
|
||||
#define RT_PAGECACHE_COUNT 4096
|
||||
#endif
|
||||
@ -161,7 +162,7 @@ void dfs_pcache_release(size_t count)
|
||||
dfs_pcache_unlock();
|
||||
}
|
||||
|
||||
void dfs_pcache_unmount(struct dfs_mnt *mnt)
|
||||
static void _pcache_clean(struct dfs_mnt *mnt, int (*cb)(struct dfs_aspace *aspace))
|
||||
{
|
||||
rt_list_t *node = RT_NULL;
|
||||
struct dfs_aspace *aspace = RT_NULL;
|
||||
@ -176,7 +177,7 @@ void dfs_pcache_unmount(struct dfs_mnt *mnt)
|
||||
if (aspace && aspace->mnt == mnt)
|
||||
{
|
||||
dfs_aspace_clean(aspace);
|
||||
dfs_aspace_release(aspace);
|
||||
cb(aspace);
|
||||
}
|
||||
}
|
||||
|
||||
@ -188,13 +189,28 @@ void dfs_pcache_unmount(struct dfs_mnt *mnt)
|
||||
if (aspace && aspace->mnt == mnt)
|
||||
{
|
||||
dfs_aspace_clean(aspace);
|
||||
dfs_aspace_release(aspace);
|
||||
cb(aspace);
|
||||
}
|
||||
}
|
||||
|
||||
dfs_pcache_unlock();
|
||||
}
|
||||
|
||||
void dfs_pcache_unmount(struct dfs_mnt *mnt)
|
||||
{
|
||||
_pcache_clean(mnt, dfs_aspace_release);
|
||||
}
|
||||
|
||||
static int _dummy_cb(struct dfs_aspace *mnt)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dfs_pcache_clean(struct dfs_mnt *mnt)
|
||||
{
|
||||
_pcache_clean(mnt, _dummy_cb);
|
||||
}
|
||||
|
||||
static int dfs_pcache_limit_check(void)
|
||||
{
|
||||
int index = 4;
|
||||
@ -1139,14 +1155,21 @@ int dfs_aspace_write(struct dfs_file *file, const void *buf, size_t count, off_t
|
||||
|
||||
if (file && file->vnode && file->vnode->aspace)
|
||||
{
|
||||
if (!(file->vnode->aspace->ops->write))
|
||||
return ret;
|
||||
struct dfs_vnode *vnode = file->vnode;
|
||||
struct dfs_aspace *aspace = vnode->aspace;
|
||||
|
||||
struct dfs_page *page;
|
||||
char *ptr = (char *)buf;
|
||||
|
||||
if (!(aspace->ops->write))
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
else if (aspace->mnt && (aspace->mnt->flags & MNT_RDONLY))
|
||||
{
|
||||
return -EROFS;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
|
||||
while (count)
|
||||
|
@ -1,17 +1,27 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2022, RT-Thread Development Team
|
||||
* Copyright (c) 2006-2025 RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2022-11-26 GuEe-GUI first version
|
||||
* 2025-01-24 wumingzi add doxygen comment
|
||||
*/
|
||||
|
||||
#include <rtthread.h>
|
||||
#include <rtservice.h>
|
||||
#include <rtdevice.h>
|
||||
|
||||
/**
|
||||
* @addtogroup Drivers RTTHREAD Driver
|
||||
* @defgroup clk clk
|
||||
* @brief clk driver api
|
||||
* @ingroup Drivers
|
||||
* @addtogroup clk
|
||||
* @{
|
||||
*/
|
||||
|
||||
#define DBG_TAG "rtdm.clk"
|
||||
#define DBG_LVL DBG_INFO
|
||||
#include <rtdbg.h>
|
||||
@ -20,6 +30,12 @@ static struct rt_spinlock _clk_lock = { 0 };
|
||||
static rt_list_t _clk_nodes = RT_LIST_OBJECT_INIT(_clk_nodes);
|
||||
static rt_list_t _clk_notifier_nodes = RT_LIST_OBJECT_INIT(_clk_notifier_nodes);
|
||||
|
||||
/**
|
||||
* @brief Release clock node
|
||||
*
|
||||
* @param r point to reference count of clock node
|
||||
* @warning The function only can print log and MORE DETAILS SHOULD BE IMPLEMENTED.
|
||||
*/
|
||||
static void clk_release(struct rt_ref *r)
|
||||
{
|
||||
struct rt_clk_node *clk_np = rt_container_of(r, struct rt_clk_node, ref);
|
||||
@ -30,6 +46,13 @@ static void clk_release(struct rt_ref *r)
|
||||
RT_ASSERT(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Increase reference count for clock node
|
||||
*
|
||||
* @param clk_np point to clock node
|
||||
*
|
||||
* @return struct rt_clk_node * point to clock node whose reference count has increased
|
||||
*/
|
||||
rt_inline struct rt_clk_node *clk_get(struct rt_clk_node *clk_np)
|
||||
{
|
||||
rt_ref_get(&clk_np->ref);
|
||||
@ -37,11 +60,27 @@ rt_inline struct rt_clk_node *clk_get(struct rt_clk_node *clk_np)
|
||||
return clk_np;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Decrease reference count for clock node
|
||||
*
|
||||
* @param clk_np point to clock node
|
||||
*
|
||||
*/
|
||||
rt_inline void clk_put(struct rt_clk_node *clk_np)
|
||||
{
|
||||
rt_ref_put(&clk_np->ref, &clk_release);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Allocate memory space for struct clock and return it
|
||||
*
|
||||
* @param clk_np point to clock node
|
||||
* @param dev_id device identifier for the clock
|
||||
* @param con_id connection identifier for the clock
|
||||
* @param fw_node point to the firmware node associated with the clock
|
||||
*
|
||||
* @return struct rt_clk* point to clock
|
||||
*/
|
||||
static struct rt_clk *clk_alloc(struct rt_clk_node *clk_np, const char *dev_id,
|
||||
const char *con_id, void *fw_node)
|
||||
{
|
||||
@ -63,6 +102,12 @@ static struct rt_clk *clk_alloc(struct rt_clk_node *clk_np, const char *dev_id,
|
||||
return clk;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Free memory space of clock object
|
||||
*
|
||||
* @param clk point to clock
|
||||
*
|
||||
*/
|
||||
static void clk_free(struct rt_clk *clk)
|
||||
{
|
||||
struct rt_clk_node *clk_np = clk->clk_np;
|
||||
@ -75,6 +120,17 @@ static void clk_free(struct rt_clk *clk)
|
||||
rt_free(clk);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Allocate memory space and creat clock object
|
||||
*
|
||||
* @param clk_np point to clock node
|
||||
* @param dev_id device identifier for the clock
|
||||
* @param con_id connection identifier for the clock
|
||||
* @param fw_data point to the firmware data associated with the clock
|
||||
* @param fw_node point to the firmware node associated with the clock
|
||||
*
|
||||
* @return struct rt_clk* point to clock
|
||||
*/
|
||||
static struct rt_clk *clk_create(struct rt_clk_node *clk_np, const char *dev_id,
|
||||
const char *con_id, void *fw_data, void *fw_node)
|
||||
{
|
||||
@ -96,6 +152,16 @@ static struct rt_clk *clk_create(struct rt_clk_node *clk_np, const char *dev_id,
|
||||
return clk;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Notify corresponding clock from all
|
||||
*
|
||||
* @param clk_np point to clock node
|
||||
* @param msg message identifier for the event
|
||||
* @param old_rate old rate of the clock before the event
|
||||
* @param new_rate new rate of the clock after the event
|
||||
*
|
||||
* @return rt_err_t RT_EOK on notify clock sucessfully, and other value is failed.
|
||||
*/
|
||||
static rt_err_t clk_notify(struct rt_clk_node *clk_np, rt_ubase_t msg, rt_ubase_t old_rate, rt_ubase_t new_rate)
|
||||
{
|
||||
rt_err_t err = RT_EOK;
|
||||
@ -118,6 +184,13 @@ static rt_err_t clk_notify(struct rt_clk_node *clk_np, rt_ubase_t msg, rt_ubase_
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set parent clock
|
||||
*
|
||||
* @param clk_np point to clock node
|
||||
* @param parent_np point to parent rt_clk
|
||||
*
|
||||
*/
|
||||
static void clk_set_parent(struct rt_clk_node *clk_np, struct rt_clk_node *parent_np)
|
||||
{
|
||||
rt_hw_spin_lock(&_clk_lock.lock);
|
||||
@ -133,6 +206,15 @@ static const struct rt_clk_ops unused_clk_ops =
|
||||
{
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Register clock node into clock list
|
||||
*
|
||||
* @param clk_np point to child node that will be registered node.
|
||||
* @param parent_np point to parent rt_clk. If it is RT_NULL, clock node will be linked to init node.
|
||||
*
|
||||
* @retval RT_EOK
|
||||
* @retval -RT_ENOMEM
|
||||
*/
|
||||
rt_err_t rt_clk_register(struct rt_clk_node *clk_np, struct rt_clk_node *parent_np)
|
||||
{
|
||||
rt_err_t err = RT_EOK;
|
||||
@ -190,6 +272,15 @@ rt_err_t rt_clk_register(struct rt_clk_node *clk_np, struct rt_clk_node *parent_
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Unregister clock node from clock list
|
||||
*
|
||||
* @param clk_np point to child node that will be Unregistered node.
|
||||
*
|
||||
* @retval RT_EOK
|
||||
* @retval -RT_EBUSY
|
||||
* @retval -RT_EINVAL
|
||||
*/
|
||||
rt_err_t rt_clk_unregister(struct rt_clk_node *clk_np)
|
||||
{
|
||||
rt_err_t err = RT_EOK;
|
||||
@ -221,6 +312,15 @@ rt_err_t rt_clk_unregister(struct rt_clk_node *clk_np)
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Register clock notifier into notifier list
|
||||
*
|
||||
* @param clk point to clock
|
||||
* @param notifier point to notifier for register
|
||||
*
|
||||
* @retval RT_EOK
|
||||
* @retval -RT_EINVAL
|
||||
*/
|
||||
rt_err_t rt_clk_notifier_register(struct rt_clk *clk, struct rt_clk_notifier *notifier)
|
||||
{
|
||||
if (!clk || !clk->clk_np || !notifier)
|
||||
@ -239,6 +339,15 @@ rt_err_t rt_clk_notifier_register(struct rt_clk *clk, struct rt_clk_notifier *no
|
||||
return RT_EOK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Unregister clock notifier into notifier list
|
||||
*
|
||||
* @param clk point to clock
|
||||
* @param notifier point to notifier for unregister
|
||||
*
|
||||
* @retval RT_EOK
|
||||
* @retval -RT_EINVAL
|
||||
*/
|
||||
rt_err_t rt_clk_notifier_unregister(struct rt_clk *clk, struct rt_clk_notifier *notifier)
|
||||
{
|
||||
struct rt_clk_notifier *notifier_find;
|
||||
@ -266,6 +375,14 @@ rt_err_t rt_clk_notifier_unregister(struct rt_clk *clk, struct rt_clk_notifier *
|
||||
return RT_EOK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Recursively prepare clock
|
||||
*
|
||||
* @param clk Ponit to clock that will be prepared
|
||||
* @param clk_np Ponit to clock node that will be prepared
|
||||
*
|
||||
* @return rt_err_t RT_EOK on prepare clock sucessfully, and other value is failed.
|
||||
*/
|
||||
static rt_err_t clk_prepare(struct rt_clk *clk, struct rt_clk_node *clk_np)
|
||||
{
|
||||
rt_err_t err = RT_EOK;
|
||||
@ -288,6 +405,13 @@ static rt_err_t clk_prepare(struct rt_clk *clk, struct rt_clk_node *clk_np)
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Prepare clock
|
||||
*
|
||||
* @param clk
|
||||
*
|
||||
* @return rt_err_t RT_EOK on prepare clock sucessfully, and other value is failed.
|
||||
*/
|
||||
rt_err_t rt_clk_prepare(struct rt_clk *clk)
|
||||
{
|
||||
rt_err_t err = RT_EOK;
|
||||
@ -306,6 +430,13 @@ rt_err_t rt_clk_prepare(struct rt_clk *clk)
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Recursively unprepare clock
|
||||
*
|
||||
* @param clk Ponit to clock that will be unprepared
|
||||
* @param clk_np Ponit to clock node that will be unprepared
|
||||
*
|
||||
*/
|
||||
static void clk_unprepare(struct rt_clk *clk, struct rt_clk_node *clk_np)
|
||||
{
|
||||
if (clk_np->parent)
|
||||
@ -341,6 +472,13 @@ rt_err_t rt_clk_unprepare(struct rt_clk *clk)
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enable clock
|
||||
*
|
||||
* @param clk point to clock
|
||||
*
|
||||
* @return rt_err_t RT_EOK on enable clock FOREVER.
|
||||
*/
|
||||
static rt_err_t clk_enable(struct rt_clk *clk, struct rt_clk_node *clk_np)
|
||||
{
|
||||
rt_err_t err = RT_EOK;
|
||||
@ -363,6 +501,13 @@ static rt_err_t clk_enable(struct rt_clk *clk, struct rt_clk_node *clk_np)
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enable clock
|
||||
*
|
||||
* @param clk point to clock
|
||||
*
|
||||
* @return rt_err_t RT_EOK on enable clock sucessfully, and other value is failed.
|
||||
*/
|
||||
rt_err_t rt_clk_enable(struct rt_clk *clk)
|
||||
{
|
||||
rt_err_t err = RT_EOK;
|
||||
@ -379,6 +524,13 @@ rt_err_t rt_clk_enable(struct rt_clk *clk)
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Recursively disable clock
|
||||
*
|
||||
* @param clk Ponit to clock that will be disabled
|
||||
* @param clk_np Ponit to clock node that will be disabled
|
||||
*
|
||||
*/
|
||||
static void clk_disable(struct rt_clk *clk, struct rt_clk_node *clk_np)
|
||||
{
|
||||
if (clk_np->parent)
|
||||
@ -396,6 +548,12 @@ static void clk_disable(struct rt_clk *clk, struct rt_clk_node *clk_np)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disable clock
|
||||
*
|
||||
* @param clk point to clock
|
||||
*
|
||||
*/
|
||||
void rt_clk_disable(struct rt_clk *clk)
|
||||
{
|
||||
if (clk && clk->clk_np)
|
||||
@ -408,6 +566,13 @@ void rt_clk_disable(struct rt_clk *clk)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Prepare and enable clock
|
||||
*
|
||||
* @param clk point to clock
|
||||
*
|
||||
* @return rt_err_t RT_EOK on prepare and enable clock sucessfully, and other value is failed.
|
||||
*/
|
||||
rt_err_t rt_clk_prepare_enable(struct rt_clk *clk)
|
||||
{
|
||||
rt_err_t err = RT_EOK;
|
||||
@ -432,6 +597,12 @@ rt_err_t rt_clk_prepare_enable(struct rt_clk *clk)
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disable and unprepare clock
|
||||
*
|
||||
* @param clk point to clock
|
||||
*
|
||||
*/
|
||||
void rt_clk_disable_unprepare(struct rt_clk *clk)
|
||||
{
|
||||
RT_DEBUG_NOT_IN_INTERRUPT;
|
||||
@ -443,6 +614,13 @@ void rt_clk_disable_unprepare(struct rt_clk *clk)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Prepare clock array for mutipule out clock
|
||||
*
|
||||
* @param clk_arr point to clock array
|
||||
*
|
||||
* @return rt_err_t RT_EOK on prepare clock array sucessfully, and other value is failed.
|
||||
*/
|
||||
rt_err_t rt_clk_array_prepare(struct rt_clk_array *clk_arr)
|
||||
{
|
||||
rt_err_t err = RT_EOK;
|
||||
@ -490,6 +668,13 @@ rt_err_t rt_clk_array_unprepare(struct rt_clk_array *clk_arr)
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enable clock array for mutipule out clock
|
||||
*
|
||||
* @param clk_arr point to clock array
|
||||
*
|
||||
* @return rt_err_t RT_EOK on Enable clock array sucessfully, and other value is failed.
|
||||
*/
|
||||
rt_err_t rt_clk_array_enable(struct rt_clk_array *clk_arr)
|
||||
{
|
||||
rt_err_t err = RT_EOK;
|
||||
@ -516,6 +701,12 @@ rt_err_t rt_clk_array_enable(struct rt_clk_array *clk_arr)
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enable clock array for mutipule out clock
|
||||
*
|
||||
* @param clk_arr point to clock array
|
||||
*
|
||||
*/
|
||||
void rt_clk_array_disable(struct rt_clk_array *clk_arr)
|
||||
{
|
||||
if (clk_arr)
|
||||
@ -527,6 +718,14 @@ void rt_clk_array_disable(struct rt_clk_array *clk_arr)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Prepare and enable clock array
|
||||
*
|
||||
* @param clk_arr point to clock array
|
||||
*
|
||||
* @return rt_err_t RT_EOK on prepare and enable clock array sucessfully, and other
|
||||
value is failed.
|
||||
*/
|
||||
rt_err_t rt_clk_array_prepare_enable(struct rt_clk_array *clk_arr)
|
||||
{
|
||||
rt_err_t err;
|
||||
@ -544,12 +743,27 @@ rt_err_t rt_clk_array_prepare_enable(struct rt_clk_array *clk_arr)
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disable and unprepare clock array
|
||||
*
|
||||
* @param clk_arr point to clock array
|
||||
*
|
||||
*/
|
||||
void rt_clk_array_disable_unprepare(struct rt_clk_array *clk_arr)
|
||||
{
|
||||
rt_clk_array_disable(clk_arr);
|
||||
rt_clk_array_unprepare(clk_arr);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set clock rate range
|
||||
*
|
||||
* @param clk point to clock
|
||||
* @param min minimum clock rate
|
||||
* @param max minimum clock rate
|
||||
*
|
||||
* @return rt_err_t RT_EOK on set clock rate range sucessfully, and other value is failed.
|
||||
*/
|
||||
rt_err_t rt_clk_set_rate_range(struct rt_clk *clk, rt_ubase_t min, rt_ubase_t max)
|
||||
{
|
||||
rt_err_t err = RT_EOK;
|
||||
@ -590,6 +804,14 @@ rt_err_t rt_clk_set_rate_range(struct rt_clk *clk, rt_ubase_t min, rt_ubase_t ma
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set minimum clock rate
|
||||
*
|
||||
* @param clk point to clock
|
||||
* @param rate miminum clock rate
|
||||
*
|
||||
* @return rt_err_t RT_EOK on set minimum clock rate sucessfully, and other value is failed.
|
||||
*/
|
||||
rt_err_t rt_clk_set_min_rate(struct rt_clk *clk, rt_ubase_t rate)
|
||||
{
|
||||
rt_err_t err = RT_EOK;
|
||||
@ -604,6 +826,14 @@ rt_err_t rt_clk_set_min_rate(struct rt_clk *clk, rt_ubase_t rate)
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set maximum clock rate
|
||||
*
|
||||
* @param clk point to clock
|
||||
* @param rate maximum clock rate
|
||||
*
|
||||
* @return rt_err_t RT_EOK on set maximum clock rate sucessfully, and other value is failed.
|
||||
*/
|
||||
rt_err_t rt_clk_set_max_rate(struct rt_clk *clk, rt_ubase_t rate)
|
||||
{
|
||||
rt_err_t err = RT_EOK;
|
||||
@ -618,6 +848,14 @@ rt_err_t rt_clk_set_max_rate(struct rt_clk *clk, rt_ubase_t rate)
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set clock rate
|
||||
*
|
||||
* @param clk point to clock
|
||||
* @param rate target rate
|
||||
*
|
||||
* @return rt_err_t RT_EOK on set clock rate sucessfully, and other value is failed.
|
||||
*/
|
||||
rt_err_t rt_clk_set_rate(struct rt_clk *clk, rt_ubase_t rate)
|
||||
{
|
||||
rt_err_t err = RT_EOK;
|
||||
@ -666,6 +904,13 @@ rt_err_t rt_clk_set_rate(struct rt_clk *clk, rt_ubase_t rate)
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get clock rate
|
||||
*
|
||||
* @param clk point to clock
|
||||
*
|
||||
* @return rt_ubase_t clock rate or error code
|
||||
*/
|
||||
rt_ubase_t rt_clk_get_rate(struct rt_clk *clk)
|
||||
{
|
||||
rt_ubase_t rate = 0;
|
||||
@ -685,6 +930,14 @@ rt_ubase_t rt_clk_get_rate(struct rt_clk *clk)
|
||||
return rate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set clock phase
|
||||
*
|
||||
* @param clk point to clock
|
||||
* @param degrees target phase and the unit of phase is degree
|
||||
*
|
||||
* @return rt_err_t RT_EOK on set clock phase sucessfully, and other value is failed.
|
||||
*/
|
||||
rt_err_t rt_clk_set_phase(struct rt_clk *clk, int degrees)
|
||||
{
|
||||
rt_err_t err = RT_EOK;
|
||||
@ -701,6 +954,13 @@ rt_err_t rt_clk_set_phase(struct rt_clk *clk, int degrees)
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get clock phase
|
||||
*
|
||||
* @param clk point to clock
|
||||
*
|
||||
* @return rt_base_t clock phase or error code
|
||||
*/
|
||||
rt_base_t rt_clk_get_phase(struct rt_clk *clk)
|
||||
{
|
||||
rt_base_t res = RT_EOK;
|
||||
@ -717,6 +977,16 @@ rt_base_t rt_clk_get_phase(struct rt_clk *clk)
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if clock rate is in the minimum to maximun and get it
|
||||
*
|
||||
* @param clk point to clock
|
||||
* @param rate rate will be checked
|
||||
*
|
||||
* @return rt_base_t get the correct rate
|
||||
* @note if parameter rate less than the minimum or more than maximum, the
|
||||
retrun rate will be set to minimum ormaximum value
|
||||
*/
|
||||
rt_base_t rt_clk_round_rate(struct rt_clk *clk, rt_ubase_t rate)
|
||||
{
|
||||
rt_base_t res = -RT_EINVAL;
|
||||
@ -761,6 +1031,14 @@ rt_base_t rt_clk_round_rate(struct rt_clk *clk, rt_ubase_t rate)
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set clock parent object
|
||||
*
|
||||
* @param clk point to clock
|
||||
* @param clk_parent point to parent clock
|
||||
*
|
||||
* @return rt_err_t RT_EOK on set clock parent sucessfully, and other value is failed.
|
||||
*/
|
||||
rt_err_t rt_clk_set_parent(struct rt_clk *clk, struct rt_clk *clk_parent)
|
||||
{
|
||||
rt_err_t err = RT_EOK;
|
||||
@ -777,6 +1055,14 @@ rt_err_t rt_clk_set_parent(struct rt_clk *clk, struct rt_clk *clk_parent)
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get parent clock pointer
|
||||
*
|
||||
* @param clk child clock
|
||||
*
|
||||
* @return struct rt_clk* parent clock object pointer will be return, unless child
|
||||
clock node havn't parent node instead return RT_NULL
|
||||
*/
|
||||
struct rt_clk *rt_clk_get_parent(struct rt_clk *clk)
|
||||
{
|
||||
struct rt_clk *parent = RT_NULL;
|
||||
@ -795,6 +1081,14 @@ struct rt_clk *rt_clk_get_parent(struct rt_clk *clk)
|
||||
return parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get clock array pointer from ofw device node
|
||||
*
|
||||
* @param dev point to dev
|
||||
*
|
||||
* @return struct rt_clk_array* if use ofw and under normal circumstance, it will return
|
||||
clock array pointer and other value is RT_NULL
|
||||
*/
|
||||
struct rt_clk_array *rt_clk_get_array(struct rt_device *dev)
|
||||
{
|
||||
struct rt_clk_array *clk_arr = RT_NULL;
|
||||
@ -806,6 +1100,15 @@ struct rt_clk_array *rt_clk_get_array(struct rt_device *dev)
|
||||
return clk_arr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get clock pointer from ofw device node by index
|
||||
*
|
||||
* @param dev point to dev
|
||||
* @param index index of clock object
|
||||
*
|
||||
* @return struct rt_clk* if use ofw and under normal circumstance, it will return clock
|
||||
pointer and other value is RT_NULL
|
||||
*/
|
||||
struct rt_clk *rt_clk_get_by_index(struct rt_device *dev, int index)
|
||||
{
|
||||
struct rt_clk *clk = RT_NULL;
|
||||
@ -817,6 +1120,15 @@ struct rt_clk *rt_clk_get_by_index(struct rt_device *dev, int index)
|
||||
return clk;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get clock pointer from ofw device node by name
|
||||
*
|
||||
* @param dev point to dev
|
||||
* @param name name of clock object
|
||||
*
|
||||
* @return struct rt_clk* if use ofw and under normal circumstance, it will return clock
|
||||
pointer and other value is RT_NULL
|
||||
*/
|
||||
struct rt_clk *rt_clk_get_by_name(struct rt_device *dev, const char *name)
|
||||
{
|
||||
struct rt_clk *clk = RT_NULL;
|
||||
@ -828,6 +1140,12 @@ struct rt_clk *rt_clk_get_by_name(struct rt_device *dev, const char *name)
|
||||
return clk;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Put reference count of all colock in the clock array
|
||||
*
|
||||
* @param clk_arr point to clock array
|
||||
*
|
||||
*/
|
||||
void rt_clk_array_put(struct rt_clk_array *clk_arr)
|
||||
{
|
||||
if (clk_arr)
|
||||
@ -848,6 +1166,12 @@ void rt_clk_array_put(struct rt_clk_array *clk_arr)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Put reference count of clock
|
||||
*
|
||||
* @param clk point to clock
|
||||
*
|
||||
*/
|
||||
void rt_clk_put(struct rt_clk *clk)
|
||||
{
|
||||
if (clk)
|
||||
@ -858,6 +1182,16 @@ void rt_clk_put(struct rt_clk *clk)
|
||||
}
|
||||
|
||||
#ifdef RT_USING_OFW
|
||||
/**
|
||||
* @brief Get a clock object from a device tree node without acquiring a lock
|
||||
*
|
||||
* @param np point to ofw node
|
||||
* @param index index of clock in ofw
|
||||
* @param name connection identifier for the clock
|
||||
* @param locked lock flag for indicating whether the caller holds the lock
|
||||
*
|
||||
* @return struct rt_clk* point to the newly created clock object, or an error pointer
|
||||
*/
|
||||
static struct rt_clk *ofw_get_clk_no_lock(struct rt_ofw_node *np, int index, const char *name, rt_bool_t locked)
|
||||
{
|
||||
struct rt_clk *clk = RT_NULL;
|
||||
@ -914,6 +1248,15 @@ static struct rt_clk *ofw_get_clk_no_lock(struct rt_ofw_node *np, int index, con
|
||||
return clk;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get clock from ofw with acquiring a spin lock
|
||||
*
|
||||
* @param np point to ofw node
|
||||
* @param index index of clock in ofw
|
||||
* @param name connection identifier for the clock
|
||||
*
|
||||
* @return struct rt_clk* point to the newly created clock object, or an error pointer
|
||||
*/
|
||||
static struct rt_clk *ofw_get_clk(struct rt_ofw_node *np, int index, const char *name)
|
||||
{
|
||||
struct rt_clk *clk;
|
||||
@ -927,6 +1270,13 @@ static struct rt_clk *ofw_get_clk(struct rt_ofw_node *np, int index, const char
|
||||
return clk;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get clock array from ofw
|
||||
*
|
||||
* @param np point to ofw node
|
||||
*
|
||||
* @return struct rt_clk_array* point to the newly created clock array, or an error pointer
|
||||
*/
|
||||
struct rt_clk_array *rt_ofw_get_clk_array(struct rt_ofw_node *np)
|
||||
{
|
||||
int count;
|
||||
@ -984,6 +1334,14 @@ struct rt_clk_array *rt_ofw_get_clk_array(struct rt_ofw_node *np)
|
||||
return clk_arr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get clock from ofw with acquiring a spin lock by index and node pointer
|
||||
*
|
||||
* @param np point to ofw node
|
||||
* @param index index of clock in ofw
|
||||
*
|
||||
* @return struct rt_clk* point to the newly created clock object, or an error pointer
|
||||
*/
|
||||
struct rt_clk *rt_ofw_get_clk(struct rt_ofw_node *np, int index)
|
||||
{
|
||||
struct rt_clk *clk = RT_NULL;
|
||||
@ -996,6 +1354,14 @@ struct rt_clk *rt_ofw_get_clk(struct rt_ofw_node *np, int index)
|
||||
return clk;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get clock from ofw with acquiring a spin lock by name
|
||||
*
|
||||
* @param np point to ofw node
|
||||
* @param name name of clock will be returned
|
||||
*
|
||||
* @return struct rt_clk* point to the newly created clock object, or an error pointer
|
||||
*/
|
||||
struct rt_clk *rt_ofw_get_clk_by_name(struct rt_ofw_node *np, const char *name)
|
||||
{
|
||||
struct rt_clk *clk = RT_NULL;
|
||||
@ -1013,6 +1379,13 @@ struct rt_clk *rt_ofw_get_clk_by_name(struct rt_ofw_node *np, const char *name)
|
||||
return clk;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Count number of clocks in ofw
|
||||
*
|
||||
* @param clk_ofw_np point to ofw node
|
||||
*
|
||||
* @return rt_ssize_t number of clocks
|
||||
*/
|
||||
rt_ssize_t rt_ofw_count_of_clk(struct rt_ofw_node *clk_ofw_np)
|
||||
{
|
||||
if (clk_ofw_np)
|
||||
@ -1080,4 +1453,7 @@ rt_ssize_t rt_ofw_count_of_clk(struct rt_ofw_node *clk_ofw_np)
|
||||
|
||||
return -RT_EINVAL;
|
||||
}
|
||||
|
||||
#endif /* RT_USING_OFW */
|
||||
|
||||
/**@}*/
|
@ -31,9 +31,9 @@
|
||||
#include <rtdevice.h> /* for wqueue_init */
|
||||
#endif /* RT_USING_POSIX_DEVIO */
|
||||
|
||||
#ifdef RT_USING_DFS_V2
|
||||
#if defined (RT_USING_DFS_V2) && defined (RT_USING_DFS_DEVFS)
|
||||
#include <devfs.h>
|
||||
#endif /* RT_USING_DFS_V2 */
|
||||
#endif /* RT_USING_DFS_V2 RT_USING_DFS_DEVFS */
|
||||
|
||||
#ifdef RT_USING_DEVICE
|
||||
|
||||
@ -84,7 +84,7 @@ rt_err_t rt_device_register(rt_device_t dev,
|
||||
rt_wqueue_init(&(dev->wait_queue));
|
||||
#endif /* RT_USING_POSIX_DEVIO */
|
||||
|
||||
#ifdef RT_USING_DFS_V2
|
||||
#if defined (RT_USING_DFS_V2) && defined (RT_USING_DFS_DEVFS)
|
||||
dfs_devfs_device_add(dev);
|
||||
#endif /* RT_USING_DFS_V2 */
|
||||
|
||||
|
@ -17,203 +17,227 @@ if RT_USING_I2C
|
||||
default n
|
||||
endif
|
||||
|
||||
config RT_USING_SOFT_I2C
|
||||
menuconfig RT_USING_SOFT_I2C
|
||||
bool "Use GPIO to soft simulate I2C"
|
||||
default n
|
||||
select RT_USING_PIN
|
||||
select RT_USING_I2C_BITOPS
|
||||
if RT_USING_SOFT_I2C
|
||||
config RT_USING_SOFT_I2C1
|
||||
bool "Enable I2C1 Bus (software simulation)"
|
||||
default y
|
||||
if RT_USING_SOFT_I2C1
|
||||
config RT_SOFT_I2C1_SCL_PIN
|
||||
int "SCL pin number"
|
||||
range 0 32767
|
||||
default 1
|
||||
config RT_SOFT_I2C1_SDA_PIN
|
||||
int "SDA pin number"
|
||||
range 0 32767
|
||||
default 2
|
||||
config RT_SOFT_I2C1_BUS_NAME
|
||||
string "Bus name"
|
||||
default "i2c1"
|
||||
config RT_SOFT_I2C1_TIMING_DELAY
|
||||
int "Timing delay (us)"
|
||||
range 0 32767
|
||||
default 10
|
||||
config RT_SOFT_I2C1_TIMING_TIMEOUT
|
||||
int "Timing timeout (tick)"
|
||||
range 0 32767
|
||||
default 10
|
||||
endif
|
||||
config RT_USING_SOFT_I2C2
|
||||
bool "Enable I2C2 Bus (software simulation)"
|
||||
default n
|
||||
if RT_USING_SOFT_I2C2
|
||||
config RT_SOFT_I2C2_SCL_PIN
|
||||
int "SCL pin number"
|
||||
range 0 32767
|
||||
default 3
|
||||
config RT_SOFT_I2C2_SDA_PIN
|
||||
int "SDA pin number"
|
||||
range 0 32767
|
||||
default 4
|
||||
config RT_SOFT_I2C2_BUS_NAME
|
||||
string "Bus name"
|
||||
default "i2c2"
|
||||
config RT_SOFT_I2C2_TIMING_DELAY
|
||||
int "Timing delay (us)"
|
||||
range 0 32767
|
||||
default 10
|
||||
config RT_SOFT_I2C2_TIMING_TIMEOUT
|
||||
int "Timing timeout (tick)"
|
||||
range 0 32767
|
||||
default 10
|
||||
endif
|
||||
config RT_USING_SOFT_I2C3
|
||||
bool "Enable I2C3 Bus (software simulation)"
|
||||
default n
|
||||
if RT_USING_SOFT_I2C3
|
||||
config RT_SOFT_I2C3_SCL_PIN
|
||||
int "SCL pin number"
|
||||
range 0 32767
|
||||
default 5
|
||||
config RT_SOFT_I2C3_SDA_PIN
|
||||
int "SDA pin number"
|
||||
range 0 32767
|
||||
default 6
|
||||
config RT_SOFT_I2C3_BUS_NAME
|
||||
string "Bus name"
|
||||
default "i2c3"
|
||||
config RT_SOFT_I2C3_TIMING_DELAY
|
||||
int "Timing delay (us)"
|
||||
range 0 32767
|
||||
default 10
|
||||
config RT_SOFT_I2C3_TIMING_TIMEOUT
|
||||
int "Timing timeout (tick)"
|
||||
range 0 32767
|
||||
default 10
|
||||
endif
|
||||
config RT_USING_SOFT_I2C4
|
||||
bool "Enable I2C4 Bus (software simulation)"
|
||||
default n
|
||||
if RT_USING_SOFT_I2C4
|
||||
config RT_SOFT_I2C4_SCL_PIN
|
||||
int "SCL pin number"
|
||||
range 0 32767
|
||||
default 7
|
||||
config RT_SOFT_I2C4_SDA_PIN
|
||||
int "SDA pin number"
|
||||
range 0 32767
|
||||
default 8
|
||||
config RT_SOFT_I2C4_BUS_NAME
|
||||
string "Bus name"
|
||||
default "i2c4"
|
||||
config RT_SOFT_I2C4_TIMING_DELAY
|
||||
int "Timing delay (us)"
|
||||
range 0 32767
|
||||
default 10
|
||||
config RT_SOFT_I2C4_TIMING_TIMEOUT
|
||||
int "Timing timeout (tick)"
|
||||
range 0 32767
|
||||
default 10
|
||||
endif
|
||||
config RT_USING_SOFT_I2C5
|
||||
bool "Enable I2C5 Bus (software simulation)"
|
||||
default n
|
||||
if RT_USING_SOFT_I2C5
|
||||
config RT_SOFT_I2C5_SCL_PIN
|
||||
int "SCL pin number"
|
||||
range 0 32767
|
||||
default 9
|
||||
config RT_SOFT_I2C5_SDA_PIN
|
||||
int "SDA pin number"
|
||||
range 0 32767
|
||||
default 10
|
||||
config RT_SOFT_I2C5_BUS_NAME
|
||||
string "Bus name"
|
||||
default "i2c5"
|
||||
config RT_SOFT_I2C5_TIMING_DELAY
|
||||
int "Timing delay (us)"
|
||||
range 0 32767
|
||||
default 10
|
||||
config RT_SOFT_I2C5_TIMING_TIMEOUT
|
||||
int "Timing timeout (tick)"
|
||||
range 0 32767
|
||||
default 10
|
||||
endif
|
||||
config RT_USING_SOFT_I2C6
|
||||
bool "Enable I2C6 Bus (software simulation)"
|
||||
default n
|
||||
if RT_USING_SOFT_I2C6
|
||||
config RT_SOFT_I2C6_SCL_PIN
|
||||
int "SCL pin number"
|
||||
range 0 32767
|
||||
default 11
|
||||
config RT_SOFT_I2C6_SDA_PIN
|
||||
int "SDA pin number"
|
||||
range 0 32767
|
||||
default 12
|
||||
config RT_SOFT_I2C6_BUS_NAME
|
||||
string "Bus name"
|
||||
default "i2c6"
|
||||
config RT_SOFT_I2C6_TIMING_DELAY
|
||||
int "Timing delay (us)"
|
||||
range 0 32767
|
||||
default 10
|
||||
config RT_SOFT_I2C6_TIMING_TIMEOUT
|
||||
int "Timing timeout (tick)"
|
||||
range 0 32767
|
||||
default 10
|
||||
endif
|
||||
config RT_USING_SOFT_I2C7
|
||||
bool "Enable I2C7 Bus (software simulation)"
|
||||
default n
|
||||
if RT_USING_SOFT_I2C7
|
||||
config RT_SOFT_I2C7_SCL_PIN
|
||||
int "SCL pin number"
|
||||
range 0 32767
|
||||
default 13
|
||||
config RT_SOFT_I2C7_SDA_PIN
|
||||
int "SDA pin number"
|
||||
range 0 32767
|
||||
default 14
|
||||
config RT_SOFT_I2C7_BUS_NAME
|
||||
string "Bus name"
|
||||
default "i2c7"
|
||||
config RT_SOFT_I2C7_TIMING_DELAY
|
||||
int "Timing delay (us)"
|
||||
range 0 32767
|
||||
default 10
|
||||
config RT_SOFT_I2C7_TIMING_TIMEOUT
|
||||
int "Timing timeout (tick)"
|
||||
range 0 32767
|
||||
default 10
|
||||
endif
|
||||
config RT_USING_SOFT_I2C8
|
||||
bool "Enable I2C8 Bus (software simulation)"
|
||||
default n
|
||||
if RT_USING_SOFT_I2C8
|
||||
config RT_SOFT_I2C8_SCL_PIN
|
||||
int "SCL pin number"
|
||||
range 0 32767
|
||||
default 15
|
||||
config RT_SOFT_I2C8_SDA_PIN
|
||||
int "SDA pin number"
|
||||
range 0 32767
|
||||
default 16
|
||||
config RT_SOFT_I2C8_BUS_NAME
|
||||
string "Bus name"
|
||||
default "i2c8"
|
||||
config RT_SOFT_I2C8_TIMING_DELAY
|
||||
int "Timing delay (us)"
|
||||
range 0 32767
|
||||
default 10
|
||||
config RT_SOFT_I2C8_TIMING_TIMEOUT
|
||||
int "Timing timeout (tick)"
|
||||
range 0 32767
|
||||
default 10
|
||||
endif
|
||||
endif
|
||||
if RT_USING_SOFT_I2C
|
||||
menuconfig RT_USING_SOFT_I2C0
|
||||
bool "Enable I2C0 Bus (software simulation)"
|
||||
default y
|
||||
if RT_USING_SOFT_I2C0
|
||||
config RT_SOFT_I2C0_SCL_PIN
|
||||
int "SCL pin number"
|
||||
range 0 32767
|
||||
default 1
|
||||
config RT_SOFT_I2C0_SDA_PIN
|
||||
int "SDA pin number"
|
||||
range 0 32767
|
||||
default 2
|
||||
config RT_SOFT_I2C0_BUS_NAME
|
||||
string "Bus name"
|
||||
default "i2c0"
|
||||
config RT_SOFT_I2C0_TIMING_DELAY
|
||||
int "Timing delay (us)"
|
||||
range 0 32767
|
||||
default 10
|
||||
config RT_SOFT_I2C0_TIMING_TIMEOUT
|
||||
int "Timing timeout (tick)"
|
||||
range 0 32767
|
||||
default 10
|
||||
endif
|
||||
menuconfig RT_USING_SOFT_I2C1
|
||||
bool "Enable I2C1 Bus (software simulation)"
|
||||
default y
|
||||
if RT_USING_SOFT_I2C1
|
||||
config RT_SOFT_I2C1_SCL_PIN
|
||||
int "SCL pin number"
|
||||
range 0 32767
|
||||
default 3
|
||||
config RT_SOFT_I2C1_SDA_PIN
|
||||
int "SDA pin number"
|
||||
range 0 32767
|
||||
default 4
|
||||
config RT_SOFT_I2C1_BUS_NAME
|
||||
string "Bus name"
|
||||
default "i2c1"
|
||||
config RT_SOFT_I2C1_TIMING_DELAY
|
||||
int "Timing delay (us)"
|
||||
range 0 32767
|
||||
default 10
|
||||
config RT_SOFT_I2C1_TIMING_TIMEOUT
|
||||
int "Timing timeout (tick)"
|
||||
range 0 32767
|
||||
default 10
|
||||
endif
|
||||
menuconfig RT_USING_SOFT_I2C2
|
||||
bool "Enable I2C2 Bus (software simulation)"
|
||||
default n
|
||||
if RT_USING_SOFT_I2C2
|
||||
config RT_SOFT_I2C2_SCL_PIN
|
||||
int "SCL pin number"
|
||||
range 0 32767
|
||||
default 5
|
||||
config RT_SOFT_I2C2_SDA_PIN
|
||||
int "SDA pin number"
|
||||
range 0 32767
|
||||
default 6
|
||||
config RT_SOFT_I2C2_BUS_NAME
|
||||
string "Bus name"
|
||||
default "i2c2"
|
||||
config RT_SOFT_I2C2_TIMING_DELAY
|
||||
int "Timing delay (us)"
|
||||
range 0 32767
|
||||
default 10
|
||||
config RT_SOFT_I2C2_TIMING_TIMEOUT
|
||||
int "Timing timeout (tick)"
|
||||
range 0 32767
|
||||
default 10
|
||||
endif
|
||||
menuconfig RT_USING_SOFT_I2C3
|
||||
bool "Enable I2C3 Bus (software simulation)"
|
||||
default n
|
||||
if RT_USING_SOFT_I2C3
|
||||
config RT_SOFT_I2C3_SCL_PIN
|
||||
int "SCL pin number"
|
||||
range 0 32767
|
||||
default 7
|
||||
config RT_SOFT_I2C3_SDA_PIN
|
||||
int "SDA pin number"
|
||||
range 0 32767
|
||||
default 8
|
||||
config RT_SOFT_I2C3_BUS_NAME
|
||||
string "Bus name"
|
||||
default "i2c3"
|
||||
config RT_SOFT_I2C3_TIMING_DELAY
|
||||
int "Timing delay (us)"
|
||||
range 0 32767
|
||||
default 10
|
||||
config RT_SOFT_I2C3_TIMING_TIMEOUT
|
||||
int "Timing timeout (tick)"
|
||||
range 0 32767
|
||||
default 10
|
||||
endif
|
||||
menuconfig RT_USING_SOFT_I2C4
|
||||
bool "Enable I2C4 Bus (software simulation)"
|
||||
default n
|
||||
if RT_USING_SOFT_I2C4
|
||||
config RT_SOFT_I2C4_SCL_PIN
|
||||
int "SCL pin number"
|
||||
range 0 32767
|
||||
default 9
|
||||
config RT_SOFT_I2C4_SDA_PIN
|
||||
int "SDA pin number"
|
||||
range 0 32767
|
||||
default 10
|
||||
config RT_SOFT_I2C4_BUS_NAME
|
||||
string "Bus name"
|
||||
default "i2c4"
|
||||
config RT_SOFT_I2C4_TIMING_DELAY
|
||||
int "Timing delay (us)"
|
||||
range 0 32767
|
||||
default 10
|
||||
config RT_SOFT_I2C4_TIMING_TIMEOUT
|
||||
int "Timing timeout (tick)"
|
||||
range 0 32767
|
||||
default 10
|
||||
endif
|
||||
menuconfig RT_USING_SOFT_I2C5
|
||||
bool "Enable I2C5 Bus (software simulation)"
|
||||
default n
|
||||
if RT_USING_SOFT_I2C5
|
||||
config RT_SOFT_I2C5_SCL_PIN
|
||||
int "SCL pin number"
|
||||
range 0 32767
|
||||
default 11
|
||||
config RT_SOFT_I2C5_SDA_PIN
|
||||
int "SDA pin number"
|
||||
range 0 32767
|
||||
default 12
|
||||
config RT_SOFT_I2C5_BUS_NAME
|
||||
string "Bus name"
|
||||
default "i2c5"
|
||||
config RT_SOFT_I2C5_TIMING_DELAY
|
||||
int "Timing delay (us)"
|
||||
range 0 32767
|
||||
default 10
|
||||
config RT_SOFT_I2C5_TIMING_TIMEOUT
|
||||
int "Timing timeout (tick)"
|
||||
range 0 32767
|
||||
default 10
|
||||
endif
|
||||
menuconfig RT_USING_SOFT_I2C6
|
||||
bool "Enable I2C6 Bus (software simulation)"
|
||||
default n
|
||||
if RT_USING_SOFT_I2C6
|
||||
config RT_SOFT_I2C6_SCL_PIN
|
||||
int "SCL pin number"
|
||||
range 0 32767
|
||||
default 13
|
||||
config RT_SOFT_I2C6_SDA_PIN
|
||||
int "SDA pin number"
|
||||
range 0 32767
|
||||
default 14
|
||||
config RT_SOFT_I2C6_BUS_NAME
|
||||
string "Bus name"
|
||||
default "i2c6"
|
||||
config RT_SOFT_I2C6_TIMING_DELAY
|
||||
int "Timing delay (us)"
|
||||
range 0 32767
|
||||
default 10
|
||||
config RT_SOFT_I2C6_TIMING_TIMEOUT
|
||||
int "Timing timeout (tick)"
|
||||
range 0 32767
|
||||
default 10
|
||||
endif
|
||||
menuconfig RT_USING_SOFT_I2C7
|
||||
bool "Enable I2C7 Bus (software simulation)"
|
||||
default n
|
||||
if RT_USING_SOFT_I2C7
|
||||
config RT_SOFT_I2C7_SCL_PIN
|
||||
int "SCL pin number"
|
||||
range 0 32767
|
||||
default 15
|
||||
config RT_SOFT_I2C7_SDA_PIN
|
||||
int "SDA pin number"
|
||||
range 0 32767
|
||||
default 16
|
||||
config RT_SOFT_I2C7_BUS_NAME
|
||||
string "Bus name"
|
||||
default "i2c7"
|
||||
config RT_SOFT_I2C7_TIMING_DELAY
|
||||
int "Timing delay (us)"
|
||||
range 0 32767
|
||||
default 10
|
||||
config RT_SOFT_I2C7_TIMING_TIMEOUT
|
||||
int "Timing timeout (tick)"
|
||||
range 0 32767
|
||||
default 10
|
||||
endif
|
||||
menuconfig RT_USING_SOFT_I2C8
|
||||
bool "Enable I2C8 Bus (software simulation)"
|
||||
default n
|
||||
if RT_USING_SOFT_I2C8
|
||||
config RT_SOFT_I2C8_SCL_PIN
|
||||
int "SCL pin number"
|
||||
range 0 32767
|
||||
default 17
|
||||
config RT_SOFT_I2C8_SDA_PIN
|
||||
int "SDA pin number"
|
||||
range 0 32767
|
||||
default 18
|
||||
config RT_SOFT_I2C8_BUS_NAME
|
||||
string "Bus name"
|
||||
default "i2c8"
|
||||
config RT_SOFT_I2C8_TIMING_DELAY
|
||||
int "Timing delay (us)"
|
||||
range 0 32767
|
||||
default 10
|
||||
config RT_SOFT_I2C8_TIMING_TIMEOUT
|
||||
int "Timing timeout (tick)"
|
||||
range 0 32767
|
||||
default 10
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
@ -11,7 +11,8 @@
|
||||
#include <rtdevice.h>
|
||||
|
||||
#ifdef RT_USING_SOFT_I2C
|
||||
#if !defined(RT_USING_SOFT_I2C1) && !defined(RT_USING_SOFT_I2C2) &&\
|
||||
#if !defined(RT_USING_SOFT_I2C0) &&\
|
||||
!defined(RT_USING_SOFT_I2C1) && !defined(RT_USING_SOFT_I2C2) &&\
|
||||
!defined(RT_USING_SOFT_I2C3) && !defined(RT_USING_SOFT_I2C4) &&\
|
||||
!defined(RT_USING_SOFT_I2C5) && !defined(RT_USING_SOFT_I2C6) &&\
|
||||
!defined(RT_USING_SOFT_I2C7) && !defined(RT_USING_SOFT_I2C8)
|
||||
@ -48,6 +49,15 @@ struct rt_soft_i2c
|
||||
|
||||
struct soft_i2c_config i2c_cfg[] =
|
||||
{
|
||||
#ifdef RT_USING_SOFT_I2C0
|
||||
{
|
||||
.scl_pin = RT_SOFT_I2C0_SCL_PIN,
|
||||
.sda_pin = RT_SOFT_I2C0_SDA_PIN,
|
||||
.bus_name = RT_SOFT_I2C0_BUS_NAME,
|
||||
.timing_delay = RT_SOFT_I2C0_TIMING_DELAY,
|
||||
.timing_timeout = RT_SOFT_I2C0_TIMING_TIMEOUT,
|
||||
},
|
||||
#endif //RT_USING_SOFT_I2C0
|
||||
#ifdef RT_USING_SOFT_I2C1
|
||||
{
|
||||
.scl_pin = RT_SOFT_I2C1_SCL_PIN,
|
||||
|
@ -1,11 +1,12 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2022, RT-Thread Development Team
|
||||
* Copyright (c) 2006-2025 RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2023-02-25 GuEe-GUI first version
|
||||
* 2025-01-24 wumingzi add doxygen comment
|
||||
*/
|
||||
|
||||
#ifndef __BLK_H__
|
||||
@ -15,10 +16,22 @@
|
||||
#include <rtthread.h>
|
||||
#include <drivers/classes/block.h>
|
||||
|
||||
/**
|
||||
* @addtogroup Drivers RTTHREAD Driver
|
||||
* @defgroup blk blk
|
||||
* @brief blk driver api
|
||||
* @ingroup Drivers
|
||||
* @addtogroup blk
|
||||
* @{
|
||||
*/
|
||||
|
||||
struct rt_dm_ida;
|
||||
struct rt_blk_device;
|
||||
struct rt_blk_disk_ops;
|
||||
|
||||
/**
|
||||
* @brief Physical blk device
|
||||
*/
|
||||
struct rt_blk_disk
|
||||
{
|
||||
struct rt_device parent;
|
||||
@ -44,6 +57,9 @@ struct rt_blk_disk
|
||||
struct rt_semaphore usr_lock;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Configure the blk device.
|
||||
*/
|
||||
struct rt_blk_disk_ops
|
||||
{
|
||||
rt_ssize_t (*read)(struct rt_blk_disk *disk, rt_off_t sector, void *buffer,
|
||||
@ -60,6 +76,9 @@ struct rt_blk_disk_ops
|
||||
#ifndef __DFS_H__
|
||||
#include <dfs_fs.h>
|
||||
|
||||
/**
|
||||
* @brief Logical blk device, if you don't used DFS it will be defined by default.
|
||||
*/
|
||||
struct rt_blk_device
|
||||
{
|
||||
struct rt_device parent;
|
||||
@ -77,11 +96,41 @@ struct rt_blk_device
|
||||
struct rt_blk_device;
|
||||
#endif /* __DFS_H__ */
|
||||
|
||||
/**
|
||||
* @brief Register the blk disk device
|
||||
* @param disk Point to blk disk
|
||||
* @return rt_err_t error code
|
||||
*/
|
||||
rt_err_t rt_hw_blk_disk_register(struct rt_blk_disk *disk);
|
||||
|
||||
/**
|
||||
* @brief Unregister the blk disk device
|
||||
* @param disk Point to blk disk
|
||||
* @return rt_err_t error code
|
||||
*/
|
||||
rt_err_t rt_hw_blk_disk_unregister(struct rt_blk_disk *disk);
|
||||
|
||||
/**
|
||||
* @brief Probe and register the blk disk partition
|
||||
* @param disk Point to blk disk
|
||||
* @return rt_err_t error code
|
||||
*/
|
||||
rt_err_t rt_blk_disk_probe_partition(struct rt_blk_disk *disk);
|
||||
|
||||
/**
|
||||
* @brief Get the blk disk capacity
|
||||
* @param disk Point to blk disk
|
||||
* @return rt_ssize_t sector count or error code
|
||||
*/
|
||||
rt_ssize_t rt_blk_disk_get_capacity(struct rt_blk_disk *disk);
|
||||
|
||||
/**
|
||||
* @brief Get the sector size
|
||||
* @param disk Point to blk disk
|
||||
* @return rt_ssize_t bytes per sector or error code
|
||||
*/
|
||||
rt_ssize_t rt_blk_disk_get_logical_block_size(struct rt_blk_disk *disk);
|
||||
|
||||
#endif /* __BLK_H__ */
|
||||
/*! @}*/
|
||||
|
||||
#endif /* __BLK_H__ */
|
@ -1,11 +1,12 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2022, RT-Thread Development Team
|
||||
* Copyright (c) 2006-2025 RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2022-11-26 GuEe-GUI first version
|
||||
* 2025-01-24 wumingzi add doxygen comment
|
||||
*/
|
||||
|
||||
#ifndef __CLK_H__
|
||||
@ -16,29 +17,40 @@
|
||||
#include <ref.h>
|
||||
#include <drivers/ofw.h>
|
||||
|
||||
/**
|
||||
* @addtogroup Drivers RTTHREAD Driver
|
||||
* @defgroup clk clk
|
||||
* @brief clk driver api
|
||||
* @ingroup Drivers
|
||||
* @addtogroup clk
|
||||
* @{
|
||||
*/
|
||||
|
||||
#define RT_CLK_NODE_OBJ_NAME "CLKNP"
|
||||
|
||||
struct rt_clk_ops;
|
||||
struct rt_reset_control_node;
|
||||
|
||||
/**
|
||||
* @brief Clk node, it is a pat of clk source or controller
|
||||
* @note Defined as the array like this if the CLK have multi out clocks:
|
||||
* @code{.c}
|
||||
* struct XYZ_single_clk
|
||||
* {
|
||||
* struct rt_clk_node parent;
|
||||
* ...
|
||||
* };
|
||||
*
|
||||
* struct XYZ_multi_clk
|
||||
* {
|
||||
* struct rt_clk_node parent[N];
|
||||
* ...
|
||||
* };
|
||||
* @endcode
|
||||
* We assume the 'N' is the max value of element in 'clock-indices' if OFW.
|
||||
*/
|
||||
struct rt_clk_node
|
||||
{
|
||||
/*
|
||||
* Defined as the array like this if if the CLK have multi out clocks:
|
||||
*
|
||||
* struct XYZ_single_clk
|
||||
* {
|
||||
* struct rt_clk_node parent;
|
||||
* ...
|
||||
* };
|
||||
*
|
||||
* struct XYZ_multi_clk
|
||||
* {
|
||||
* struct rt_clk_node parent[N];
|
||||
* ...
|
||||
* };
|
||||
* We assume the 'N' is the max value of element in 'clock-indices' if OFW.
|
||||
*/
|
||||
struct rt_object rt_parent;
|
||||
|
||||
rt_list_t list;
|
||||
@ -62,6 +74,9 @@ struct rt_clk_node
|
||||
rt_size_t multi_clk;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Constant rate clk
|
||||
*/
|
||||
struct rt_clk_fixed_rate
|
||||
{
|
||||
struct rt_clk_node clk;
|
||||
@ -70,6 +85,9 @@ struct rt_clk_fixed_rate
|
||||
rt_ubase_t fixed_accuracy;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Clk object, it can be clk source or controller
|
||||
*/
|
||||
struct rt_clk
|
||||
{
|
||||
struct rt_clk_node *clk_np;
|
||||
@ -85,6 +103,9 @@ struct rt_clk
|
||||
void *priv;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Clk array
|
||||
*/
|
||||
struct rt_clk_array
|
||||
{
|
||||
rt_size_t count;
|
||||
@ -118,6 +139,9 @@ struct rt_clk_notifier;
|
||||
typedef rt_err_t (*rt_clk_notifier_callback)(struct rt_clk_notifier *notifier,
|
||||
rt_ubase_t msg, rt_ubase_t old_rate, rt_ubase_t new_rate);
|
||||
|
||||
/**
|
||||
* @brief Clock notifier, it containers of clock list and callback function
|
||||
*/
|
||||
struct rt_clk_notifier
|
||||
{
|
||||
rt_list_t list;
|
||||
@ -192,4 +216,6 @@ rt_inline rt_ssize_t rt_ofw_count_of_clk(struct rt_ofw_node *clk_ofw_np)
|
||||
}
|
||||
#endif /* RT_USING_OFW */
|
||||
|
||||
#endif /* __CLK_H__ */
|
||||
/*! @}*/
|
||||
|
||||
#endif /* __CLK_H__ */
|
@ -13,6 +13,7 @@
|
||||
|
||||
#include <rtdef.h>
|
||||
#include <rtconfig.h>
|
||||
#include "completion.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@ -42,6 +43,7 @@ struct rt_workqueue
|
||||
struct rt_semaphore sem;
|
||||
rt_thread_t work_thread;
|
||||
struct rt_spinlock spinlock;
|
||||
struct rt_completion wakeup_completion;
|
||||
};
|
||||
|
||||
struct rt_work
|
||||
@ -52,7 +54,7 @@ struct rt_work
|
||||
void *work_data;
|
||||
rt_uint16_t flags;
|
||||
rt_uint16_t type;
|
||||
struct rt_timer timer;
|
||||
rt_tick_t timeout_tick;
|
||||
struct rt_workqueue *workqueue;
|
||||
};
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2023, RT-Thread Development Team
|
||||
* Copyright (c) 2006-2022, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
@ -10,6 +10,7 @@
|
||||
* 2021-08-14 Jackistang add comments for function interface
|
||||
* 2022-01-16 Meco Man add rt_work_urgent()
|
||||
* 2023-09-15 xqyjlj perf rt_hw_interrupt_disable/enable
|
||||
* 2024-12-21 yuqingli delete timer, using list
|
||||
*/
|
||||
|
||||
#include <rthw.h>
|
||||
@ -17,8 +18,6 @@
|
||||
|
||||
#ifdef RT_USING_HEAP
|
||||
|
||||
static void _delayed_work_timeout_handler(void *parameter);
|
||||
|
||||
rt_inline rt_err_t _workqueue_work_completion(struct rt_workqueue *queue)
|
||||
{
|
||||
rt_err_t result;
|
||||
@ -50,38 +49,61 @@ rt_inline rt_err_t _workqueue_work_completion(struct rt_workqueue *queue)
|
||||
|
||||
static void _workqueue_thread_entry(void *parameter)
|
||||
{
|
||||
rt_base_t level;
|
||||
struct rt_work *work;
|
||||
rt_base_t level;
|
||||
struct rt_work *work;
|
||||
struct rt_workqueue *queue;
|
||||
rt_tick_t current_tick;
|
||||
rt_int32_t delay_tick;
|
||||
void (*work_func)(struct rt_work *work, void *work_data);
|
||||
void *work_data;
|
||||
|
||||
queue = (struct rt_workqueue *) parameter;
|
||||
queue = (struct rt_workqueue *)parameter;
|
||||
RT_ASSERT(queue != RT_NULL);
|
||||
|
||||
while (1)
|
||||
{
|
||||
level = rt_spin_lock_irqsave(&(queue->spinlock));
|
||||
|
||||
/* timer check */
|
||||
current_tick = rt_tick_get();
|
||||
delay_tick = RT_WAITING_FOREVER;
|
||||
while (!rt_list_isempty(&(queue->delayed_list)))
|
||||
{
|
||||
work = rt_list_entry(queue->delayed_list.next, struct rt_work, list);
|
||||
if ((current_tick - work->timeout_tick) < RT_TICK_MAX / 2)
|
||||
{
|
||||
rt_list_remove(&(work->list));
|
||||
rt_list_insert_after(queue->work_list.prev, &(work->list));
|
||||
work->flags &= ~RT_WORK_STATE_SUBMITTING;
|
||||
work->flags |= RT_WORK_STATE_PENDING;
|
||||
}
|
||||
else
|
||||
{
|
||||
delay_tick = work->timeout_tick - current_tick;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (rt_list_isempty(&(queue->work_list)))
|
||||
{
|
||||
/* no software timer exist, suspend self. */
|
||||
rt_thread_suspend_with_flag(rt_thread_self(), RT_UNINTERRUPTIBLE);
|
||||
|
||||
/* release lock after suspend so we will not lost any wakeups */
|
||||
rt_spin_unlock_irqrestore(&(queue->spinlock), level);
|
||||
|
||||
rt_schedule();
|
||||
/* wait for work completion */
|
||||
rt_completion_wait(&(queue->wakeup_completion), delay_tick);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* we have work to do with. */
|
||||
work = rt_list_entry(queue->work_list.next, struct rt_work, list);
|
||||
rt_list_remove(&(work->list));
|
||||
queue->work_current = work;
|
||||
work->flags &= ~RT_WORK_STATE_PENDING;
|
||||
work->workqueue = RT_NULL;
|
||||
queue->work_current = work;
|
||||
work->flags &= ~RT_WORK_STATE_PENDING;
|
||||
work->workqueue = RT_NULL;
|
||||
work_func = work->work_func;
|
||||
work_data = work->work_data;
|
||||
rt_spin_unlock_irqrestore(&(queue->spinlock), level);
|
||||
|
||||
/* do work */
|
||||
work->work_func(work, work->work_data);
|
||||
work_func(work, work_data);
|
||||
/* clean current work */
|
||||
queue->work_current = RT_NULL;
|
||||
|
||||
@ -93,52 +115,52 @@ static void _workqueue_thread_entry(void *parameter)
|
||||
static rt_err_t _workqueue_submit_work(struct rt_workqueue *queue,
|
||||
struct rt_work *work, rt_tick_t ticks)
|
||||
{
|
||||
rt_base_t level;
|
||||
rt_err_t err = RT_EOK;
|
||||
rt_base_t level;
|
||||
rt_err_t err = RT_EOK;
|
||||
struct rt_work *work_tmp;
|
||||
rt_list_t *list_tmp;
|
||||
|
||||
level = rt_spin_lock_irqsave(&(queue->spinlock));
|
||||
|
||||
/* remove list */
|
||||
rt_list_remove(&(work->list));
|
||||
work->flags &= ~RT_WORK_STATE_PENDING;
|
||||
work->flags = 0;
|
||||
|
||||
if (ticks == 0)
|
||||
{
|
||||
rt_list_insert_after(queue->work_list.prev, &(work->list));
|
||||
work->flags |= RT_WORK_STATE_PENDING;
|
||||
work->workqueue = queue;
|
||||
work->flags |= RT_WORK_STATE_PENDING;
|
||||
work->workqueue = queue;
|
||||
|
||||
/* whether the workqueue is doing work */
|
||||
if (queue->work_current == RT_NULL)
|
||||
{
|
||||
/* resume work thread, and do a re-schedule if succeed */
|
||||
rt_thread_resume(queue->work_thread);
|
||||
}
|
||||
rt_completion_done(&(queue->wakeup_completion));
|
||||
err = RT_EOK;
|
||||
}
|
||||
else if (ticks < RT_TICK_MAX / 2)
|
||||
{
|
||||
/* Timer started */
|
||||
if (work->flags & RT_WORK_STATE_SUBMITTING)
|
||||
{
|
||||
rt_timer_control(&work->timer, RT_TIMER_CTRL_SET_TIME, &ticks);
|
||||
}
|
||||
else
|
||||
{
|
||||
rt_timer_init(&(work->timer), "work", _delayed_work_timeout_handler,
|
||||
work, ticks, RT_TIMER_FLAG_ONE_SHOT | RT_TIMER_FLAG_SOFT_TIMER);
|
||||
work->flags |= RT_WORK_STATE_SUBMITTING;
|
||||
}
|
||||
work->workqueue = queue;
|
||||
/* insert delay work list */
|
||||
rt_list_insert_after(queue->delayed_list.prev, &(work->list));
|
||||
work->flags |= RT_WORK_STATE_SUBMITTING;
|
||||
work->workqueue = queue;
|
||||
work->timeout_tick = rt_tick_get() + ticks;
|
||||
|
||||
err = rt_timer_start(&(work->timer));
|
||||
list_tmp = &(queue->delayed_list);
|
||||
for (work_tmp = rt_list_entry(list_tmp->next, struct rt_work, list);
|
||||
&work_tmp->list != list_tmp;
|
||||
work_tmp = rt_list_entry(work_tmp->list.next, struct rt_work, list))
|
||||
{
|
||||
if ((work_tmp->timeout_tick - work->timeout_tick) < RT_TICK_MAX / 2)
|
||||
{
|
||||
list_tmp = &(work_tmp->list);
|
||||
break;
|
||||
}
|
||||
}
|
||||
rt_list_insert_before(list_tmp, &(work->list));
|
||||
|
||||
rt_completion_done(&(queue->wakeup_completion));
|
||||
err = RT_EOK;
|
||||
}
|
||||
else
|
||||
{
|
||||
err = - RT_ERROR;
|
||||
err = -RT_ERROR;
|
||||
}
|
||||
|
||||
rt_spin_unlock_irqrestore(&(queue->spinlock), level);
|
||||
return err;
|
||||
}
|
||||
@ -146,61 +168,17 @@ static rt_err_t _workqueue_submit_work(struct rt_workqueue *queue,
|
||||
static rt_err_t _workqueue_cancel_work(struct rt_workqueue *queue, struct rt_work *work)
|
||||
{
|
||||
rt_base_t level;
|
||||
rt_err_t err;
|
||||
rt_err_t err;
|
||||
|
||||
level = rt_spin_lock_irqsave(&(queue->spinlock));
|
||||
rt_list_remove(&(work->list));
|
||||
work->flags &= ~RT_WORK_STATE_PENDING;
|
||||
/* Timer started */
|
||||
if (work->flags & RT_WORK_STATE_SUBMITTING)
|
||||
{
|
||||
if ((err = rt_timer_stop(&(work->timer))) != RT_EOK)
|
||||
{
|
||||
goto exit;
|
||||
}
|
||||
rt_timer_detach(&(work->timer));
|
||||
work->flags &= ~RT_WORK_STATE_SUBMITTING;
|
||||
}
|
||||
err = queue->work_current != work ? RT_EOK : -RT_EBUSY;
|
||||
work->flags = 0;
|
||||
err = queue->work_current != work ? RT_EOK : -RT_EBUSY;
|
||||
work->workqueue = RT_NULL;
|
||||
exit:
|
||||
rt_spin_unlock_irqrestore(&(queue->spinlock), level);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void _delayed_work_timeout_handler(void *parameter)
|
||||
{
|
||||
struct rt_work *work;
|
||||
struct rt_workqueue *queue;
|
||||
rt_base_t level;
|
||||
|
||||
work = (struct rt_work *)parameter;
|
||||
queue = work->workqueue;
|
||||
|
||||
RT_ASSERT(work->flags & RT_WORK_STATE_SUBMITTING);
|
||||
RT_ASSERT(queue != RT_NULL);
|
||||
|
||||
level = rt_spin_lock_irqsave(&(queue->spinlock));
|
||||
rt_timer_detach(&(work->timer));
|
||||
work->flags &= ~RT_WORK_STATE_SUBMITTING;
|
||||
/* remove delay list */
|
||||
rt_list_remove(&(work->list));
|
||||
/* insert work queue */
|
||||
if (queue->work_current != work)
|
||||
{
|
||||
rt_list_insert_after(queue->work_list.prev, &(work->list));
|
||||
work->flags |= RT_WORK_STATE_PENDING;
|
||||
}
|
||||
/* whether the workqueue is doing work */
|
||||
if (queue->work_current == RT_NULL)
|
||||
{
|
||||
/* resume work thread, and do a re-schedule if succeed */
|
||||
rt_thread_resume(queue->work_thread);
|
||||
}
|
||||
|
||||
rt_spin_unlock_irqrestore(&(queue->spinlock), level);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize a work item, binding with a callback function.
|
||||
*
|
||||
@ -221,8 +199,8 @@ void rt_work_init(struct rt_work *work,
|
||||
work->work_func = work_func;
|
||||
work->work_data = work_data;
|
||||
work->workqueue = RT_NULL;
|
||||
work->flags = 0;
|
||||
work->type = 0;
|
||||
work->flags = 0;
|
||||
work->type = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -248,6 +226,7 @@ struct rt_workqueue *rt_workqueue_create(const char *name, rt_uint16_t stack_siz
|
||||
rt_list_init(&(queue->delayed_list));
|
||||
queue->work_current = RT_NULL;
|
||||
rt_sem_init(&(queue->sem), "wqueue", 0, RT_IPC_FLAG_FIFO);
|
||||
rt_completion_init(&(queue->wakeup_completion));
|
||||
|
||||
/* create the work thread */
|
||||
queue->work_thread = rt_thread_create(name, _workqueue_thread_entry, queue, stack_size, priority, 10);
|
||||
@ -292,7 +271,6 @@ rt_err_t rt_workqueue_destroy(struct rt_workqueue *queue)
|
||||
* @param work is a pointer to the work item object.
|
||||
*
|
||||
* @return RT_EOK Success.
|
||||
* -RT_EBUSY This work item is executing.
|
||||
*/
|
||||
rt_err_t rt_workqueue_dowork(struct rt_workqueue *queue, struct rt_work *work)
|
||||
{
|
||||
@ -314,7 +292,6 @@ rt_err_t rt_workqueue_dowork(struct rt_workqueue *queue, struct rt_work *work)
|
||||
* NOTE: The max timeout tick should be no more than (RT_TICK_MAX/2 - 1)
|
||||
*
|
||||
* @return RT_EOK Success.
|
||||
* -RT_EBUSY This work item is executing.
|
||||
* -RT_ERROR The ticks parameter is invalid.
|
||||
*/
|
||||
rt_err_t rt_workqueue_submit_work(struct rt_workqueue *queue, struct rt_work *work, rt_tick_t ticks)
|
||||
@ -346,14 +323,10 @@ rt_err_t rt_workqueue_urgent_work(struct rt_workqueue *queue, struct rt_work *wo
|
||||
/* NOTE: the work MUST be initialized firstly */
|
||||
rt_list_remove(&(work->list));
|
||||
rt_list_insert_after(&queue->work_list, &(work->list));
|
||||
/* whether the workqueue is doing work */
|
||||
if (queue->work_current == RT_NULL)
|
||||
{
|
||||
/* resume work thread, and do a re-schedule if succeed */
|
||||
rt_thread_resume(queue->work_thread);
|
||||
}
|
||||
|
||||
rt_completion_done(&(queue->wakeup_completion));
|
||||
rt_spin_unlock_irqrestore(&(queue->spinlock), level);
|
||||
|
||||
return RT_EOK;
|
||||
}
|
||||
|
||||
@ -448,7 +421,6 @@ static struct rt_workqueue *sys_workq; /* system work queue */
|
||||
* NOTE: The max timeout tick should be no more than (RT_TICK_MAX/2 - 1)
|
||||
*
|
||||
* @return RT_EOK Success.
|
||||
* -RT_EBUSY This work item is executing.
|
||||
* -RT_ERROR The ticks parameter is invalid.
|
||||
*/
|
||||
rt_err_t rt_work_submit(struct rt_work *work, rt_tick_t ticks)
|
||||
|
@ -131,7 +131,7 @@ static rt_err_t _serial_ty_bypass(struct rt_serial_device* serial, char ch,void
|
||||
rt_inline void _setup_serial(struct rt_serial_device* serial, lwp_tty_t tp,
|
||||
struct serial_tty_context *softc)
|
||||
{
|
||||
rt_bypass_lower_register(serial, "tty",RT_BYPASS_PROTECT_LEVEL_1, _serial_ty_bypass,(void *)tp);
|
||||
rt_bypass_lower_register(serial, "tty", RT_BYPASS_PROTECT_LEVEL_1, _serial_ty_bypass, (void *)tp);
|
||||
}
|
||||
|
||||
rt_inline void _restore_serial(struct rt_serial_device *serial, lwp_tty_t tp,
|
||||
@ -232,7 +232,7 @@ static void serial_tty_close(struct lwp_tty *tp)
|
||||
|
||||
LOG_D("%s", __func__);
|
||||
|
||||
_restore_serial(serial, tp, softc);
|
||||
rt_bypass_lower_unregister(serial, RT_BYPASS_PROTECT_LEVEL_1);
|
||||
rt_device_close(&serial->parent);
|
||||
}
|
||||
|
||||
|
@ -14,6 +14,183 @@ config RT_USING_SPI
|
||||
default n
|
||||
endif
|
||||
|
||||
menuconfig RT_USING_SOFT_SPI
|
||||
bool "Use GPIO to soft simulate SPI"
|
||||
default n
|
||||
select RT_USING_PIN
|
||||
select RT_USING_SPI_BITOPS
|
||||
if RT_USING_SOFT_SPI
|
||||
menuconfig RT_USING_SOFT_SPI0
|
||||
bool "Enable SPI0 Bus (software simulation)"
|
||||
default y
|
||||
if RT_USING_SOFT_SPI0
|
||||
config RT_SOFT_SPI0_SCK_PIN
|
||||
int "SCK pin number"
|
||||
range 0 32767
|
||||
default 1
|
||||
config RT_SOFT_SPI0_MISO_PIN
|
||||
int "MISO pin number"
|
||||
range 0 32767
|
||||
default 2
|
||||
config RT_SOFT_SPI0_MOSI_PIN
|
||||
int "MOSI pin number"
|
||||
range 0 32767
|
||||
default 3
|
||||
config RT_SOFT_SPI0_BUS_NAME
|
||||
string "Bus name"
|
||||
default "spi0"
|
||||
config RT_SOFT_SPI0_TIMING_DELAY
|
||||
int "Timing delay (us)"
|
||||
range 0 32767
|
||||
default 1
|
||||
endif
|
||||
menuconfig RT_USING_SOFT_SPI1
|
||||
bool "Enable SPI1 Bus (software simulation)"
|
||||
default y
|
||||
if RT_USING_SOFT_SPI1
|
||||
config RT_SOFT_SPI1_SCK_PIN
|
||||
int "SCK pin number"
|
||||
range 0 32767
|
||||
default 4
|
||||
config RT_SOFT_SPI1_MISO_PIN
|
||||
int "MISO pin number"
|
||||
range 0 32767
|
||||
default 5
|
||||
config RT_SOFT_SPI1_MOSI_PIN
|
||||
int "MOSI pin number"
|
||||
range 0 32767
|
||||
default 6
|
||||
config RT_SOFT_SPI1_BUS_NAME
|
||||
string "Bus name"
|
||||
default "spi1"
|
||||
config RT_SOFT_SPI1_TIMING_DELAY
|
||||
int "Timing delay (us)"
|
||||
range 0 32767
|
||||
default 1
|
||||
endif
|
||||
menuconfig RT_USING_SOFT_SPI2
|
||||
bool "Enable SPI2 Bus (software simulation)"
|
||||
default n
|
||||
if RT_USING_SOFT_SPI2
|
||||
config RT_SOFT_SPI2_SCK_PIN
|
||||
int "SCK pin number"
|
||||
range 0 32767
|
||||
default 7
|
||||
config RT_SOFT_SPI2_MISO_PIN
|
||||
int "MISO pin number"
|
||||
range 0 32767
|
||||
default 8
|
||||
config RT_SOFT_SPI2_MOSI_PIN
|
||||
int "MOSI pin number"
|
||||
range 0 32767
|
||||
default 9
|
||||
config RT_SOFT_SPI2_BUS_NAME
|
||||
string "Bus name"
|
||||
default "spi2"
|
||||
config RT_SOFT_SPI2_TIMING_DELAY
|
||||
int "Timing delay (us)"
|
||||
range 0 32767
|
||||
default 1
|
||||
endif
|
||||
menuconfig RT_USING_SOFT_SPI3
|
||||
bool "Enable SPI3 Bus (software simulation)"
|
||||
default n
|
||||
if RT_USING_SOFT_SPI3
|
||||
config RT_SOFT_SPI3_SCK_PIN
|
||||
int "SCK pin number"
|
||||
range 0 32767
|
||||
default 10
|
||||
config RT_SOFT_SPI3_MISO_PIN
|
||||
int "MISO pin number"
|
||||
range 0 32767
|
||||
default 11
|
||||
config RT_SOFT_SPI3_MOSI_PIN
|
||||
int "MOSI pin number"
|
||||
range 0 32767
|
||||
default 12
|
||||
config RT_SOFT_SPI3_BUS_NAME
|
||||
string "Bus name"
|
||||
default "spi3"
|
||||
config RT_SOFT_SPI3_TIMING_DELAY
|
||||
int "Timing delay (us)"
|
||||
range 0 32767
|
||||
default 1
|
||||
endif
|
||||
menuconfig RT_USING_SOFT_SPI4
|
||||
bool "Enable SPI4 Bus (software simulation)"
|
||||
default n
|
||||
if RT_USING_SOFT_SPI4
|
||||
config RT_SOFT_SPI4_SCK_PIN
|
||||
int "SCK pin number"
|
||||
range 0 32767
|
||||
default 13
|
||||
config RT_SOFT_SPI4_MISO_PIN
|
||||
int "MISO pin number"
|
||||
range 0 32767
|
||||
default 14
|
||||
config RT_SOFT_SPI4_MOSI_PIN
|
||||
int "MOSI pin number"
|
||||
range 0 32767
|
||||
default 15
|
||||
config RT_SOFT_SPI4_BUS_NAME
|
||||
string "Bus name"
|
||||
default "spi4"
|
||||
config RT_SOFT_SPI4_TIMING_DELAY
|
||||
int "Timing delay (us)"
|
||||
range 0 32767
|
||||
default 1
|
||||
endif
|
||||
menuconfig RT_USING_SOFT_SPI5
|
||||
bool "Enable SPI5 Bus (software simulation)"
|
||||
default n
|
||||
if RT_USING_SOFT_SPI5
|
||||
config RT_SOFT_SPI5_SCK_PIN
|
||||
int "SCK pin number"
|
||||
range 0 32767
|
||||
default 16
|
||||
config RT_SOFT_SPI5_MISO_PIN
|
||||
int "MISO pin number"
|
||||
range 0 32767
|
||||
default 17
|
||||
config RT_SOFT_SPI5_MOSI_PIN
|
||||
int "MOSI pin number"
|
||||
range 0 32767
|
||||
default 18
|
||||
config RT_SOFT_SPI5_BUS_NAME
|
||||
string "Bus name"
|
||||
default "spi5"
|
||||
config RT_SOFT_SPI5_TIMING_DELAY
|
||||
int "Timing delay (us)"
|
||||
range 0 32767
|
||||
default 1
|
||||
endif
|
||||
menuconfig RT_USING_SOFT_SPI6
|
||||
bool "Enable SPI6 Bus (software simulation)"
|
||||
default n
|
||||
if RT_USING_SOFT_SPI6
|
||||
config RT_SOFT_SPI6_SCK_PIN
|
||||
int "SCK pin number"
|
||||
range 0 32767
|
||||
default 19
|
||||
config RT_SOFT_SPI6_MISO_PIN
|
||||
int "MISO pin number"
|
||||
range 0 32767
|
||||
default 20
|
||||
config RT_SOFT_SPI6_MOSI_PIN
|
||||
int "MOSI pin number"
|
||||
range 0 32767
|
||||
default 21
|
||||
config RT_SOFT_SPI6_BUS_NAME
|
||||
string "Bus name"
|
||||
default "spi6"
|
||||
config RT_SOFT_SPI6_TIMING_DELAY
|
||||
int "Timing delay (us)"
|
||||
range 0 32767
|
||||
default 1
|
||||
endif
|
||||
|
||||
endif
|
||||
|
||||
config RT_USING_QSPI
|
||||
bool "Enable QSPI mode"
|
||||
default n
|
||||
|
@ -10,6 +10,9 @@ LOCAL_CFLAGS = ''
|
||||
if GetDepend('RT_USING_SPI_BITOPS'):
|
||||
src += ['dev_spi_bit_ops.c']
|
||||
|
||||
if GetDepend('RT_USING_SOFT_SPI'):
|
||||
src += ['dev_soft_spi.c']
|
||||
|
||||
if GetDepend('RT_USING_QSPI'):
|
||||
src += ['dev_qspi_core.c']
|
||||
|
||||
|
269
rt-thread-no-bsp/components/drivers/spi/dev_soft_spi.c
Normal file
269
rt-thread-no-bsp/components/drivers/spi/dev_soft_spi.c
Normal file
@ -0,0 +1,269 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2025 RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2025-01-23 CYFS first version
|
||||
*/
|
||||
#include <rthw.h>
|
||||
#include <rtdevice.h>
|
||||
#include <dev_spi_bit_ops.h>
|
||||
|
||||
#ifdef RT_USING_SOFT_SPI
|
||||
#if !defined(RT_USING_SOFT_SPI0) &&\
|
||||
!defined(RT_USING_SOFT_SPI1) && !defined(RT_USING_SOFT_SPI2) &&\
|
||||
!defined(RT_USING_SOFT_SPI3) && !defined(RT_USING_SOFT_SPI4) &&\
|
||||
!defined(RT_USING_SOFT_SPI5) && !defined(RT_USING_SOFT_SPI6)
|
||||
#error "Please define at least one RT_USING_SOFT_SPIx"
|
||||
/*
|
||||
This driver can be disabled at:
|
||||
menuconfig -> RT-Thread Components -> Device Drivers -> Using I2C device drivers
|
||||
*/
|
||||
#endif
|
||||
|
||||
#define DBG_ENABLE
|
||||
#define DBG_TAG "SPI_S"
|
||||
#ifdef RT_SPI_BITOPS_DEBUG
|
||||
#define DBG_LEVEL DBG_LOG
|
||||
#endif
|
||||
#include <rtdbg.h>
|
||||
|
||||
/* spi config class */
|
||||
struct rt_soft_spi_config
|
||||
{
|
||||
rt_base_t sck;
|
||||
rt_base_t miso;
|
||||
rt_base_t mosi;
|
||||
rt_uint32_t timing_delay;
|
||||
const char *bus_name;
|
||||
};
|
||||
|
||||
/* spi dirver class */
|
||||
struct rt_soft_spi
|
||||
{
|
||||
struct rt_spi_bit_obj spi;
|
||||
struct rt_spi_bit_ops ops;
|
||||
struct rt_soft_spi_config *cfg;
|
||||
};
|
||||
|
||||
static struct rt_soft_spi_config soft_spi_config[] =
|
||||
{
|
||||
#ifdef RT_USING_SOFT_SPI0
|
||||
{
|
||||
.sck = RT_SOFT_SPI0_SCK_PIN,
|
||||
.miso = RT_SOFT_SPI0_MISO_PIN,
|
||||
.mosi = RT_SOFT_SPI0_MOSI_PIN,
|
||||
.timing_delay = RT_SOFT_SPI0_TIMING_DELAY,
|
||||
.bus_name = RT_SOFT_SPI0_BUS_NAME,
|
||||
},
|
||||
#endif /*RT_USING_SOFT_SPI0*/
|
||||
#ifdef RT_USING_SOFT_SPI1
|
||||
{
|
||||
.sck = RT_SOFT_SPI1_SCK_PIN,
|
||||
.miso = RT_SOFT_SPI1_MISO_PIN,
|
||||
.mosi = RT_SOFT_SPI1_MOSI_PIN,
|
||||
.timing_delay = RT_SOFT_SPI1_TIMING_DELAY,
|
||||
.bus_name = RT_SOFT_SPI1_BUS_NAME,
|
||||
},
|
||||
#endif /*RT_USING_SOFT_SPI1*/
|
||||
#ifdef RT_USING_SOFT_SPI2
|
||||
{
|
||||
.sck = RT_SOFT_SPI2_SCK_PIN,
|
||||
.miso = RT_SOFT_SPI2_MISO_PIN,
|
||||
.mosi = RT_SOFT_SPI2_MOSI_PIN,
|
||||
.timing_delay = RT_SOFT_SPI2_TIMING_DELAY,
|
||||
.bus_name = RT_SOFT_SPI2_BUS_NAME,
|
||||
},
|
||||
#endif /*RT_USING_SOFT_SPI2*/
|
||||
#ifdef RT_USING_SOFT_SPI3
|
||||
{
|
||||
.sck = RT_SOFT_SPI3_SCK_PIN,
|
||||
.miso = RT_SOFT_SPI3_MISO_PIN,
|
||||
.mosi = RT_SOFT_SPI3_MOSI_PIN,
|
||||
.timing_delay = RT_SOFT_SPI3_TIMING_DELAY,
|
||||
.bus_name = RT_SOFT_SPI3_BUS_NAME,
|
||||
},
|
||||
#endif /*RT_USING_SOFT_SPI3*/
|
||||
#ifdef RT_USING_SOFT_SPI4
|
||||
{
|
||||
.sck = RT_SOFT_SPI4_SCK_PIN,
|
||||
.miso = RT_SOFT_SPI4_MISO_PIN,
|
||||
.mosi = RT_SOFT_SPI4_MOSI_PIN,
|
||||
.timing_delay = RT_SOFT_SPI4_TIMING_DELAY,
|
||||
.bus_name = RT_SOFT_SPI4_BUS_NAME,
|
||||
},
|
||||
#endif /*RT_USING_SOFT_SPI4*/
|
||||
#ifdef RT_USING_SOFT_SPI5
|
||||
{
|
||||
.sck = RT_SOFT_SPI5_SCK_PIN,
|
||||
.miso = RT_SOFT_SPI5_MISO_PIN,
|
||||
.mosi = RT_SOFT_SPI5_MOSI_PIN,
|
||||
.timing_delay = RT_SOFT_SPI5_TIMING_DELAY,
|
||||
.bus_name = RT_SOFT_SPI5_BUS_NAME,
|
||||
},
|
||||
#endif /*RT_USING_SOFT_SPI5*/
|
||||
#ifdef RT_USING_SOFT_SPI6
|
||||
{
|
||||
.sck = RT_SOFT_SPI6_SCK_PIN,
|
||||
.miso = RT_SOFT_SPI6_MISO_PIN,
|
||||
.mosi = RT_SOFT_SPI6_MOSI_PIN,
|
||||
.timing_delay = RT_SOFT_SPI6_TIMING_DELAY,
|
||||
.bus_name = RT_SOFT_SPI6_BUS_NAME,
|
||||
},
|
||||
#endif /*RT_USING_SOFT_SPI6*/
|
||||
|
||||
};
|
||||
|
||||
static struct rt_soft_spi spi_obj[sizeof(soft_spi_config) / sizeof(soft_spi_config[0])];
|
||||
|
||||
static void spi_soft_pin_init(struct rt_soft_spi * soft_spi)
|
||||
{
|
||||
struct rt_soft_spi_config *cfg = (struct rt_soft_spi_config *)soft_spi->cfg;
|
||||
rt_pin_mode(cfg->sck, PIN_MODE_OUTPUT);
|
||||
rt_pin_mode(cfg->miso, PIN_MODE_INPUT);
|
||||
rt_pin_mode(cfg->mosi, PIN_MODE_OUTPUT);
|
||||
|
||||
rt_pin_write(cfg->miso, PIN_HIGH);
|
||||
rt_pin_write(cfg->sck, PIN_HIGH);
|
||||
rt_pin_write(cfg->mosi, PIN_HIGH);
|
||||
}
|
||||
|
||||
|
||||
static void spi_soft_tog_sclk(void *data)
|
||||
{
|
||||
struct rt_soft_spi_config *cfg = (struct rt_soft_spi_config *)data;
|
||||
if(rt_pin_read(cfg->sck) == PIN_HIGH)
|
||||
{
|
||||
rt_pin_write(cfg->sck, PIN_LOW);
|
||||
}
|
||||
else
|
||||
{
|
||||
rt_pin_write(cfg->sck, PIN_HIGH);
|
||||
}
|
||||
}
|
||||
|
||||
static void spi_soft_set_sclk(void *data, rt_int32_t state)
|
||||
{
|
||||
struct rt_soft_spi_config *cfg = (struct rt_soft_spi_config *)data;
|
||||
if (state)
|
||||
{
|
||||
rt_pin_write(cfg->sck, PIN_HIGH);
|
||||
}
|
||||
else
|
||||
{
|
||||
rt_pin_write(cfg->sck, PIN_LOW);
|
||||
}
|
||||
}
|
||||
|
||||
static void spi_soft_set_mosi(void *data, rt_int32_t state)
|
||||
{
|
||||
struct rt_soft_spi_config *cfg = (struct rt_soft_spi_config *)data;
|
||||
if (state)
|
||||
{
|
||||
rt_pin_write(cfg->mosi, PIN_HIGH);
|
||||
}
|
||||
else
|
||||
{
|
||||
rt_pin_write(cfg->mosi, PIN_LOW);
|
||||
}
|
||||
}
|
||||
|
||||
static void spi_soft_set_miso(void *data, rt_int32_t state)
|
||||
{
|
||||
struct rt_soft_spi_config *cfg = (struct rt_soft_spi_config *)data;
|
||||
if (state)
|
||||
{
|
||||
rt_pin_write(cfg->miso, PIN_HIGH);
|
||||
}
|
||||
else
|
||||
{
|
||||
rt_pin_write(cfg->miso, PIN_LOW);
|
||||
}
|
||||
}
|
||||
|
||||
static rt_int32_t spi_soft_get_sclk(void *data)
|
||||
{
|
||||
struct rt_soft_spi_config *cfg = (struct rt_soft_spi_config *)data;
|
||||
return rt_pin_read(cfg->sck);
|
||||
}
|
||||
|
||||
static rt_int32_t spi_soft_get_mosi(void *data)
|
||||
{
|
||||
struct rt_soft_spi_config *cfg = (struct rt_soft_spi_config *)data;
|
||||
return rt_pin_read(cfg->mosi);
|
||||
}
|
||||
|
||||
static rt_int32_t spi_soft_get_miso(void *data)
|
||||
{
|
||||
struct rt_soft_spi_config *cfg = (struct rt_soft_spi_config *)data;
|
||||
return rt_pin_read(cfg->miso);
|
||||
}
|
||||
|
||||
static void spi_soft_dir_mosi(void *data, rt_int32_t state)
|
||||
{
|
||||
struct rt_soft_spi_config *cfg = (struct rt_soft_spi_config *)data;
|
||||
if (state)
|
||||
{
|
||||
rt_pin_mode(cfg->mosi, PIN_MODE_INPUT);
|
||||
}
|
||||
else
|
||||
{
|
||||
rt_pin_mode(cfg->mosi, PIN_MODE_OUTPUT);
|
||||
}
|
||||
}
|
||||
|
||||
static void spi_soft_dir_miso(void *data, rt_int32_t state)
|
||||
{
|
||||
struct rt_soft_spi_config *cfg = (struct rt_soft_spi_config *)data;
|
||||
if (state)
|
||||
{
|
||||
rt_pin_mode(cfg->miso, PIN_MODE_INPUT);
|
||||
}
|
||||
else
|
||||
{
|
||||
rt_pin_mode(cfg->miso, PIN_MODE_OUTPUT);
|
||||
}
|
||||
}
|
||||
|
||||
static struct rt_spi_bit_ops soft_spi_ops=
|
||||
{
|
||||
.data = RT_NULL,
|
||||
.pin_init = RT_NULL,
|
||||
.tog_sclk = spi_soft_tog_sclk,
|
||||
.set_sclk = spi_soft_set_sclk,
|
||||
.set_mosi = spi_soft_set_mosi,
|
||||
.set_miso = spi_soft_set_miso,
|
||||
.get_sclk = spi_soft_get_sclk,
|
||||
.get_mosi = spi_soft_get_mosi,
|
||||
.get_miso = spi_soft_get_miso,
|
||||
.dir_mosi = spi_soft_dir_mosi,
|
||||
.dir_miso = spi_soft_dir_miso,
|
||||
.udelay = rt_hw_us_delay,
|
||||
};
|
||||
|
||||
/* Soft SPI initialization function */
|
||||
int rt_soft_spi_init(void)
|
||||
{
|
||||
rt_size_t obj_num = sizeof(spi_obj) / sizeof(struct rt_soft_spi);
|
||||
rt_err_t result;
|
||||
|
||||
for (rt_size_t i = 0; i < obj_num; i++)
|
||||
{
|
||||
rt_memcpy(&spi_obj[i].ops, &soft_spi_ops, sizeof(struct rt_spi_bit_ops));
|
||||
spi_obj[i].ops.data = (void *)&soft_spi_config[i];
|
||||
spi_obj[i].spi.ops = &soft_spi_ops;
|
||||
spi_obj[i].cfg = (void *)&soft_spi_config[i];
|
||||
spi_soft_pin_init(&spi_obj[i]);
|
||||
spi_obj[i].spi.ops->delay_us = soft_spi_config[i].timing_delay;
|
||||
result = rt_spi_bit_add_bus(&spi_obj[i].spi, soft_spi_config[i].bus_name, &spi_obj[i].ops);
|
||||
RT_ASSERT(result == RT_EOK);
|
||||
}
|
||||
|
||||
return RT_EOK;
|
||||
}
|
||||
INIT_PREV_EXPORT(rt_soft_spi_init);
|
||||
|
||||
#endif /* RT_USING_SOFT_SPI */
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2024, RT-Thread Development Team
|
||||
* Copyright (c) 2006-2025 RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
@ -87,11 +87,23 @@ rt_inline rt_ssize_t spi_xfer_4line_data8(struct rt_spi_bit_ops *ops,
|
||||
|
||||
TOG_SCLK(ops);
|
||||
|
||||
if (config->mode & RT_SPI_MSB) { rx_data <<= 1; bit = 0x01; }
|
||||
else { rx_data >>= 1; bit = 0x80; }
|
||||
if (config->mode & RT_SPI_MSB)
|
||||
{
|
||||
rx_data <<= 1; bit = 0x01;
|
||||
}
|
||||
else
|
||||
{
|
||||
rx_data >>= 1; bit = 0x80;
|
||||
}
|
||||
|
||||
if (GET_MISO(ops)) { rx_data |= bit; }
|
||||
else { rx_data &= ~bit; }
|
||||
if (GET_MISO(ops))
|
||||
{
|
||||
rx_data |= bit;
|
||||
}
|
||||
else
|
||||
{
|
||||
rx_data &= ~bit;
|
||||
}
|
||||
|
||||
spi_delay2(ops);
|
||||
|
||||
@ -150,11 +162,23 @@ rt_inline rt_ssize_t spi_xfer_4line_data16(struct rt_spi_bit_ops *ops,
|
||||
|
||||
TOG_SCLK(ops);
|
||||
|
||||
if (config->mode & RT_SPI_MSB) { rx_data <<= 1; bit = 0x0001; }
|
||||
else { rx_data >>= 1; bit = 0x8000; }
|
||||
if (config->mode & RT_SPI_MSB)
|
||||
{
|
||||
rx_data <<= 1; bit = 0x0001;
|
||||
}
|
||||
else
|
||||
{
|
||||
rx_data >>= 1; bit = 0x8000;
|
||||
}
|
||||
|
||||
if (GET_MISO(ops)) { rx_data |= bit; }
|
||||
else { rx_data &= ~bit; }
|
||||
if (GET_MISO(ops))
|
||||
{
|
||||
rx_data |= bit;
|
||||
}
|
||||
else
|
||||
{
|
||||
rx_data &= ~bit;
|
||||
}
|
||||
|
||||
spi_delay2(ops);
|
||||
|
||||
@ -244,11 +268,23 @@ rt_inline rt_ssize_t spi_xfer_3line_data8(struct rt_spi_bit_ops *ops,
|
||||
|
||||
TOG_SCLK(ops);
|
||||
|
||||
if (config->mode & RT_SPI_MSB) { rx_data <<= 1; bit = 0x01; }
|
||||
else { rx_data >>= 1; bit = 0x80; }
|
||||
if (config->mode & RT_SPI_MSB)
|
||||
{
|
||||
rx_data <<= 1; bit = 0x01;
|
||||
}
|
||||
else
|
||||
{
|
||||
rx_data >>= 1; bit = 0x80;
|
||||
}
|
||||
|
||||
if (GET_MOSI(ops)) { rx_data |= bit; }
|
||||
else { rx_data &= ~bit; }
|
||||
if (GET_MOSI(ops))
|
||||
{
|
||||
rx_data |= bit;
|
||||
}
|
||||
else
|
||||
{
|
||||
rx_data &= ~bit;
|
||||
}
|
||||
|
||||
spi_delay2(ops);
|
||||
|
||||
@ -345,11 +381,23 @@ rt_inline rt_ssize_t spi_xfer_3line_data16(struct rt_spi_bit_ops *ops,
|
||||
|
||||
TOG_SCLK(ops);
|
||||
|
||||
if (config->mode & RT_SPI_MSB) { rx_data <<= 1; bit = 0x0001; }
|
||||
else { rx_data >>= 1; bit = 0x8000; }
|
||||
if (config->mode & RT_SPI_MSB)
|
||||
{
|
||||
rx_data <<= 1; bit = 0x0001;
|
||||
}
|
||||
else
|
||||
{
|
||||
rx_data >>= 1; bit = 0x8000;
|
||||
}
|
||||
|
||||
if (GET_MOSI(ops)) { rx_data |= bit; }
|
||||
else { rx_data &= ~bit; }
|
||||
if (GET_MOSI(ops))
|
||||
{
|
||||
rx_data |= bit;
|
||||
}
|
||||
else
|
||||
{
|
||||
rx_data &= ~bit;
|
||||
}
|
||||
|
||||
spi_delay2(ops);
|
||||
|
||||
@ -456,15 +504,14 @@ rt_ssize_t spi_bit_xfer(struct rt_spi_device *device, struct rt_spi_message *mes
|
||||
rt_pin_write(cs_pin, PIN_LOW);
|
||||
}
|
||||
spi_delay(ops);
|
||||
}
|
||||
|
||||
/* spi phase */
|
||||
if (config->mode & RT_SPI_CPHA)
|
||||
if ((config->mode & RT_SPI_CPHA))
|
||||
{
|
||||
spi_delay(ops);
|
||||
TOG_SCLK(ops);
|
||||
}
|
||||
}
|
||||
|
||||
if (config->mode & RT_SPI_3WIRE)
|
||||
{
|
||||
if (config->data_width <= 8)
|
||||
@ -487,10 +534,15 @@ rt_ssize_t spi_bit_xfer(struct rt_spi_device *device, struct rt_spi_message *mes
|
||||
length = spi_xfer_4line_data16(ops, config, message->send_buf, message->recv_buf, message->length);
|
||||
}
|
||||
}
|
||||
|
||||
/* release CS */
|
||||
if (message->cs_take && !(device->config.mode & RT_SPI_NO_CS) && (cs_pin != PIN_NONE))
|
||||
if (message->cs_release && !(device->config.mode & RT_SPI_NO_CS) && (cs_pin != PIN_NONE))
|
||||
{
|
||||
|
||||
if ((config->mode & RT_SPI_CPOL) && !GET_SCLK(ops))
|
||||
{
|
||||
spi_delay(ops);
|
||||
TOG_SCLK(ops);
|
||||
}
|
||||
spi_delay(ops);
|
||||
if (device->config.mode & RT_SPI_CS_HIGH)
|
||||
{
|
||||
@ -501,6 +553,7 @@ rt_ssize_t spi_bit_xfer(struct rt_spi_device *device, struct rt_spi_message *mes
|
||||
rt_pin_write(cs_pin, PIN_HIGH);
|
||||
}
|
||||
LOG_I("spi release cs\n");
|
||||
|
||||
}
|
||||
|
||||
return length;
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2023, RT-Thread Development Team
|
||||
* Copyright (c) 2006-2025 RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
@ -140,7 +140,7 @@ rt_err_t rt_spi_bus_configure(struct rt_spi_device *device)
|
||||
result = rt_mutex_take(&(device->bus->lock), RT_WAITING_FOREVER);
|
||||
if (result == RT_EOK)
|
||||
{
|
||||
if (device->bus->owner == device)
|
||||
if (device->bus->owner == RT_NULL || device->bus->owner == device)
|
||||
{
|
||||
/* current device is using, re-configure SPI bus */
|
||||
result = device->bus->ops->configure(device, &device->config);
|
||||
|
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
@ -38,6 +38,8 @@ if RT_USING_CHERRYUSB
|
||||
bool "dwc2_gd"
|
||||
config RT_CHERRYUSB_DEVICE_DWC2_HC
|
||||
bool "dwc2_hc"
|
||||
config RT_CHERRYUSB_DEVICE_DWC2_KENDRYTE
|
||||
bool "dwc2_kendryte"
|
||||
config RT_CHERRYUSB_DEVICE_DWC2_CUSTOM
|
||||
bool "dwc2_custom"
|
||||
config RT_CHERRYUSB_DEVICE_MUSB_ES
|
||||
@ -66,6 +68,8 @@ if RT_USING_CHERRYUSB
|
||||
bool "aic"
|
||||
config RT_CHERRYUSB_DEVICE_PUSB2
|
||||
bool "pusb2"
|
||||
config RT_CHERRYUSB_DEVICE_NRF5X
|
||||
bool "nrf5x"
|
||||
endchoice
|
||||
|
||||
config RT_CHERRYUSB_DEVICE_CDC_ACM
|
||||
@ -182,6 +186,8 @@ if RT_USING_CHERRYUSB
|
||||
bool "dwc2_st"
|
||||
config RT_CHERRYUSB_HOST_DWC2_ESP
|
||||
bool "dwc2_esp"
|
||||
config RT_CHERRYUSB_HOST_DWC2_KENDRYTE
|
||||
bool "dwc2_kendryte"
|
||||
config RT_CHERRYUSB_HOST_DWC2_CUSTOM
|
||||
bool "dwc2_custom"
|
||||
config RT_CHERRYUSB_HOST_MUSB_ES
|
||||
|
@ -1,6 +1,16 @@
|
||||
# CherryUSB
|
||||
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">CherryUSB</h1>
|
||||
<p align="center">
|
||||
<a href="https://github.com/cherry-embedded/CherryUSB/releases"><img src="https://img.shields.io/github/release/cherry-embedded/CherryUSB.svg"><a>
|
||||
<a href="https://github.com/cherry-embedded/CherryUSB/blob/master/LICENSE"><img src="https://img.shields.io/github/license/cherry-embedded/CherryUSB.svg?style=flat-square"></a>
|
||||
<a href="https://github.com/cherry-embedded/CherryUSB/actions/workflows/deploy-docs.yml"><img src="https://github.com/cherry-embedded/CherryUSB/actions/workflows/deploy-docs.yml/badge.svg"> </a>
|
||||
<a href="https://discord.com/invite/wFfvrSAey8"><img src="https://img.shields.io/badge/Discord-blue?logo=discord&style=flat-square"> </a>
|
||||
</p>
|
||||
|
||||
[中文版](./README_zh.md)
|
||||
<p align="center">
|
||||
<a href="./README_zh.md">中文</a>
|
||||
|
|
||||
<a href="./README.md">English</a>
|
||||
</p>
|
||||
|
||||
CherryUSB is a tiny, beautiful and portable USB host and device stack for embedded system with USB IP.
|
||||
|
||||
@ -103,6 +113,7 @@ CherryUSB Host Stack has the following functions:
|
||||
- Support USB Bluetooth class (support nimble and zephyr bluetooth stack, support **CLASS:0xE0** or vendor class like cdc acm)
|
||||
- Support Vendor class (serial, net, wifi)
|
||||
- Support USB modeswitch
|
||||
- Support Android Open Accessory
|
||||
- Support multi host with the same USB IP
|
||||
|
||||
The CherryUSB Host stack also provides the lsusb function, which allows you to view information about all mounted devices, including those on external hubs, with the help of a shell plugin.
|
||||
@ -149,7 +160,7 @@ Only standard and commercial USB IP are listed.
|
||||
|
||||
| IP | device | host | Support status |
|
||||
|:----------------:|:----------:|:--------:|:--------------:|
|
||||
| OHCI(intel) | none | OHCI | × |
|
||||
| OHCI(intel) | none | OHCI | √ |
|
||||
| EHCI(intel) | none | EHCI | √ |
|
||||
| XHCI(intel) | none | XHCI | √ |
|
||||
| UHCI(intel) | none | UHCI | × |
|
||||
@ -186,6 +197,7 @@ USB basic concepts and how the CherryUSB Device stack is implemented, see [Cherr
|
||||
|Artinchip | d12x/d13x/d21x | aic/ehci/ohci |[luban-lite](https://gitee.com/artinchip/luban-lite)|<= latest | Long-term |
|
||||
|Espressif | esp32s2/esp32s3/esp32p4 | dwc2 |[esp32_repo](https://github.com/CherryUSB/cherryusb_esp32)|<= latest | Long-term |
|
||||
|NXP | mcx | kinetis/chipidea/ehci |[nxp_mcx_repo](https://github.com/CherryUSB/cherryusb_mcx)|<= latest | Long-term |
|
||||
|Kendryte | k230 | dwc2 |[k230_repo](https://github.com/CherryUSB/canmv_k230)|v1.2.0 | Long-term |
|
||||
|AllwinnerTech | F1C100S/F1C200S | musb |[cherryusb_rtt_f1c100s](https://github.com/CherryUSB/cherryusb_rtt_f1c100s)|<= latest | the same with musb |
|
||||
|Bekencorp | bk7256/bk7258 | musb |[bk_idk](https://github.com/CherryUSB/bk_idk)| v0.7.0 | the same with musb |
|
||||
|Sophgo | cv18xx | dwc2 |[cvi_alios_open](https://github.com/CherryUSB/cvi_alios_open)| v0.7.0 | TBD |
|
||||
@ -212,4 +224,4 @@ CherryUSB discord: https://discord.com/invite/wFfvrSAey8.
|
||||
|
||||
Thanks to the following companies for their support (in no particular order).
|
||||
|
||||
<img src="docs/assets/bouffalolab.jpg" width="100" height="80"/> <img src="docs/assets/hpmicro.jpg" width="100" height="80" /> <img src="docs/assets/eastsoft.jpg" width="100" height="80" /> <img src="docs/assets/rtthread.jpg" width="100" height="80" /> <img src="docs/assets/sophgo.jpg" width="100" height="80" /> <img src="docs/assets/phytium.jpg" width="100" height="80" /> <img src="docs/assets/thead.jpg" width="100" height="80" /> <img src="docs/assets/nuvoton.jpg" width="100" height="80" /> <img src="docs/assets/artinchip.jpg" width="100" height="80" /> <img src="docs/assets/bekencorp.jpg" width="100" height="80" /> <img src="docs/assets/nxp.png" width="100" height="80" /> <img src="docs/assets/espressif.png" width="100" height="80" />
|
||||
<img src="docs/assets/bouffalolab.jpg" width="100" height="80"/> <img src="docs/assets/hpmicro.jpg" width="100" height="80" /> <img src="docs/assets/eastsoft.jpg" width="100" height="80" /> <img src="docs/assets/rtthread.jpg" width="100" height="80" /> <img src="docs/assets/sophgo.jpg" width="100" height="80" /> <img src="docs/assets/phytium.jpg" width="100" height="80" /> <img src="docs/assets/thead.jpg" width="100" height="80" /> <img src="docs/assets/nuvoton.jpg" width="100" height="80" /> <img src="docs/assets/artinchip.jpg" width="100" height="80" /> <img src="docs/assets/bekencorp.jpg" width="100" height="80" /> <img src="docs/assets/nxp.png" width="100" height="80" /> <img src="docs/assets/espressif.png" width="100" height="80" /> <img src="docs/assets/canaan.jpg" width="100" height="80" />
|
||||
|
@ -1,6 +1,16 @@
|
||||
# CherryUSB
|
||||
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">CherryUSB</h1>
|
||||
<p align="center">
|
||||
<a href="https://github.com/cherry-embedded/CherryUSB/releases"><img src="https://img.shields.io/github/release/cherry-embedded/CherryUSB.svg"><a>
|
||||
<a href="https://github.com/cherry-embedded/CherryUSB/blob/master/LICENSE"><img src="https://img.shields.io/github/license/cherry-embedded/CherryUSB.svg?style=flat-square"></a>
|
||||
<a href="https://github.com/cherry-embedded/CherryUSB/actions/workflows/deploy-docs.yml"><img src="https://github.com/cherry-embedded/CherryUSB/actions/workflows/deploy-docs.yml/badge.svg"> </a>
|
||||
<a href="https://discord.com/invite/wFfvrSAey8"><img src="https://img.shields.io/badge/Discord-blue?logo=discord&style=flat-square"> </a>
|
||||
</p>
|
||||
|
||||
[English](./README.md)
|
||||
<p align="center">
|
||||
<a href="./README_zh.md">中文</a>
|
||||
|
|
||||
<a href="./README.md">English</a>
|
||||
</p>
|
||||
|
||||
CherryUSB 是一个小而美的、可移植性高的、用于嵌入式系统(带 USB IP)的 USB 主从协议栈。
|
||||
|
||||
@ -103,6 +113,7 @@ CherryUSB Host 协议栈当前实现以下功能:
|
||||
- 支持 USB Bluetooth (支持 nimble and zephyr bluetooth 协议栈,支持 **CLASS: 0xE0** 或者厂家自定义类,类似于 cdc acm 功能)
|
||||
- 支持 Vendor 类 class (serial, net, wifi)
|
||||
- 支持 USB modeswitch
|
||||
- 支持 Android Open Accessory
|
||||
- 支持相同 USB IP 的多主机
|
||||
|
||||
同时,CherryUSB Host 协议栈还提供了 lsusb 的功能,借助 shell 插件可以查看所有挂载设备的信息,包括外部 hub 上的设备的信息。
|
||||
@ -149,7 +160,7 @@ x 受以下宏影响:
|
||||
|
||||
| IP | device | host | Support status |
|
||||
|:----------------:|:----------:|:--------:|:--------------:|
|
||||
| OHCI(intel) | none | OHCI | × |
|
||||
| OHCI(intel) | none | OHCI | √ |
|
||||
| EHCI(intel) | none | EHCI | √ |
|
||||
| XHCI(intel) | none | XHCI | √ |
|
||||
| UHCI(intel) | none | UHCI | × |
|
||||
@ -187,6 +198,7 @@ CherryUSB 快速入门、USB 基本概念,API 手册,Class 基本概念和
|
||||
|Artinchip | d12x/d13x/d21x | aic/ehci/ohci |[luban-lite](https://gitee.com/artinchip/luban-lite)|<= latest | Long-term |
|
||||
|Espressif | esp32s2/esp32s3/esp32p4 | dwc2 |[esp32_repo](https://github.com/CherryUSB/cherryusb_esp32)|<= latest | Long-term |
|
||||
|NXP | mcx | kinetis/chipidea/ehci |[nxp_mcx_repo](https://github.com/CherryUSB/cherryusb_mcx)|<= latest | Long-term |
|
||||
|Kendryte | k230 | dwc2 |[k230_repo](https://github.com/CherryUSB/canmv_k230)|v1.2.0 | Long-term |
|
||||
|AllwinnerTech | F1C100S/F1C200S | musb |[cherryusb_rtt_f1c100s](https://github.com/CherryUSB/cherryusb_rtt_f1c100s)|<= latest | the same with musb |
|
||||
|Bekencorp | bk7256/bk7258 | musb |[bk_idk](https://github.com/CherryUSB/bk_idk)| v0.7.0 | the same with musb |
|
||||
|Sophgo | cv18xx | dwc2 |[cvi_alios_open](https://github.com/CherryUSB/cvi_alios_open)| v0.7.0 | TBD |
|
||||
@ -214,4 +226,4 @@ CherryUSB 微信群:与我联系后邀请加入
|
||||
|
||||
感谢以下企业支持(顺序不分先后)。
|
||||
|
||||
<img src="docs/assets/bouffalolab.jpg" width="100" height="80"/> <img src="docs/assets/hpmicro.jpg" width="100" height="80" /> <img src="docs/assets/eastsoft.jpg" width="100" height="80" /> <img src="docs/assets/rtthread.jpg" width="100" height="80" /> <img src="docs/assets/sophgo.jpg" width="100" height="80" /> <img src="docs/assets/phytium.jpg" width="100" height="80" /> <img src="docs/assets/thead.jpg" width="100" height="80" /> <img src="docs/assets/nuvoton.jpg" width="100" height="80" /> <img src="docs/assets/artinchip.jpg" width="100" height="80" /> <img src="docs/assets/bekencorp.jpg" width="100" height="80" /> <img src="docs/assets/nxp.png" width="100" height="80" /> <img src="docs/assets/espressif.png" width="100" height="80" />
|
||||
<img src="docs/assets/bouffalolab.jpg" width="100" height="80"/> <img src="docs/assets/hpmicro.jpg" width="100" height="80" /> <img src="docs/assets/eastsoft.jpg" width="100" height="80" /> <img src="docs/assets/rtthread.jpg" width="100" height="80" /> <img src="docs/assets/sophgo.jpg" width="100" height="80" /> <img src="docs/assets/phytium.jpg" width="100" height="80" /> <img src="docs/assets/thead.jpg" width="100" height="80" /> <img src="docs/assets/nuvoton.jpg" width="100" height="80" /> <img src="docs/assets/artinchip.jpg" width="100" height="80" /> <img src="docs/assets/bekencorp.jpg" width="100" height="80" /> <img src="docs/assets/nxp.png" width="100" height="80" /> <img src="docs/assets/espressif.png" width="100" height="80" /> <img src="docs/assets/canaan.jpg" width="100" height="80" />
|
||||
|
@ -28,6 +28,8 @@ if GetDepend(['RT_CHERRYUSB_DEVICE']):
|
||||
if GetDepend(['RT_CHERRYUSB_DEVICE_SPEED_HS']):
|
||||
CPPDEFINES+=['CONFIG_USB_HS']
|
||||
|
||||
if GetDepend(['RT_CHERRYUSB_DEVICE_NRF5X']):
|
||||
src += Glob('port/nrf5x/usb_dc_nrf5x.c')
|
||||
if GetDepend(['RT_CHERRYUSB_DEVICE_FSDEV']):
|
||||
src += Glob('port/fsdev/usb_dc_fsdev.c')
|
||||
if GetDepend(['RT_CHERRYUSB_DEVICE_DWC2_ST']):
|
||||
@ -45,6 +47,9 @@ if GetDepend(['RT_CHERRYUSB_DEVICE']):
|
||||
if GetDepend(['RT_CHERRYUSB_DEVICE_DWC2_HC']):
|
||||
src += Glob('port/dwc2/usb_dc_dwc2.c')
|
||||
src += Glob('port/dwc2/usb_glue_hc.c')
|
||||
if GetDepend(['RT_CHERRYUSB_DEVICE_DWC2_KENDRYTE']):
|
||||
src += Glob('port/dwc2/usb_dc_dwc2.c')
|
||||
src += Glob('port/dwc2/usb_glue_kendryte.c')
|
||||
if GetDepend(['RT_CHERRYUSB_DEVICE_DWC2_CUSTOM']):
|
||||
src += Glob('port/dwc2/usb_dc_dwc2.c')
|
||||
if GetDepend(['RT_CHERRYUSB_DEVICE_MUSB_ES']):
|
||||
@ -177,6 +182,9 @@ if GetDepend(['RT_CHERRYUSB_HOST']):
|
||||
if GetDepend(['RT_CHERRYUSB_HOST_DWC2_ESP']):
|
||||
src += Glob('port/dwc2/usb_hc_dwc2.c')
|
||||
src += Glob('port/dwc2/usb_glue_esp.c')
|
||||
if GetDepend(['RT_CHERRYUSB_HOST_DWC2_KENDRYTE']):
|
||||
src += Glob('port/dwc2/usb_hc_dwc2.c')
|
||||
src += Glob('port/dwc2/usb_glue_kendryte.c')
|
||||
if GetDepend(['RT_CHERRYUSB_HOST_DWC2_CUSTOM']):
|
||||
src += Glob('port/dwc2/usb_hc_dwc2.c')
|
||||
if GetDepend(['RT_CHERRYUSB_HOST_MUSB_STANDARD']):
|
||||
|
@ -1,5 +1,5 @@
|
||||
VERSION_MAJOR = 1
|
||||
VERSION_MINOR = 4
|
||||
PATCHLEVEL = 1
|
||||
PATCHLEVEL = 2
|
||||
VERSION_TWEAK = 0
|
||||
EXTRAVERSION = 0
|
||||
|
@ -40,6 +40,7 @@ ${CMAKE_CURRENT_LIST_DIR}/class/adb
|
||||
${CMAKE_CURRENT_LIST_DIR}/class/vendor/net
|
||||
${CMAKE_CURRENT_LIST_DIR}/class/vendor/serial
|
||||
${CMAKE_CURRENT_LIST_DIR}/class/vendor/wifi
|
||||
${CMAKE_CURRENT_LIST_DIR}/class/aoa
|
||||
)
|
||||
|
||||
if(CONFIG_CHERRYUSB_DEVICE)
|
||||
@ -218,6 +219,9 @@ if(CONFIG_CHERRYUSB_HOST)
|
||||
if(CONFIG_CHERRYUSB_HOST_BL616)
|
||||
list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/class/vendor/wifi/usbh_bl616.c)
|
||||
endif()
|
||||
if(CONFIG_CHERRYUSB_HOST_AOA)
|
||||
list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/class/aoa/usbh_aoa.c)
|
||||
endif()
|
||||
|
||||
if(DEFINED CONFIG_CHERRYUSB_HOST_HCD)
|
||||
if("${CONFIG_CHERRYUSB_HOST_HCD}" STREQUAL "ehci_bouffalo")
|
||||
@ -294,4 +298,9 @@ endif()
|
||||
if(CONFIG_CHERRYMP)
|
||||
list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/third_party/cherrymp/chry_mempool.c)
|
||||
list(APPEND cherryusb_incs ${CMAKE_CURRENT_LIST_DIR}/third_party/cherrymp)
|
||||
if("${CONFIG_CHERRYUSB_OSAL}" STREQUAL "freertos")
|
||||
list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/third_party/cherrymp/chry_mempool_osal_freertos.c)
|
||||
elseif("${CONFIG_CHERRYUSB_OSAL}" STREQUAL "rtthread")
|
||||
list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/third_party/cherrymp/chry_mempool_osal_rtthread.c)
|
||||
endif()
|
||||
endif()
|
@ -208,6 +208,9 @@
|
||||
#define CONFIG_USBDEV_EP_NUM 8
|
||||
#endif
|
||||
|
||||
/* When your chip hardware supports high-speed and wants to initialize it in high-speed mode, the relevant IP will configure the internal or external high-speed PHY according to CONFIG_USB_HS. */
|
||||
// #define CONFIG_USB_HS
|
||||
|
||||
/* ---------------- FSDEV Configuration ---------------- */
|
||||
//#define CONFIG_USBDEV_FSDEV_PMA_ACCESS 2 // maybe 1 or 2, many chips may have a difference
|
||||
|
||||
|
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (c) 2024, sakumisu
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#ifndef USB_AOA_H
|
||||
#define USB_AOA_H
|
||||
|
||||
//AOA 1.0
|
||||
#define AOA_ACCESSORY_VENDOR_ID 0x18D1
|
||||
#define AOA_ACCESSORY_PRODUCT_ID 0x2D00
|
||||
#define AOA_ACCESSORY_ADB_PRODUCT_ID 0x2D01
|
||||
|
||||
//AOA 2.0
|
||||
#define AOA_AUDIO_PRODUCT_ID 0x2D02
|
||||
#define AOA_AUDIO_ADB_PRODUCT_ID 0x2D03
|
||||
#define AOA_ACCESSORY_AUDIO_PRODUCT_ID 0x2D04
|
||||
#define AOA_ACCESSORY_AUDIO_ADB_PRODUCT_ID 0x2D05
|
||||
|
||||
//AOA 1.0
|
||||
#define AOA_ACCESSORY_GET_PROTOCOL 51
|
||||
#define AOA_ACCESSORY_SEND_STRING 52
|
||||
#define AOA_ACCESSORY_START 53
|
||||
|
||||
//AOA 2.0
|
||||
#define AOA_ACCESSORY_REGISTER_HID 54
|
||||
#define AOA_ACCESSORY_UNREGISTER_HID 55
|
||||
#define AOA_ACCESSORY_SET_HID_REPORT_DESC 56
|
||||
#define AOA_ACCESSORY_SEND_HID_EVENT 57
|
||||
#define AOA_ACCESSORY_SET_AUDIO_MODE 58
|
||||
|
||||
#define AOA_ACCESSORY_STRING_MANUFACTURER 0
|
||||
#define AOA_ACCESSORY_STRING_MODEL 1
|
||||
#define AOA_ACCESSORY_STRING_DESCRIPTION 2
|
||||
#define AOA_ACCESSORY_STRING_VERSION 3
|
||||
#define AOA_ACCESSORY_STRING_URI 4
|
||||
#define AOA_ACCESSORY_STRING_SERIAL 5
|
||||
|
||||
struct aoa_string_info {
|
||||
char acc_manufacturer[64];
|
||||
char acc_model[64];
|
||||
char acc_description[64];
|
||||
char acc_version[64];
|
||||
char acc_uri[64];
|
||||
char acc_serial[64];
|
||||
};
|
||||
|
||||
#endif /* USB_AOA_H */
|
@ -0,0 +1,289 @@
|
||||
/*
|
||||
* Copyright (c) 2024, sakumisu
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include "usbh_core.h"
|
||||
#include "usbh_aoa.h"
|
||||
|
||||
#undef USB_DBG_TAG
|
||||
#define USB_DBG_TAG "usbh_aoa"
|
||||
#include "usb_log.h"
|
||||
|
||||
#define DEV_FORMAT "/dev/aoa"
|
||||
|
||||
USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_aoa_buffer[128];
|
||||
|
||||
static struct usbh_aoa g_aoa_class;
|
||||
|
||||
int usbh_aoa_switch(struct usbh_hubport *hport, struct aoa_string_info *info)
|
||||
{
|
||||
struct usb_setup_packet *setup;
|
||||
int ret;
|
||||
|
||||
setup = hport->setup;
|
||||
|
||||
if (setup == NULL) {
|
||||
return -USB_ERR_INVAL;
|
||||
}
|
||||
|
||||
USB_LOG_INFO("Try switch into aoa mode\r\n");
|
||||
|
||||
setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
|
||||
setup->bRequest = AOA_ACCESSORY_GET_PROTOCOL;
|
||||
setup->wValue = 0;
|
||||
setup->wIndex = 0;
|
||||
setup->wLength = 2;
|
||||
|
||||
ret = usbh_control_transfer(hport, setup, g_aoa_buffer);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
USB_LOG_INFO("AOA version: v%d.%d\r\n", g_aoa_buffer[0], g_aoa_buffer[1]);
|
||||
|
||||
setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
|
||||
setup->bRequest = AOA_ACCESSORY_SEND_STRING;
|
||||
setup->wValue = 0;
|
||||
setup->wIndex = AOA_ACCESSORY_STRING_MANUFACTURER;
|
||||
setup->wLength = strlen(info->acc_manufacturer) + 1;
|
||||
|
||||
memcpy(g_aoa_buffer, info->acc_manufacturer, strlen(info->acc_manufacturer));
|
||||
ret = usbh_control_transfer(hport, setup, g_aoa_buffer);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
|
||||
setup->bRequest = AOA_ACCESSORY_SEND_STRING;
|
||||
setup->wValue = 0;
|
||||
setup->wIndex = AOA_ACCESSORY_STRING_MODEL;
|
||||
setup->wLength = strlen(info->acc_model) + 1;
|
||||
|
||||
memcpy(g_aoa_buffer, info->acc_model, strlen(info->acc_model));
|
||||
ret = usbh_control_transfer(hport, setup, g_aoa_buffer);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
|
||||
setup->bRequest = AOA_ACCESSORY_SEND_STRING;
|
||||
setup->wValue = 0;
|
||||
setup->wIndex = AOA_ACCESSORY_STRING_DESCRIPTION;
|
||||
setup->wLength = strlen(info->acc_description) + 1;
|
||||
|
||||
memcpy(g_aoa_buffer, info->acc_description, strlen(info->acc_description));
|
||||
ret = usbh_control_transfer(hport, setup, g_aoa_buffer);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
|
||||
setup->bRequest = AOA_ACCESSORY_SEND_STRING;
|
||||
setup->wValue = 0;
|
||||
setup->wIndex = AOA_ACCESSORY_STRING_VERSION;
|
||||
setup->wLength = strlen(info->acc_version) + 1;
|
||||
|
||||
memcpy(g_aoa_buffer, info->acc_version, strlen(info->acc_version));
|
||||
ret = usbh_control_transfer(hport, setup, g_aoa_buffer);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
|
||||
setup->bRequest = AOA_ACCESSORY_SEND_STRING;
|
||||
setup->wValue = 0;
|
||||
setup->wIndex = AOA_ACCESSORY_STRING_URI;
|
||||
setup->wLength = strlen(info->acc_uri) + 1;
|
||||
|
||||
memcpy(g_aoa_buffer, info->acc_uri, strlen(info->acc_uri));
|
||||
ret = usbh_control_transfer(hport, setup, g_aoa_buffer);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
|
||||
setup->bRequest = AOA_ACCESSORY_SEND_STRING;
|
||||
setup->wValue = 0;
|
||||
setup->wIndex = AOA_ACCESSORY_STRING_SERIAL;
|
||||
setup->wLength = strlen(info->acc_serial) + 1;
|
||||
|
||||
memcpy(g_aoa_buffer, info->acc_serial, strlen(info->acc_serial));
|
||||
ret = usbh_control_transfer(hport, setup, g_aoa_buffer);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
|
||||
setup->bRequest = AOA_ACCESSORY_START;
|
||||
setup->wValue = 0;
|
||||
setup->wIndex = 0;
|
||||
setup->wLength = 0;
|
||||
|
||||
ret = usbh_control_transfer(hport, setup, NULL);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
USB_LOG_INFO("Switch into aoa mode success, wait usb device restart...\r\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int usbh_aoa_register_hid(struct usbh_aoa *aoa_class, uint16_t id, uint8_t *report, uint32_t report_len)
|
||||
{
|
||||
struct usb_setup_packet *setup;
|
||||
int ret;
|
||||
uint8_t len;
|
||||
uint32_t offset;
|
||||
|
||||
if (!aoa_class || !aoa_class->hport) {
|
||||
return -USB_ERR_INVAL;
|
||||
}
|
||||
setup = aoa_class->hport->setup;
|
||||
|
||||
setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
|
||||
setup->bRequest = AOA_ACCESSORY_REGISTER_HID;
|
||||
setup->wValue = id;
|
||||
setup->wIndex = report_len;
|
||||
setup->wLength = 0;
|
||||
|
||||
ret = usbh_control_transfer(aoa_class->hport, setup, NULL);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
offset = 0;
|
||||
while (report_len > 0) {
|
||||
len = report_len > 64 ? 64 : report_len;
|
||||
|
||||
setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
|
||||
setup->bRequest = AOA_ACCESSORY_SET_HID_REPORT_DESC;
|
||||
setup->wValue = id;
|
||||
setup->wIndex = offset;
|
||||
setup->wLength = len;
|
||||
|
||||
memcpy(g_aoa_buffer, report + offset, len);
|
||||
ret = usbh_control_transfer(aoa_class->hport, setup, g_aoa_buffer);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
offset += len;
|
||||
report_len -= len;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int usbh_aoa_send_hid_event(struct usbh_aoa *aoa_class, uint16_t id, uint8_t *event, uint32_t event_len)
|
||||
{
|
||||
struct usb_setup_packet *setup;
|
||||
int ret;
|
||||
uint8_t len;
|
||||
uint32_t offset;
|
||||
|
||||
if (!aoa_class || !aoa_class->hport) {
|
||||
return -USB_ERR_INVAL;
|
||||
}
|
||||
setup = aoa_class->hport->setup;
|
||||
|
||||
setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
|
||||
setup->bRequest = AOA_ACCESSORY_SEND_HID_EVENT;
|
||||
setup->wValue = id;
|
||||
setup->wIndex = 0;
|
||||
setup->wLength = event_len;
|
||||
|
||||
memcpy(g_aoa_buffer, event, event_len);
|
||||
return usbh_control_transfer(aoa_class->hport, setup, event);
|
||||
}
|
||||
|
||||
static int usbh_aoa_connect(struct usbh_hubport *hport, uint8_t intf)
|
||||
{
|
||||
struct usb_endpoint_descriptor *ep_desc;
|
||||
int ret = 0;
|
||||
|
||||
struct usbh_aoa *aoa_class = &g_aoa_class;
|
||||
|
||||
memset(aoa_class, 0, sizeof(struct usbh_aoa));
|
||||
|
||||
aoa_class->hport = hport;
|
||||
aoa_class->intf = intf;
|
||||
|
||||
hport->config.intf[intf].priv = aoa_class;
|
||||
|
||||
for (uint8_t i = 0; i < hport->config.intf[intf].altsetting[0].intf_desc.bNumEndpoints; i++) {
|
||||
ep_desc = &hport->config.intf[intf].altsetting[0].ep[i].ep_desc;
|
||||
|
||||
if (ep_desc->bEndpointAddress & 0x80) {
|
||||
USBH_EP_INIT(aoa_class->bulkin, ep_desc);
|
||||
} else {
|
||||
USBH_EP_INIT(aoa_class->bulkout, ep_desc);
|
||||
}
|
||||
}
|
||||
|
||||
strncpy(hport->config.intf[intf].devname, DEV_FORMAT, CONFIG_USBHOST_DEV_NAMELEN);
|
||||
|
||||
USB_LOG_INFO("Register AOA Class:%s\r\n", hport->config.intf[intf].devname);
|
||||
|
||||
usbh_aoa_run(aoa_class);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int usbh_aoa_disconnect(struct usbh_hubport *hport, uint8_t intf)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
struct usbh_aoa *aoa_class = (struct usbh_aoa *)hport->config.intf[intf].priv;
|
||||
|
||||
if (aoa_class) {
|
||||
if (aoa_class->bulkin) {
|
||||
usbh_kill_urb(&aoa_class->bulkin_urb);
|
||||
}
|
||||
|
||||
if (aoa_class->bulkout) {
|
||||
usbh_kill_urb(&aoa_class->bulkout_urb);
|
||||
}
|
||||
|
||||
if (hport->config.intf[intf].devname[0] != '\0') {
|
||||
USB_LOG_INFO("Unregister AOA Class:%s\r\n", hport->config.intf[intf].devname);
|
||||
usbh_aoa_stop(aoa_class);
|
||||
}
|
||||
|
||||
memset(aoa_class, 0, sizeof(struct usbh_aoa));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
__WEAK void usbh_aoa_run(struct usbh_aoa *aoa_class)
|
||||
{
|
||||
(void)aoa_class;
|
||||
}
|
||||
|
||||
__WEAK void usbh_aoa_stop(struct usbh_aoa *aoa_class)
|
||||
{
|
||||
(void)aoa_class;
|
||||
}
|
||||
|
||||
static const uint16_t aoa_id_table[][2] = {
|
||||
{ AOA_ACCESSORY_VENDOR_ID, AOA_ACCESSORY_PRODUCT_ID },
|
||||
{ AOA_ACCESSORY_VENDOR_ID, AOA_ACCESSORY_ADB_PRODUCT_ID },
|
||||
{ AOA_ACCESSORY_VENDOR_ID, AOA_AUDIO_PRODUCT_ID },
|
||||
{ AOA_ACCESSORY_VENDOR_ID, AOA_AUDIO_ADB_PRODUCT_ID },
|
||||
{ AOA_ACCESSORY_VENDOR_ID, AOA_ACCESSORY_AUDIO_PRODUCT_ID },
|
||||
{ AOA_ACCESSORY_VENDOR_ID, AOA_ACCESSORY_AUDIO_ADB_PRODUCT_ID },
|
||||
{ 0, 0 },
|
||||
};
|
||||
|
||||
const struct usbh_class_driver aoa_class_driver = {
|
||||
.driver_name = "aoa",
|
||||
.connect = usbh_aoa_connect,
|
||||
.disconnect = usbh_aoa_disconnect
|
||||
};
|
||||
|
||||
CLASS_INFO_DEFINE const struct usbh_class_info aoa_intf_class_info = {
|
||||
.match_flags = USB_CLASS_MATCH_VID_PID | USB_CLASS_MATCH_INTF_CLASS | USB_CLASS_MATCH_INTF_SUBCLASS,
|
||||
.bInterfaceClass = 0xff,
|
||||
.bInterfaceSubClass = 0xff,
|
||||
.bInterfaceProtocol = 0x00,
|
||||
.id_table = aoa_id_table,
|
||||
.class_driver = &aoa_class_driver
|
||||
};
|
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (c) 2024, sakumisu
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#ifndef USBH_AOA_H
|
||||
#define USBH_AOA_H
|
||||
|
||||
#include "usb_aoa.h"
|
||||
|
||||
struct usbh_aoa {
|
||||
struct usbh_hubport *hport;
|
||||
struct usb_endpoint_descriptor *bulkin; /* Bulk IN endpoint */
|
||||
struct usb_endpoint_descriptor *bulkout; /* Bulk OUT endpoint */
|
||||
|
||||
struct usbh_urb bulkout_urb;
|
||||
struct usbh_urb bulkin_urb;
|
||||
|
||||
uint8_t intf;
|
||||
uint8_t minor;
|
||||
|
||||
void *user_data;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int usbh_aoa_switch(struct usbh_hubport *hport, struct aoa_string_info *info);
|
||||
int usbh_aoa_register_hid(struct usbh_aoa *aoa_class, uint16_t id, uint8_t *report, uint32_t report_len);
|
||||
int usbh_aoa_send_hid_event(struct usbh_aoa *aoa_class, uint16_t id, uint8_t *event, uint32_t event_len);
|
||||
|
||||
void usbh_aoa_run(struct usbh_aoa *aoa_class);
|
||||
void usbh_aoa_stop(struct usbh_aoa *aoa_class);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* USBH_AOA_H */
|
@ -648,6 +648,18 @@ struct audio_cs_if_ac_feature_unit_descriptor {
|
||||
|
||||
#define AUDIO_SIZEOF_AC_FEATURE_UNIT_DESC(ch, n) (7 + (ch + 1) * n)
|
||||
|
||||
struct audio_cs_if_ac_selector_unit_descriptor {
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
uint8_t bDescriptorSubtype;
|
||||
uint8_t bUnitID;
|
||||
uint8_t bNrInPins;
|
||||
uint8_t baSourceID[1];
|
||||
uint8_t iSelector;
|
||||
} __PACKED;
|
||||
|
||||
#define AUDIO_SIZEOF_AC_SELECTOR_UNIT_DESC(n) (6 + n)
|
||||
|
||||
struct audio_cs_if_as_general_descriptor {
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
|
@ -136,27 +136,26 @@ static int audio_class_interface_request_handler(uint8_t busid, struct usb_setup
|
||||
memcpy(&volume, *data, *len);
|
||||
if (volume < 0x8000) {
|
||||
volume_db = volume / 256;
|
||||
} else if (volume > 0x8000) {
|
||||
volume_db = (0xffff - volume + 1) / -256;
|
||||
} else {
|
||||
volume_db = (volume - 0x10000) / 256;
|
||||
}
|
||||
volume_db += 128; /* 0 ~ 255 */
|
||||
USB_LOG_DBG("Set ep:0x%02x ch:%d volume:0x%04x\r\n", ep, ch, volume);
|
||||
USB_LOG_DBG("Set ep:0x%02x ch:%d vol_hex:0x%04x, vol_db:%d dB\r\n", ep, ch, volume, volume_db);
|
||||
usbd_audio_set_volume(busid, ep, ch, volume_db);
|
||||
break;
|
||||
case AUDIO_REQUEST_GET_CUR:
|
||||
volume_db = usbd_audio_get_volume(busid, ep, ch);
|
||||
volume_db -= 128;
|
||||
if (volume_db >= 0) {
|
||||
volume = volume_db * 256;
|
||||
} else {
|
||||
volume = volume_db * 256 + 0xffff + 1;
|
||||
volume = volume_db * 256 + 0x10000;
|
||||
}
|
||||
USB_LOG_DBG("Get ep:0x%02x ch:%d vol_hex:0x%04x, vol_db:%d dB\r\n", ep, ch, volume, volume_db);
|
||||
memcpy(*data, &volume, 2);
|
||||
*len = 2;
|
||||
break;
|
||||
case AUDIO_REQUEST_GET_MIN:
|
||||
(*data)[0] = 0x00; /* -2560/256 dB */
|
||||
(*data)[1] = 0xdb;
|
||||
(*data)[0] = 0x00; /* -100 dB */
|
||||
(*data)[1] = 0x9c;
|
||||
*len = 2;
|
||||
break;
|
||||
case AUDIO_REQUEST_GET_MAX:
|
||||
@ -165,7 +164,7 @@ static int audio_class_interface_request_handler(uint8_t busid, struct usb_setup
|
||||
*len = 2;
|
||||
break;
|
||||
case AUDIO_REQUEST_GET_RES:
|
||||
(*data)[0] = 0x00; /* -256/256 dB */
|
||||
(*data)[0] = 0x00; /* 1 dB */
|
||||
(*data)[1] = 0x01;
|
||||
*len = 2;
|
||||
break;
|
||||
@ -178,22 +177,31 @@ static int audio_class_interface_request_handler(uint8_t busid, struct usb_setup
|
||||
case AUDIO_REQUEST_CUR:
|
||||
if (setup->bmRequestType & USB_REQUEST_DIR_MASK) {
|
||||
volume_db = usbd_audio_get_volume(busid, ep, ch);
|
||||
volume = volume_db;
|
||||
if (volume_db >= 0) {
|
||||
volume = volume_db * 256;
|
||||
} else {
|
||||
volume = volume_db * 256 + 0x10000;
|
||||
}
|
||||
USB_LOG_DBG("Get ep:0x%02x ch:%d vol_hex:0x%04x, vol_db:%d dB\r\n", ep, ch, volume, volume_db);
|
||||
memcpy(*data, &volume, 2);
|
||||
*len = 2;
|
||||
} else {
|
||||
memcpy(&volume, *data, *len);
|
||||
volume_db = volume;
|
||||
USB_LOG_DBG("Set ep:0x%02x ch:%d volume:0x%02x\r\n", ep, ch, volume);
|
||||
if (volume < 0x8000) {
|
||||
volume_db = volume / 256;
|
||||
} else {
|
||||
volume_db = (volume - 0x10000) / 256;
|
||||
}
|
||||
USB_LOG_DBG("Set ep:0x%02x ch:%d vol_hex:0x%04x, vol_db:%d dB\r\n", ep, ch, volume, volume_db);
|
||||
usbd_audio_set_volume(busid, ep, ch, volume_db);
|
||||
}
|
||||
break;
|
||||
case AUDIO_REQUEST_RANGE:
|
||||
if (setup->bmRequestType & USB_REQUEST_DIR_MASK) {
|
||||
*((uint16_t *)(*data + 0)) = 1;
|
||||
*((uint16_t *)(*data + 2)) = 0;
|
||||
*((uint16_t *)(*data + 4)) = 100;
|
||||
*((uint16_t *)(*data + 6)) = 1;
|
||||
*((uint16_t *)(*data + 2)) = 0x9c00; /* MIN -100 dB */
|
||||
*((uint16_t *)(*data + 4)) = 0x0000; /* MAX 0 dB */
|
||||
*((uint16_t *)(*data + 6)) = 0x100; /* RES 1 dB */
|
||||
*len = 8;
|
||||
} else {
|
||||
}
|
||||
@ -312,12 +320,12 @@ struct usbd_interface *usbd_audio_init_intf(uint8_t busid,
|
||||
return intf;
|
||||
}
|
||||
|
||||
__WEAK void usbd_audio_set_volume(uint8_t busid, uint8_t ep, uint8_t ch, int volume)
|
||||
__WEAK void usbd_audio_set_volume(uint8_t busid, uint8_t ep, uint8_t ch, int volume_db)
|
||||
{
|
||||
(void)busid;
|
||||
(void)ep;
|
||||
(void)ch;
|
||||
(void)volume;
|
||||
(void)volume_db;
|
||||
}
|
||||
|
||||
__WEAK int usbd_audio_get_volume(uint8_t busid, uint8_t ep, uint8_t ch)
|
||||
|
@ -27,7 +27,7 @@ struct usbd_interface *usbd_audio_init_intf(uint8_t busid, struct usbd_interface
|
||||
void usbd_audio_open(uint8_t busid, uint8_t intf);
|
||||
void usbd_audio_close(uint8_t busid, uint8_t intf);
|
||||
|
||||
void usbd_audio_set_volume(uint8_t busid, uint8_t ep, uint8_t ch, int volume);
|
||||
void usbd_audio_set_volume(uint8_t busid, uint8_t ep, uint8_t ch, int volume_db);
|
||||
int usbd_audio_get_volume(uint8_t busid, uint8_t ep, uint8_t ch);
|
||||
void usbd_audio_set_mute(uint8_t busid, uint8_t ep, uint8_t ch, bool mute);
|
||||
bool usbd_audio_get_mute(uint8_t busid, uint8_t ep, uint8_t ch);
|
||||
|
@ -184,18 +184,21 @@ int usbh_audio_close(struct usbh_audio *audio_class, const char *name)
|
||||
return ret;
|
||||
}
|
||||
|
||||
int usbh_audio_set_volume(struct usbh_audio *audio_class, const char *name, uint8_t ch, uint8_t volume)
|
||||
int usbh_audio_set_volume(struct usbh_audio *audio_class, const char *name, uint8_t ch, int volume_db)
|
||||
{
|
||||
struct usb_setup_packet *setup;
|
||||
int ret;
|
||||
uint8_t feature_id = 0xff;
|
||||
uint8_t intf;
|
||||
uint16_t volume_hex;
|
||||
int volume_min_db;
|
||||
int volume_max_db;
|
||||
|
||||
if (!audio_class || !audio_class->hport) {
|
||||
return -USB_ERR_INVAL;
|
||||
}
|
||||
|
||||
if (volume > 100) {
|
||||
if ((volume_db > 127) || (volume_db < -127)) {
|
||||
return -USB_ERR_INVAL;
|
||||
}
|
||||
|
||||
@ -204,20 +207,102 @@ int usbh_audio_set_volume(struct usbh_audio *audio_class, const char *name, uint
|
||||
for (uint8_t i = 0; i < audio_class->stream_intf_num; i++) {
|
||||
if (strcmp(name, audio_class->as_msg_table[i].stream_name) == 0) {
|
||||
feature_id = audio_class->as_msg_table[i].feature_terminal_id;
|
||||
intf = audio_class->as_msg_table[i].stream_intf;
|
||||
}
|
||||
}
|
||||
|
||||
if (feature_id == 0xff) {
|
||||
return -USB_ERR_NODEV;
|
||||
}
|
||||
|
||||
setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
|
||||
setup->bRequest = AUDIO_REQUEST_GET_CUR;
|
||||
setup->wValue = (AUDIO_FU_CONTROL_VOLUME << 8) | ch;
|
||||
setup->wIndex = (feature_id << 8) | audio_class->ctrl_intf;
|
||||
setup->wLength = 2;
|
||||
|
||||
ret = usbh_control_transfer(audio_class->hport, setup, g_audio_buf);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
memcpy(&audio_class->as_msg_table[intf - audio_class->ctrl_intf - 1].volume_cur, g_audio_buf, 2);
|
||||
|
||||
setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
|
||||
setup->bRequest = AUDIO_REQUEST_GET_MIN;
|
||||
setup->wValue = (AUDIO_FU_CONTROL_VOLUME << 8) | ch;
|
||||
setup->wIndex = (feature_id << 8) | audio_class->ctrl_intf;
|
||||
setup->wLength = 2;
|
||||
|
||||
ret = usbh_control_transfer(audio_class->hport, setup, g_audio_buf);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
memcpy(&audio_class->as_msg_table[intf - audio_class->ctrl_intf - 1].volume_min, g_audio_buf, 2);
|
||||
|
||||
setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
|
||||
setup->bRequest = AUDIO_REQUEST_GET_MAX;
|
||||
setup->wValue = (AUDIO_FU_CONTROL_VOLUME << 8) | ch;
|
||||
setup->wIndex = (feature_id << 8) | audio_class->ctrl_intf;
|
||||
setup->wLength = 2;
|
||||
|
||||
ret = usbh_control_transfer(audio_class->hport, setup, g_audio_buf);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
memcpy(&audio_class->as_msg_table[intf - audio_class->ctrl_intf - 1].volume_max, g_audio_buf, 2);
|
||||
|
||||
setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
|
||||
setup->bRequest = AUDIO_REQUEST_GET_RES;
|
||||
setup->wValue = (AUDIO_FU_CONTROL_VOLUME << 8) | ch;
|
||||
setup->wIndex = (feature_id << 8) | audio_class->ctrl_intf;
|
||||
setup->wLength = 2;
|
||||
|
||||
ret = usbh_control_transfer(audio_class->hport, setup, g_audio_buf);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
memcpy(&audio_class->as_msg_table[intf - audio_class->ctrl_intf - 1].volume_res, g_audio_buf, 2);
|
||||
|
||||
setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
|
||||
setup->bRequest = AUDIO_REQUEST_SET_CUR;
|
||||
setup->wValue = (AUDIO_FU_CONTROL_VOLUME << 8) | ch;
|
||||
setup->wIndex = (feature_id << 8) | audio_class->ctrl_intf;
|
||||
setup->wLength = 2;
|
||||
|
||||
volume_hex = -0xDB00 / 100 * volume + 0xdb00;
|
||||
if (audio_class->as_msg_table[intf - audio_class->ctrl_intf - 1].volume_min < 0x8000) {
|
||||
volume_min_db = audio_class->as_msg_table[intf - audio_class->ctrl_intf - 1].volume_min / 256;
|
||||
} else {
|
||||
volume_min_db = (audio_class->as_msg_table[intf - audio_class->ctrl_intf - 1].volume_min - 0x10000) / 256;
|
||||
}
|
||||
|
||||
if (audio_class->as_msg_table[intf - audio_class->ctrl_intf - 1].volume_max < 0x8000) {
|
||||
volume_max_db = audio_class->as_msg_table[intf - audio_class->ctrl_intf - 1].volume_max / 256;
|
||||
} else {
|
||||
volume_max_db = (audio_class->as_msg_table[intf - audio_class->ctrl_intf - 1].volume_max - 0x10000) / 256;
|
||||
}
|
||||
|
||||
USB_LOG_INFO("Get ch:%d dB range: %d dB ~ %d dB\r\n", volume_min_db, volume_max_db);
|
||||
|
||||
if (volume_db >= 0) {
|
||||
volume_hex = volume_db * 256;
|
||||
if (volume_hex > audio_class->as_msg_table[intf - audio_class->ctrl_intf - 1].volume_max) {
|
||||
return -USB_ERR_RANGE;
|
||||
}
|
||||
} else {
|
||||
volume_hex = volume_db * 256 + 0x10000;
|
||||
if (volume_hex < audio_class->as_msg_table[intf - audio_class->ctrl_intf - 1].volume_min) {
|
||||
return -USB_ERR_RANGE;
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(g_audio_buf, &volume_hex, 2);
|
||||
ret = usbh_control_transfer(audio_class->hport, setup, NULL);
|
||||
|
||||
ret = usbh_control_transfer(audio_class->hport, setup, g_audio_buf);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
audio_class->as_msg_table[intf - audio_class->ctrl_intf - 1].volume_cur = volume_hex;
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -226,6 +311,7 @@ int usbh_audio_set_mute(struct usbh_audio *audio_class, const char *name, uint8_
|
||||
struct usb_setup_packet *setup;
|
||||
int ret;
|
||||
uint8_t feature_id = 0xff;
|
||||
uint8_t intf = 0xff;
|
||||
|
||||
if (!audio_class || !audio_class->hport) {
|
||||
return -USB_ERR_INVAL;
|
||||
@ -235,9 +321,14 @@ int usbh_audio_set_mute(struct usbh_audio *audio_class, const char *name, uint8_
|
||||
for (uint8_t i = 0; i < audio_class->stream_intf_num; i++) {
|
||||
if (strcmp(name, audio_class->as_msg_table[i].stream_name) == 0) {
|
||||
feature_id = audio_class->as_msg_table[i].feature_terminal_id;
|
||||
intf = audio_class->as_msg_table[i].stream_intf;
|
||||
}
|
||||
}
|
||||
|
||||
if (feature_id == 0xff) {
|
||||
return -USB_ERR_NODEV;
|
||||
}
|
||||
|
||||
setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
|
||||
setup->bRequest = AUDIO_REQUEST_SET_CUR;
|
||||
setup->wValue = (AUDIO_FU_CONTROL_MUTE << 8) | ch;
|
||||
@ -246,7 +337,10 @@ int usbh_audio_set_mute(struct usbh_audio *audio_class, const char *name, uint8_
|
||||
|
||||
memcpy(g_audio_buf, &mute, 1);
|
||||
ret = usbh_control_transfer(audio_class->hport, setup, g_audio_buf);
|
||||
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
audio_class->as_msg_table[intf - audio_class->ctrl_intf - 1].mute = mute;
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -286,13 +380,14 @@ void usbh_audio_list_module(struct usbh_audio *audio_class)
|
||||
static int usbh_audio_ctrl_connect(struct usbh_hubport *hport, uint8_t intf)
|
||||
{
|
||||
int ret;
|
||||
uint8_t cur_iface = 0xff;
|
||||
uint8_t cur_iface_count = 0xff;
|
||||
uint8_t cur_alt_setting = 0xff;
|
||||
uint8_t cur_iface = 0;
|
||||
uint8_t cur_iface_count = 0;
|
||||
uint8_t cur_alt_setting = 0;
|
||||
uint8_t input_offset = 0;
|
||||
uint8_t output_offset = 0;
|
||||
uint8_t feature_unit_offset = 0;
|
||||
uint8_t *p;
|
||||
struct usbh_audio_ac_msg ac_msg_table[CONFIG_USBHOST_AUDIO_MAX_STREAMS];
|
||||
|
||||
struct usbh_audio *audio_class = usbh_audio_class_alloc();
|
||||
if (audio_class == NULL) {
|
||||
@ -327,26 +422,24 @@ static int usbh_audio_ctrl_connect(struct usbh_hubport *hport, uint8_t intf)
|
||||
case AUDIO_CONTROL_INPUT_TERMINAL: {
|
||||
struct audio_cs_if_ac_input_terminal_descriptor *desc = (struct audio_cs_if_ac_input_terminal_descriptor *)p;
|
||||
|
||||
memcpy(&audio_class->ac_msg_table[input_offset].ac_input, desc, sizeof(struct audio_cs_if_ac_input_terminal_descriptor));
|
||||
memcpy(&ac_msg_table[input_offset].ac_input, desc, sizeof(struct audio_cs_if_ac_input_terminal_descriptor));
|
||||
input_offset++;
|
||||
} break;
|
||||
case AUDIO_CONTROL_OUTPUT_TERMINAL: {
|
||||
struct audio_cs_if_ac_output_terminal_descriptor *desc = (struct audio_cs_if_ac_output_terminal_descriptor *)p;
|
||||
|
||||
memcpy(&audio_class->ac_msg_table[output_offset].ac_output, desc, sizeof(struct audio_cs_if_ac_output_terminal_descriptor));
|
||||
memcpy(&ac_msg_table[output_offset].ac_output, desc, sizeof(struct audio_cs_if_ac_output_terminal_descriptor));
|
||||
output_offset++;
|
||||
} break;
|
||||
case AUDIO_CONTROL_FEATURE_UNIT: {
|
||||
struct audio_cs_if_ac_feature_unit_descriptor *desc = (struct audio_cs_if_ac_feature_unit_descriptor *)p;
|
||||
|
||||
memcpy(&audio_class->ac_msg_table[feature_unit_offset].ac_feature_unit, desc, desc->bLength);
|
||||
memcpy(&ac_msg_table[feature_unit_offset].ac_feature_unit, desc, desc->bLength);
|
||||
feature_unit_offset++;
|
||||
} break;
|
||||
case AUDIO_CONTROL_PROCESSING_UNIT:
|
||||
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
USB_LOG_ERR("Do not support %02x subtype\r\n", p[DESC_bDescriptorSubType]);
|
||||
return -USB_ERR_NOTSUPP;
|
||||
}
|
||||
} else if ((cur_iface > audio_class->ctrl_intf) && (cur_iface < (audio_class->ctrl_intf + cur_iface_count))) {
|
||||
switch (p[DESC_bDescriptorSubType]) {
|
||||
@ -383,7 +476,12 @@ static int usbh_audio_ctrl_connect(struct usbh_hubport *hport, uint8_t intf)
|
||||
}
|
||||
|
||||
if ((input_offset != output_offset) && (input_offset != feature_unit_offset)) {
|
||||
USB_LOG_ERR("Audio descriptor is invalid\r\n");
|
||||
USB_LOG_ERR("Audio control descriptor is invalid\r\n");
|
||||
return -USB_ERR_INVAL;
|
||||
}
|
||||
|
||||
if (cur_iface_count == 0xff) {
|
||||
USB_LOG_ERR("Audio descriptor must have iad descriptor\r\n");
|
||||
return -USB_ERR_INVAL;
|
||||
}
|
||||
|
||||
@ -392,21 +490,21 @@ static int usbh_audio_ctrl_connect(struct usbh_hubport *hport, uint8_t intf)
|
||||
for (uint8_t i = 0; i < audio_class->stream_intf_num; i++) {
|
||||
/* Search 0x0101 in input or output desc */
|
||||
for (uint8_t streamidx = 0; streamidx < audio_class->stream_intf_num; streamidx++) {
|
||||
if (audio_class->as_msg_table[i].as_general.bTerminalLink == audio_class->ac_msg_table[streamidx].ac_input.bTerminalID) {
|
||||
if (audio_class->as_msg_table[i].as_general.bTerminalLink == ac_msg_table[streamidx].ac_input.bTerminalID) {
|
||||
/* INPUT --> FEATURE UNIT --> OUTPUT */
|
||||
audio_class->as_msg_table[i].input_terminal_id = audio_class->ac_msg_table[streamidx].ac_input.bTerminalID;
|
||||
audio_class->as_msg_table[i].input_terminal_id = ac_msg_table[streamidx].ac_input.bTerminalID;
|
||||
|
||||
/* Search input terminal id in feature desc */
|
||||
for (uint8_t featureidx = 0; featureidx < audio_class->stream_intf_num; featureidx++) {
|
||||
if (audio_class->ac_msg_table[streamidx].ac_input.bTerminalID == audio_class->ac_msg_table[featureidx].ac_feature_unit.bSourceID) {
|
||||
audio_class->as_msg_table[i].feature_terminal_id = audio_class->ac_msg_table[featureidx].ac_feature_unit.bUnitID;
|
||||
if (ac_msg_table[streamidx].ac_input.bTerminalID == ac_msg_table[featureidx].ac_feature_unit.bSourceID) {
|
||||
audio_class->as_msg_table[i].feature_terminal_id = ac_msg_table[featureidx].ac_feature_unit.bUnitID;
|
||||
|
||||
/* Search feature unit id in output desc */
|
||||
for (uint8_t outputid = 0; outputid < audio_class->stream_intf_num; outputid++) {
|
||||
if (audio_class->ac_msg_table[featureidx].ac_feature_unit.bUnitID == audio_class->ac_msg_table[outputid].ac_output.bSourceID) {
|
||||
audio_class->as_msg_table[i].output_terminal_id = audio_class->ac_msg_table[outputid].ac_output.bTerminalID;
|
||||
if (ac_msg_table[featureidx].ac_feature_unit.bUnitID == ac_msg_table[outputid].ac_output.bSourceID) {
|
||||
audio_class->as_msg_table[i].output_terminal_id = ac_msg_table[outputid].ac_output.bTerminalID;
|
||||
|
||||
switch (audio_class->ac_msg_table[outputid].ac_output.wTerminalType) {
|
||||
switch (ac_msg_table[outputid].ac_output.wTerminalType) {
|
||||
case AUDIO_OUTTERM_SPEAKER:
|
||||
audio_class->as_msg_table[i].stream_name = "speaker";
|
||||
break;
|
||||
@ -426,21 +524,21 @@ static int usbh_audio_ctrl_connect(struct usbh_hubport *hport, uint8_t intf)
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (audio_class->as_msg_table[i].as_general.bTerminalLink == audio_class->ac_msg_table[streamidx].ac_output.bTerminalID) {
|
||||
} else if (audio_class->as_msg_table[i].as_general.bTerminalLink == ac_msg_table[streamidx].ac_output.bTerminalID) {
|
||||
/* OUTPUT --> FEATURE UNIT --> INPUT */
|
||||
audio_class->as_msg_table[i].output_terminal_id = audio_class->ac_msg_table[streamidx].ac_output.bTerminalID;
|
||||
audio_class->as_msg_table[i].output_terminal_id = ac_msg_table[streamidx].ac_output.bTerminalID;
|
||||
|
||||
/* Search output terminal id in feature desc */
|
||||
for (uint8_t featureidx = 0; featureidx < audio_class->stream_intf_num; featureidx++) {
|
||||
if (audio_class->ac_msg_table[streamidx].ac_output.bSourceID == audio_class->ac_msg_table[featureidx].ac_feature_unit.bUnitID) {
|
||||
audio_class->as_msg_table[i].feature_terminal_id = audio_class->ac_msg_table[featureidx].ac_feature_unit.bUnitID;
|
||||
if (ac_msg_table[streamidx].ac_output.bSourceID == ac_msg_table[featureidx].ac_feature_unit.bUnitID) {
|
||||
audio_class->as_msg_table[i].feature_terminal_id = ac_msg_table[featureidx].ac_feature_unit.bUnitID;
|
||||
|
||||
/* Search feature unit id in input desc */
|
||||
for (uint8_t inputid = 0; inputid < audio_class->stream_intf_num; inputid++) {
|
||||
if (audio_class->ac_msg_table[featureidx].ac_feature_unit.bSourceID == audio_class->ac_msg_table[inputid].ac_input.bTerminalID) {
|
||||
audio_class->as_msg_table[i].input_terminal_id = audio_class->ac_msg_table[inputid].ac_input.bTerminalID;
|
||||
if (ac_msg_table[featureidx].ac_feature_unit.bSourceID == ac_msg_table[inputid].ac_input.bTerminalID) {
|
||||
audio_class->as_msg_table[i].input_terminal_id = ac_msg_table[inputid].ac_input.bTerminalID;
|
||||
|
||||
switch (audio_class->ac_msg_table[inputid].ac_input.wTerminalType) {
|
||||
switch (ac_msg_table[inputid].ac_input.wTerminalType) {
|
||||
case AUDIO_INTERM_MIC:
|
||||
audio_class->as_msg_table[i].stream_name = "mic";
|
||||
break;
|
||||
@ -458,6 +556,13 @@ static int usbh_audio_ctrl_connect(struct usbh_hubport *hport, uint8_t intf)
|
||||
}
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < audio_class->stream_intf_num; i++) {
|
||||
if (audio_class->as_msg_table[i].stream_name == NULL) {
|
||||
USB_LOG_ERR("Audio stream search fail\r\n");
|
||||
return -USB_ERR_NODEV;
|
||||
}
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < audio_class->stream_intf_num; i++) {
|
||||
ret = usbh_audio_close(audio_class, audio_class->as_msg_table[i].stream_name);
|
||||
if (ret < 0) {
|
||||
@ -537,18 +642,18 @@ const struct usbh_class_driver audio_streaming_class_driver = {
|
||||
|
||||
CLASS_INFO_DEFINE const struct usbh_class_info audio_ctrl_intf_class_info = {
|
||||
.match_flags = USB_CLASS_MATCH_INTF_CLASS | USB_CLASS_MATCH_INTF_SUBCLASS,
|
||||
.class = USB_DEVICE_CLASS_AUDIO,
|
||||
.subclass = AUDIO_SUBCLASS_AUDIOCONTROL,
|
||||
.protocol = 0x00,
|
||||
.bInterfaceClass = USB_DEVICE_CLASS_AUDIO,
|
||||
.bInterfaceSubClass = AUDIO_SUBCLASS_AUDIOCONTROL,
|
||||
.bInterfaceProtocol = 0x00,
|
||||
.id_table = NULL,
|
||||
.class_driver = &audio_ctrl_class_driver
|
||||
};
|
||||
|
||||
CLASS_INFO_DEFINE const struct usbh_class_info audio_streaming_intf_class_info = {
|
||||
.match_flags = USB_CLASS_MATCH_INTF_CLASS | USB_CLASS_MATCH_INTF_SUBCLASS,
|
||||
.class = USB_DEVICE_CLASS_AUDIO,
|
||||
.subclass = AUDIO_SUBCLASS_AUDIOSTREAMING,
|
||||
.protocol = 0x00,
|
||||
.bInterfaceClass = USB_DEVICE_CLASS_AUDIO,
|
||||
.bInterfaceSubClass = AUDIO_SUBCLASS_AUDIOSTREAMING,
|
||||
.bInterfaceProtocol = 0x00,
|
||||
.id_table = NULL,
|
||||
.class_driver = &audio_streaming_class_driver
|
||||
};
|
||||
|
@ -26,6 +26,11 @@ struct usbh_audio_as_msg {
|
||||
uint8_t output_terminal_id;
|
||||
uint8_t ep_attr;
|
||||
uint8_t num_of_altsetting;
|
||||
uint16_t volume_min;
|
||||
uint16_t volume_max;
|
||||
uint16_t volume_res;
|
||||
uint16_t volume_cur;
|
||||
bool mute;
|
||||
struct audio_cs_if_as_general_descriptor as_general;
|
||||
struct audio_cs_if_as_format_type_descriptor as_format[CONFIG_USBHOST_MAX_INTF_ALTSETTINGS];
|
||||
};
|
||||
@ -43,7 +48,6 @@ struct usbh_audio {
|
||||
uint16_t bcdADC;
|
||||
uint8_t bInCollection;
|
||||
uint8_t stream_intf_num;
|
||||
struct usbh_audio_ac_msg ac_msg_table[CONFIG_USBHOST_AUDIO_MAX_STREAMS];
|
||||
struct usbh_audio_as_msg as_msg_table[CONFIG_USBHOST_AUDIO_MAX_STREAMS];
|
||||
|
||||
void *user_data;
|
||||
@ -55,7 +59,7 @@ extern "C" {
|
||||
|
||||
int usbh_audio_open(struct usbh_audio *audio_class, const char *name, uint32_t samp_freq, uint8_t bitresolution);
|
||||
int usbh_audio_close(struct usbh_audio *audio_class, const char *name);
|
||||
int usbh_audio_set_volume(struct usbh_audio *audio_class, const char *name, uint8_t ch, uint8_t volume);
|
||||
int usbh_audio_set_volume(struct usbh_audio *audio_class, const char *name, uint8_t ch, int volume_db);
|
||||
int usbh_audio_set_mute(struct usbh_audio *audio_class, const char *name, uint8_t ch, bool mute);
|
||||
|
||||
void usbh_audio_run(struct usbh_audio *audio_class);
|
||||
|
@ -524,7 +524,7 @@ struct cdc_ncm_ndp16 {
|
||||
int_ep, /* bEndpointAddress */ \
|
||||
0x03, /* bmAttributes */ \
|
||||
0x08, 0x00, /* wMaxPacketSize */ \
|
||||
0x10, /* bInterval */ \
|
||||
0x05, /* bInterval */ \
|
||||
0x09, /* bLength */ \
|
||||
USB_DESCRIPTOR_TYPE_INTERFACE, /* bDescriptorType */ \
|
||||
(uint8_t)(bFirstInterface + 1), /* bInterfaceNumber */ \
|
||||
@ -596,7 +596,7 @@ eth_statistics, wMaxSegmentSize, wNumberMCFilters, bNumberPowerFilters, str_idx)
|
||||
int_ep, /* bEndpointAddress */ \
|
||||
0x03, /* bmAttributes */ \
|
||||
0x10, 0x00, /* wMaxPacketSize */ \
|
||||
0x10, /* bInterval */ \
|
||||
0x05, /* bInterval */ \
|
||||
0x09, /* bLength */ \
|
||||
USB_DESCRIPTOR_TYPE_INTERFACE, /* bDescriptorType */ \
|
||||
(uint8_t)(bFirstInterface + 1), /* bInterfaceNumber */ \
|
||||
|
@ -7,17 +7,21 @@
|
||||
#include "usbd_cdc_ecm.h"
|
||||
|
||||
#define CDC_ECM_OUT_EP_IDX 0
|
||||
#define CDC_ECM_IN_EP_IDX 1
|
||||
#define CDC_ECM_INT_EP_IDX 2
|
||||
#define CDC_ECM_IN_EP_IDX 1
|
||||
#define CDC_ECM_INT_EP_IDX 2
|
||||
|
||||
/* Ethernet Maximum Segment size, typically 1514 bytes */
|
||||
#define CONFIG_CDC_ECM_ETH_MAX_SEGSZE 1536U
|
||||
|
||||
/* Describe EndPoints configuration */
|
||||
static struct usbd_endpoint cdc_ecm_ep_data[3];
|
||||
|
||||
#ifdef CONFIG_USBDEV_CDC_ECM_USING_LWIP
|
||||
static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_cdc_ecm_rx_buffer[CONFIG_CDC_ECM_ETH_MAX_SEGSZE];
|
||||
static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_cdc_ecm_tx_buffer[CONFIG_CDC_ECM_ETH_MAX_SEGSZE];
|
||||
#endif
|
||||
static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_cdc_ecm_notify_buf[16];
|
||||
|
||||
volatile uint8_t *g_cdc_ecm_rx_data_buffer = NULL;
|
||||
volatile uint32_t g_cdc_ecm_rx_data_length = 0;
|
||||
volatile uint32_t g_cdc_ecm_tx_data_length = 0;
|
||||
|
||||
@ -68,8 +72,10 @@ void usbd_cdc_ecm_send_notify(uint8_t notifycode, uint8_t value, uint32_t *speed
|
||||
break;
|
||||
}
|
||||
|
||||
if (bytes2send) {
|
||||
usbd_ep_start_write(0, cdc_ecm_ep_data[CDC_ECM_INT_EP_IDX].ep_addr, g_cdc_ecm_notify_buf, bytes2send);
|
||||
if (usb_device_is_configured(0)) {
|
||||
if (bytes2send) {
|
||||
usbd_ep_start_write(0, cdc_ecm_ep_data[CDC_ECM_INT_EP_IDX].ep_addr, g_cdc_ecm_notify_buf, bytes2send);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -93,11 +99,11 @@ static int cdc_ecm_class_interface_request_handler(uint8_t busid, struct usb_set
|
||||
* bit3 Broadcast
|
||||
* bit4 Multicast
|
||||
*/
|
||||
if (g_current_net_status == 0) {
|
||||
g_current_net_status = 1;
|
||||
usbd_cdc_ecm_send_notify(CDC_ECM_NOTIFY_CODE_NETWORK_CONNECTION, CDC_ECM_NET_CONNECTED, NULL);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USBDEV_CDC_ECM_USING_LWIP
|
||||
g_connect_speed_table[0] = 100000000; /* 100 Mbps */
|
||||
g_connect_speed_table[1] = 100000000; /* 100 Mbps */
|
||||
usbd_cdc_ecm_set_connect(true, g_connect_speed_table);
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
USB_LOG_WRN("Unhandled CDC ECM Class bRequest 0x%02x\r\n", setup->bRequest);
|
||||
@ -117,10 +123,11 @@ void cdc_ecm_notify_handler(uint8_t busid, uint8_t event, void *arg)
|
||||
g_current_net_status = 0;
|
||||
g_cdc_ecm_rx_data_length = 0;
|
||||
g_cdc_ecm_tx_data_length = 0;
|
||||
g_cdc_ecm_rx_data_buffer = NULL;
|
||||
break;
|
||||
case USBD_EVENT_CONFIGURED:
|
||||
usbd_ep_start_read(0, cdc_ecm_ep_data[CDC_ECM_OUT_EP_IDX].ep_addr, &g_cdc_ecm_rx_buffer[g_cdc_ecm_rx_data_length], usbd_get_ep_mps(busid, cdc_ecm_ep_data[CDC_ECM_OUT_EP_IDX].ep_addr));
|
||||
#ifdef CONFIG_USBDEV_CDC_ECM_USING_LWIP
|
||||
usbd_cdc_ecm_start_read(g_cdc_ecm_rx_buffer, CONFIG_CDC_ECM_ETH_MAX_SEGSZE);
|
||||
#endif
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -132,14 +139,8 @@ void cdc_ecm_bulk_out(uint8_t busid, uint8_t ep, uint32_t nbytes)
|
||||
{
|
||||
(void)busid;
|
||||
|
||||
g_cdc_ecm_rx_data_length += nbytes;
|
||||
|
||||
if (nbytes < usbd_get_ep_mps(0, ep)) {
|
||||
g_cdc_ecm_rx_data_buffer = g_cdc_ecm_rx_buffer;
|
||||
usbd_cdc_ecm_data_recv_done(g_cdc_ecm_rx_buffer, g_cdc_ecm_rx_data_length);
|
||||
} else {
|
||||
usbd_ep_start_read(0, ep, &g_cdc_ecm_rx_buffer[g_cdc_ecm_rx_data_length], usbd_get_ep_mps(0, ep));
|
||||
}
|
||||
g_cdc_ecm_rx_data_length = nbytes;
|
||||
usbd_cdc_ecm_data_recv_done(g_cdc_ecm_rx_data_length);
|
||||
}
|
||||
|
||||
void cdc_ecm_bulk_in(uint8_t busid, uint8_t ep, uint32_t nbytes)
|
||||
@ -150,6 +151,7 @@ void cdc_ecm_bulk_in(uint8_t busid, uint8_t ep, uint32_t nbytes)
|
||||
/* send zlp */
|
||||
usbd_ep_start_write(0, ep, NULL, 0);
|
||||
} else {
|
||||
usbd_cdc_ecm_data_send_done(g_cdc_ecm_tx_data_length);
|
||||
g_cdc_ecm_tx_data_length = 0;
|
||||
}
|
||||
}
|
||||
@ -160,14 +162,20 @@ void cdc_ecm_int_in(uint8_t busid, uint8_t ep, uint32_t nbytes)
|
||||
(void)ep;
|
||||
(void)nbytes;
|
||||
|
||||
if (g_current_net_status == 1) {
|
||||
g_current_net_status = 2;
|
||||
usbd_cdc_ecm_send_notify(CDC_ECM_NOTIFY_CODE_NETWORK_CONNECTION, CDC_ECM_NET_CONNECTED, g_connect_speed_table);
|
||||
if (g_current_net_status == 2) {
|
||||
g_current_net_status = 3;
|
||||
usbd_cdc_ecm_send_notify(CDC_ECM_NOTIFY_CODE_CONNECTION_SPEED_CHANGE, 0, g_connect_speed_table);
|
||||
} else {
|
||||
g_current_net_status = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int usbd_cdc_ecm_start_write(uint8_t *buf, uint32_t len)
|
||||
{
|
||||
if (!usb_device_is_configured(0)) {
|
||||
return -USB_ERR_NODEV;
|
||||
}
|
||||
|
||||
if (g_cdc_ecm_tx_data_length > 0) {
|
||||
return -USB_ERR_BUSY;
|
||||
}
|
||||
@ -175,14 +183,17 @@ int usbd_cdc_ecm_start_write(uint8_t *buf, uint32_t len)
|
||||
g_cdc_ecm_tx_data_length = len;
|
||||
|
||||
USB_LOG_DBG("txlen:%d\r\n", g_cdc_ecm_tx_data_length);
|
||||
return usbd_ep_start_write(0, cdc_ecm_ep_data[CDC_ECM_IN_EP_IDX].ep_addr, buf, g_cdc_ecm_tx_data_length);
|
||||
return usbd_ep_start_write(0, cdc_ecm_ep_data[CDC_ECM_IN_EP_IDX].ep_addr, buf, len);
|
||||
}
|
||||
|
||||
void usbd_cdc_ecm_start_read_next(void)
|
||||
int usbd_cdc_ecm_start_read(uint8_t *buf, uint32_t len)
|
||||
{
|
||||
if (!usb_device_is_configured(0)) {
|
||||
return -USB_ERR_NODEV;
|
||||
}
|
||||
|
||||
g_cdc_ecm_rx_data_length = 0;
|
||||
g_cdc_ecm_rx_data_buffer = NULL;
|
||||
usbd_ep_start_read(0, cdc_ecm_ep_data[CDC_ECM_OUT_EP_IDX].ep_addr, g_cdc_ecm_rx_buffer, usbd_get_ep_mps(0, cdc_ecm_ep_data[CDC_ECM_OUT_EP_IDX].ep_addr));
|
||||
return usbd_ep_start_read(0, cdc_ecm_ep_data[CDC_ECM_OUT_EP_IDX].ep_addr, buf, len);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USBDEV_CDC_ECM_USING_LWIP
|
||||
@ -190,19 +201,19 @@ struct pbuf *usbd_cdc_ecm_eth_rx(void)
|
||||
{
|
||||
struct pbuf *p;
|
||||
|
||||
if (g_cdc_ecm_rx_data_buffer == NULL) {
|
||||
if (g_cdc_ecm_rx_data_length == 0) {
|
||||
return NULL;
|
||||
}
|
||||
p = pbuf_alloc(PBUF_RAW, g_cdc_ecm_rx_data_length, PBUF_POOL);
|
||||
if (p == NULL) {
|
||||
usbd_cdc_ecm_start_read_next();
|
||||
usbd_cdc_ecm_start_read(g_cdc_ecm_rx_buffer, CONFIG_CDC_ECM_ETH_MAX_SEGSZE);
|
||||
return NULL;
|
||||
}
|
||||
usb_memcpy(p->payload, (uint8_t *)g_cdc_ecm_rx_buffer, g_cdc_ecm_rx_data_length);
|
||||
p->len = g_cdc_ecm_rx_data_length;
|
||||
|
||||
USB_LOG_DBG("rxlen:%d\r\n", g_cdc_ecm_rx_data_length);
|
||||
usbd_cdc_ecm_start_read_next();
|
||||
usbd_cdc_ecm_start_read(g_cdc_ecm_rx_buffer, CONFIG_CDC_ECM_ETH_MAX_SEGSZE);
|
||||
return p;
|
||||
}
|
||||
|
||||
@ -250,13 +261,24 @@ struct usbd_interface *usbd_cdc_ecm_init_intf(struct usbd_interface *intf, const
|
||||
return intf;
|
||||
}
|
||||
|
||||
void usbd_cdc_ecm_set_connect_speed(uint32_t speed[2])
|
||||
void usbd_cdc_ecm_set_connect(bool connect, uint32_t speed[2])
|
||||
{
|
||||
memcpy(g_connect_speed_table, speed, 8);
|
||||
if (connect) {
|
||||
g_current_net_status = 2;
|
||||
memcpy(g_connect_speed_table, speed, 8);
|
||||
usbd_cdc_ecm_send_notify(CDC_ECM_NOTIFY_CODE_NETWORK_CONNECTION, CDC_ECM_NET_CONNECTED, NULL);
|
||||
} else {
|
||||
g_current_net_status = 1;
|
||||
usbd_cdc_ecm_send_notify(CDC_ECM_NOTIFY_CODE_NETWORK_CONNECTION, CDC_ECM_NET_DISCONNECTED, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
__WEAK void usbd_cdc_ecm_data_recv_done(uint8_t *buf, uint32_t len)
|
||||
__WEAK void usbd_cdc_ecm_data_recv_done(uint32_t len)
|
||||
{
|
||||
(void)len;
|
||||
}
|
||||
|
||||
__WEAK void usbd_cdc_ecm_data_send_done(uint32_t len)
|
||||
{
|
||||
(void)buf;
|
||||
(void)len;
|
||||
}
|
||||
|
@ -12,21 +12,15 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Ethernet Maximum Segment size, typically 1514 bytes */
|
||||
#define CONFIG_CDC_ECM_ETH_MAX_SEGSZE 1514U
|
||||
|
||||
/* Init cdc ecm interface driver */
|
||||
struct usbd_interface *usbd_cdc_ecm_init_intf(struct usbd_interface *intf, const uint8_t int_ep, const uint8_t out_ep, const uint8_t in_ep);
|
||||
|
||||
/* Setup request command callback api */
|
||||
void usbd_cdc_ecm_set_connect_speed(uint32_t speed[2]);
|
||||
void usbd_cdc_ecm_set_connect(bool connect, uint32_t speed[2]);
|
||||
|
||||
/* Api for eth only without any net stack */
|
||||
uint8_t *usbd_cdc_ecm_get_tx_buffer(void);
|
||||
void usbd_cdc_ecm_send_done(void);
|
||||
void usbd_cdc_ecm_data_recv_done(uint32_t len);
|
||||
void usbd_cdc_ecm_data_send_done(uint32_t len);
|
||||
int usbd_cdc_ecm_start_write(uint8_t *buf, uint32_t len);
|
||||
void usbd_cdc_ecm_data_recv_done(uint8_t *buf, uint32_t len);
|
||||
void usbd_cdc_ecm_start_read_next(void);
|
||||
int usbd_cdc_ecm_start_read(uint8_t *buf, uint32_t len);
|
||||
|
||||
#ifdef CONFIG_USBDEV_CDC_ECM_USING_LWIP
|
||||
#include "lwip/netif.h"
|
||||
|
@ -267,18 +267,18 @@ const struct usbh_class_driver cdc_data_class_driver = {
|
||||
|
||||
CLASS_INFO_DEFINE const struct usbh_class_info cdc_acm_class_info = {
|
||||
.match_flags = USB_CLASS_MATCH_INTF_CLASS | USB_CLASS_MATCH_INTF_SUBCLASS,
|
||||
.class = USB_DEVICE_CLASS_CDC,
|
||||
.subclass = CDC_ABSTRACT_CONTROL_MODEL,
|
||||
.protocol = 0x00,
|
||||
.bInterfaceClass = USB_DEVICE_CLASS_CDC,
|
||||
.bInterfaceSubClass = CDC_ABSTRACT_CONTROL_MODEL,
|
||||
.bInterfaceProtocol = 0x00,
|
||||
.id_table = NULL,
|
||||
.class_driver = &cdc_acm_class_driver
|
||||
};
|
||||
|
||||
CLASS_INFO_DEFINE const struct usbh_class_info cdc_data_class_info = {
|
||||
.match_flags = USB_CLASS_MATCH_INTF_CLASS,
|
||||
.class = USB_DEVICE_CLASS_CDC_DATA,
|
||||
.subclass = 0x00,
|
||||
.protocol = 0x00,
|
||||
.bInterfaceClass = USB_DEVICE_CLASS_CDC_DATA,
|
||||
.bInterfaceSubClass = 0x00,
|
||||
.bInterfaceProtocol = 0x00,
|
||||
.id_table = NULL,
|
||||
.class_driver = &cdc_data_class_driver
|
||||
};
|
||||
|
@ -323,9 +323,9 @@ const struct usbh_class_driver cdc_ecm_class_driver = {
|
||||
|
||||
CLASS_INFO_DEFINE const struct usbh_class_info cdc_ecm_class_info = {
|
||||
.match_flags = USB_CLASS_MATCH_INTF_CLASS | USB_CLASS_MATCH_INTF_SUBCLASS | USB_CLASS_MATCH_INTF_PROTOCOL,
|
||||
.class = USB_DEVICE_CLASS_CDC,
|
||||
.subclass = CDC_ETHERNET_NETWORKING_CONTROL_MODEL,
|
||||
.protocol = CDC_COMMON_PROTOCOL_NONE,
|
||||
.bInterfaceClass = USB_DEVICE_CLASS_CDC,
|
||||
.bInterfaceSubClass = CDC_ETHERNET_NETWORKING_CONTROL_MODEL,
|
||||
.bInterfaceProtocol = CDC_COMMON_PROTOCOL_NONE,
|
||||
.id_table = NULL,
|
||||
.class_driver = &cdc_ecm_class_driver
|
||||
};
|
@ -403,9 +403,9 @@ const struct usbh_class_driver cdc_ncm_class_driver = {
|
||||
|
||||
CLASS_INFO_DEFINE const struct usbh_class_info cdc_ncm_class_info = {
|
||||
.match_flags = USB_CLASS_MATCH_INTF_CLASS | USB_CLASS_MATCH_INTF_SUBCLASS | USB_CLASS_MATCH_INTF_PROTOCOL,
|
||||
.class = USB_DEVICE_CLASS_CDC,
|
||||
.subclass = CDC_NETWORK_CONTROL_MODEL,
|
||||
.protocol = CDC_COMMON_PROTOCOL_NONE,
|
||||
.bInterfaceClass = USB_DEVICE_CLASS_CDC,
|
||||
.bInterfaceSubClass = CDC_NETWORK_CONTROL_MODEL,
|
||||
.bInterfaceProtocol = CDC_COMMON_PROTOCOL_NONE,
|
||||
.id_table = NULL,
|
||||
.class_driver = &cdc_ncm_class_driver
|
||||
};
|
||||
|
@ -303,9 +303,9 @@ const struct usbh_class_driver hid_class_driver = {
|
||||
|
||||
CLASS_INFO_DEFINE const struct usbh_class_info hid_custom_class_info = {
|
||||
.match_flags = USB_CLASS_MATCH_INTF_CLASS,
|
||||
.class = USB_DEVICE_CLASS_HID,
|
||||
.subclass = 0x00,
|
||||
.protocol = 0x00,
|
||||
.bInterfaceClass = USB_DEVICE_CLASS_HID,
|
||||
.bInterfaceSubClass = 0x00,
|
||||
.bInterfaceProtocol = 0x00,
|
||||
.id_table = NULL,
|
||||
.class_driver = &hid_class_driver
|
||||
};
|
||||
|
@ -734,9 +734,9 @@ const struct usbh_class_driver hub_class_driver = {
|
||||
|
||||
CLASS_INFO_DEFINE const struct usbh_class_info hub_class_info = {
|
||||
.match_flags = USB_CLASS_MATCH_INTF_CLASS,
|
||||
.class = USB_DEVICE_CLASS_HUB,
|
||||
.subclass = 0,
|
||||
.protocol = 0,
|
||||
.bInterfaceClass = USB_DEVICE_CLASS_HUB,
|
||||
.bInterfaceSubClass = 0,
|
||||
.bInterfaceProtocol = 0,
|
||||
.id_table = NULL,
|
||||
.class_driver = &hub_class_driver
|
||||
};
|
||||
|
@ -443,9 +443,9 @@ const struct usbh_class_driver msc_class_driver = {
|
||||
|
||||
CLASS_INFO_DEFINE const struct usbh_class_info msc_class_info = {
|
||||
.match_flags = USB_CLASS_MATCH_INTF_CLASS | USB_CLASS_MATCH_INTF_SUBCLASS | USB_CLASS_MATCH_INTF_PROTOCOL,
|
||||
.class = USB_DEVICE_CLASS_MASS_STORAGE,
|
||||
.subclass = MSC_SUBCLASS_SCSI,
|
||||
.protocol = MSC_PROTOCOL_BULK_ONLY,
|
||||
.bInterfaceClass = USB_DEVICE_CLASS_MASS_STORAGE,
|
||||
.bInterfaceSubClass = MSC_SUBCLASS_SCSI,
|
||||
.bInterfaceProtocol = MSC_PROTOCOL_BULK_ONLY,
|
||||
.id_table = NULL,
|
||||
.class_driver = &msc_class_driver
|
||||
};
|
||||
|
@ -89,9 +89,9 @@ static const struct usbh_class_driver xxx_class_driver = {
|
||||
|
||||
CLASS_INFO_DEFINE const struct usbh_class_info xxx_class_info = {
|
||||
.match_flags = USB_CLASS_MATCH_INTF_CLASS | USB_CLASS_MATCH_INTF_SUBCLASS | USB_CLASS_MATCH_INTF_PROTOCOL,
|
||||
.class = 0,
|
||||
.subclass = 0,
|
||||
.protocol = 0,
|
||||
.bInterfaceClass = 0,
|
||||
.bInterfaceSubClass = 0,
|
||||
.bInterfaceProtocol = 0,
|
||||
.id_table = NULL,
|
||||
.class_driver = &xxx_class_driver
|
||||
};
|
||||
|
@ -817,9 +817,9 @@ static const struct usbh_class_driver asix_class_driver = {
|
||||
|
||||
CLASS_INFO_DEFINE const struct usbh_class_info asix_class_info = {
|
||||
.match_flags = USB_CLASS_MATCH_VID_PID | USB_CLASS_MATCH_INTF_CLASS,
|
||||
.class = 0xff,
|
||||
.subclass = 0x00,
|
||||
.protocol = 0x00,
|
||||
.bInterfaceClass = 0xff,
|
||||
.bInterfaceSubClass = 0x00,
|
||||
.bInterfaceProtocol = 0x00,
|
||||
.id_table = asix_id_table,
|
||||
.class_driver = &asix_class_driver
|
||||
};
|
@ -2272,9 +2272,9 @@ static const struct usbh_class_driver rtl8152_class_driver = {
|
||||
|
||||
CLASS_INFO_DEFINE const struct usbh_class_info rtl8152_class_info = {
|
||||
.match_flags = USB_CLASS_MATCH_VID_PID | USB_CLASS_MATCH_INTF_CLASS,
|
||||
.class = 0xff,
|
||||
.subclass = 0x00,
|
||||
.protocol = 0x00,
|
||||
.bInterfaceClass = 0xff,
|
||||
.bInterfaceSubClass = 0x00,
|
||||
.bInterfaceProtocol = 0x00,
|
||||
.id_table = rtl_id_table,
|
||||
.class_driver = &rtl8152_class_driver
|
||||
};
|
||||
|
@ -370,9 +370,9 @@ const struct usbh_class_driver ch34x_class_driver = {
|
||||
|
||||
CLASS_INFO_DEFINE const struct usbh_class_info ch34x_class_info = {
|
||||
.match_flags = USB_CLASS_MATCH_VID_PID | USB_CLASS_MATCH_INTF_CLASS,
|
||||
.class = 0xff,
|
||||
.subclass = 0x00,
|
||||
.protocol = 0x00,
|
||||
.bInterfaceClass = 0xff,
|
||||
.bInterfaceSubClass = 0x00,
|
||||
.bInterfaceProtocol = 0x00,
|
||||
.id_table = ch34x_id_table,
|
||||
.class_driver = &ch34x_class_driver
|
||||
};
|
@ -319,9 +319,9 @@ const struct usbh_class_driver cp210x_class_driver = {
|
||||
|
||||
CLASS_INFO_DEFINE const struct usbh_class_info cp210x_class_info = {
|
||||
.match_flags = USB_CLASS_MATCH_VID_PID | USB_CLASS_MATCH_INTF_CLASS,
|
||||
.class = 0xff,
|
||||
.subclass = 0x00,
|
||||
.protocol = 0x00,
|
||||
.bInterfaceClass = 0xff,
|
||||
.bInterfaceSubClass = 0x00,
|
||||
.bInterfaceProtocol = 0x00,
|
||||
.id_table = cp210x_id_table,
|
||||
.class_driver = &cp210x_class_driver
|
||||
};
|
@ -392,9 +392,9 @@ const struct usbh_class_driver ftdi_class_driver = {
|
||||
|
||||
CLASS_INFO_DEFINE const struct usbh_class_info ftdi_class_info = {
|
||||
.match_flags = USB_CLASS_MATCH_VID_PID | USB_CLASS_MATCH_INTF_CLASS,
|
||||
.class = 0xff,
|
||||
.subclass = 0x00,
|
||||
.protocol = 0x00,
|
||||
.bInterfaceClass = 0xff,
|
||||
.bInterfaceSubClass = 0x00,
|
||||
.bInterfaceProtocol = 0x00,
|
||||
.id_table = ftdi_id_table,
|
||||
.class_driver = &ftdi_class_driver
|
||||
};
|
@ -440,9 +440,9 @@ const struct usbh_class_driver pl2303_class_driver = {
|
||||
|
||||
CLASS_INFO_DEFINE const struct usbh_class_info pl2303_class_info = {
|
||||
.match_flags = USB_CLASS_MATCH_VID_PID | USB_CLASS_MATCH_INTF_CLASS,
|
||||
.class = 0xff,
|
||||
.subclass = 0x00,
|
||||
.protocol = 0x00,
|
||||
.bInterfaceClass = 0xff,
|
||||
.bInterfaceSubClass = 0x00,
|
||||
.bInterfaceProtocol = 0x00,
|
||||
.id_table = pl2303_id_table,
|
||||
.class_driver = &pl2303_class_driver
|
||||
};
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user