rtt更新

This commit is contained in:
2025-01-18 13:25:25 +08:00
parent c6a7554b51
commit d6009a0773
726 changed files with 103376 additions and 6270 deletions

View File

@@ -162,20 +162,32 @@ endif
bool "Using devfs for device objects"
default y
config RT_USING_DFS_ROMFS
if RT_USING_DFS_V1
config RT_USING_DFS_ISO9660
bool "Using ISO9660 filesystem"
depends on RT_USING_MEMHEAP
default n
endif
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

View File

@@ -1,4 +1,6 @@
from building import *
from gcc import *
import rtconfig
import os
# The set of source files associated with this SConscript file.
@@ -6,6 +8,7 @@ src = []
cwd = GetCurrentDir()
CPPPATH = [cwd + "/include"]
group = []
LOCAL_CFLAGS = ''
if GetDepend('RT_USING_DFS') and not GetDepend('RT_USING_DFS_V2'):
src = ['src/dfs.c', 'src/dfs_file.c', 'src/dfs_fs.c']
@@ -13,7 +16,12 @@ if GetDepend('RT_USING_DFS') and not GetDepend('RT_USING_DFS_V2'):
if GetDepend('DFS_USING_POSIX'):
src += ['src/dfs_posix.c']
group = DefineGroup('Filesystem', src, depend = ['RT_USING_DFS'], CPPPATH = CPPPATH)
if rtconfig.PLATFORM in GetGCCLikePLATFORM():
LOCAL_CFLAGS += ' -std=c99'
elif rtconfig.PLATFORM in ['armcc']:
LOCAL_CFLAGS += ' --c99'
group = DefineGroup('Filesystem', src, depend = ['RT_USING_DFS'], CPPPATH = CPPPATH, LOCAL_CFLAGS = LOCAL_CFLAGS)
# search in the file system implementation
list = os.listdir(cwd)

View File

@@ -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_ISO9660'], CPPPATH = CPPPATH)
Return('group')

View File

@@ -0,0 +1,698 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-02-25 GuEe-GUI the first version
*/
#include <rthw.h>
#include <rtthread.h>
#include <rtservice.h>
#define DBG_TAG "dfs.iso9660"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
#include "dfs_iso9660.h"
#include <dfs.h>
#include <dfs_fs.h>
#include <dfs_file.h>
#include <posix/string.h>
#include <drivers/misc.h>
#include <drivers/byteorder.h>
#include <sys/time.h>
#define ISO9660_FSTYPE_DIR 0040000
#define ISO9660_FSTYPE_REG 0100000
#define ISO9660_FSTYPE_SYMLINK 0120000
#define ISO9660_FSTYPE_MASK 0170000
#define ISO9660_BLKSZ 2048
#define ISO9660_VOLDESC_BOOT 0
#define ISO9660_VOLDESC_PRIMARY 1
#define ISO9660_VOLDESC_SUPP 2
#define ISO9660_VOLDESC_PART 3
#define ISO9660_VOLDESC_END 255
rt_packed(struct iso9660_voldesc
{
rt_uint8_t type;
rt_uint8_t magic[5];
rt_uint8_t version;
});
rt_packed(struct iso9660_date2
{
rt_uint8_t year;
rt_uint8_t month;
rt_uint8_t day;
rt_uint8_t hour;
rt_uint8_t minute;
rt_uint8_t second;
rt_uint8_t offset;
});
/* Directory entry */
rt_packed(struct iso9660_dir
{
rt_uint8_t len;
rt_uint8_t ext_sectors;
rt_le32_t first_sector;
rt_le32_t first_sector_be;
rt_le32_t size;
rt_le32_t size_be;
struct iso9660_date2 mtime;
#define FLAG_TYPE_PLAIN 0
#define FLAG_TYPE_DIR 2
#define FLAG_TYPE 3
#define FLAG_MORE_EXTENTS 0x80
rt_uint8_t flags;
rt_uint8_t file_unit_size;
rt_uint8_t interleave_gap_size;
rt_le16_t vol_seq_num;
rt_le16_t vol_seq_num_be;
#define MAX_NAMELEN 255
rt_uint8_t namelen;
char name[0];
});
rt_packed(struct iso9660_date
{
rt_uint8_t year[4];
rt_uint8_t month[2];
rt_uint8_t day[2];
rt_uint8_t hour[2];
rt_uint8_t minute[2];
rt_uint8_t second[2];
rt_uint8_t hundredth[2];
rt_uint8_t offset;
});
/* Common volume descriptor */
rt_packed(struct iso9660_common_voldesc
{
struct iso9660_voldesc voldesc;
rt_uint8_t sysname[33];
rt_uint8_t volname[32];
rt_uint8_t unused2[8];
rt_le32_t vol_space_size_le;
rt_le32_t vol_space_size_be;
rt_uint8_t escape[32];
rt_le16_t vol_set_size_le;
rt_le16_t vol_set_size_be;
rt_le16_t vol_seq_num_le;
rt_le16_t vol_seq_num_be;
rt_le16_t logical_block_size_le;
rt_le16_t logical_block_size_be;
rt_le32_t path_table_size;
rt_le32_t path_table_size_be;
rt_le32_t path_table;
rt_le32_t path_table_be;
rt_uint8_t unused3[8];
struct iso9660_dir rootdir;
rt_uint8_t unused4[624];
struct iso9660_date created;
struct iso9660_date modified;
rt_uint8_t unused5[0 /* 1201 */];
});
struct iso9660
{
struct rt_device *dev;
rt_uint8_t joliet;
rt_uint8_t swap[ISO9660_BLKSZ];
struct iso9660_common_voldesc primary, supp;
};
struct iso9660_fd
{
struct iso9660 *iso;
struct iso9660_dir dirent;
};
struct iso9660_iterate
{
struct iso9660_fd *fd;
int i, index, count;
struct dirent *dirp;
};
static void iso9660_convert_string(char *dest, rt_uint16_t *src, int len)
{
/* UTF16 to ASCII */
len >>= 1;
for (int i = 0; i < len; ++i)
{
rt_uint16_t utf16 = rt_be16_to_cpu(*src++);
if (utf16 < 0x80)
{
*dest++ = (rt_uint8_t)utf16;
}
else
{
*dest++ = '?';
}
}
*dest = '\0';
}
static void iso9660_convert_lower(char *dest, rt_uint8_t *src, int len)
{
for (int i = 0; i < len; ++i, ++src)
{
if (*src >= 'A' && *src <= 'Z')
{
*dest++ = *src - ('A' - 'a');
}
else
{
*dest++ = *src;
}
}
*dest = '\0';
}
static time_t iso9660_convert_unixtime(struct iso9660_date *date)
{
struct tm tm;
tm.tm_sec = (date->second[0] - '0') * 10 + (date->second[1] - '0');
tm.tm_min = (date->minute[0] - '0') * 10 + (date->minute[1] - '0');
tm.tm_hour = (date->hour[0] - '0') * 10 + (date->hour[1] - '0');
tm.tm_mday = (date->day[0] - '0') * 10 + (date->day[1] - '0');
tm.tm_mon = (date->month[0] - '0') * 10 + (date->month[1] - '0');
tm.tm_year = (date->year[0] - '0') * 1000 + (date->year[1] - '0') * 100 +
(date->year[2] - '0') * 10 + (date->year[3] - '0');
tm.tm_wday = 0;
return mktime(&tm);
}
static time_t iso9660_convert_unixtime2(struct iso9660_date2 *date)
{
struct tm tm;
tm.tm_sec = date->second;
tm.tm_min = date->minute;
tm.tm_hour = date->hour;
tm.tm_mday = date->day;
tm.tm_mon = date->month;
tm.tm_year = date->year + 1900;
tm.tm_wday = 0;
return mktime(&tm);
}
static struct iso9660_fd *iso9660_lookup(struct iso9660 *iso, const char *path,
struct iso9660_iterate *it)
{
rt_uint32_t lba;
rt_size_t sz, len, namelen;
char sname[MAX_NAMELEN];
struct iso9660_fd *fd;
struct iso9660_dir *dirent;
if (it)
{
fd = it->fd;
iso = fd->iso;
dirent = &fd->dirent;
/* No next entry, always goon */
len = 1;
}
else
{
if (!(fd = rt_malloc(sizeof(*fd))))
{
return fd;
}
fd->iso = iso;
dirent = iso->joliet ? &iso->supp.rootdir : &iso->primary.rootdir;
if (!rt_strcmp(path, "/"))
{
rt_memcpy(&fd->dirent, dirent, sizeof(*dirent));
return fd;
}
/* Skip the first '/' */
++path;
len = strchrnul(path, '/') - path;
}
lba = rt_le32_to_cpu(dirent->first_sector);
if (rt_device_read(iso->dev, lba, iso->swap, 1) <= 0)
{
goto _fail;
}
dirent = (void *)iso->swap;
sz = 0;
do {
/* Ignore "." and ".." */
do {
rt_uint32_t dlen = rt_le32_to_cpu(dirent->len);
dirent = (void *)dirent + dlen;
sz += dlen;
if (ISO9660_BLKSZ - sz < sizeof(*dirent))
{
/* Sector end, goto the next sector */
if (rt_device_read(iso->dev, ++lba, iso->swap, 1) <= 0)
{
goto _fail;
}
dirent = (void *)iso->swap;
sz = 0;
}
if (rt_le32_to_cpu(dirent->first_sector) == 0)
{
/* Is end, no found. */
goto _fail;
}
} while (dirent->name[0] >> 1 == 0 && rt_le32_to_cpu(dirent->namelen) == 1);
namelen = rt_le32_to_cpu(dirent->namelen);
if (iso->joliet)
{
iso9660_convert_string(sname, (rt_uint16_t *)dirent->name, namelen);
}
else
{
if (!(rt_le32_to_cpu(dirent->flags) & FLAG_TYPE_DIR))
{
/* Remove ";1" */
namelen -= 2;
}
iso9660_convert_lower(sname, (rt_uint8_t *)dirent->name, namelen);
}
if (it)
{
if (it->i < it->index)
{
goto _next;
}
if ((rt_le32_to_cpu(dirent->flags) & FLAG_TYPE) == FLAG_TYPE_DIR)
{
it->dirp->d_type = DT_DIR;
}
else
{
it->dirp->d_type = DT_REG;
}
it->dirp->d_namlen = namelen;
rt_strncpy(it->dirp->d_name, sname, namelen);
it->dirp->d_name[namelen] = '\0';
it->dirp->d_reclen = (rt_uint16_t)sizeof(struct dirent);
++it->dirp;
_next:
++it->i;
if (it->i - it->index >= it->count)
{
/* Iterate end */
return RT_NULL;
}
/* No next entry */
continue;
}
if (!rt_strncmp(sname, path, len))
{
/* The end of path, found ok */
if (!path[len])
{
rt_memcpy(&fd->dirent, dirent, sizeof(*dirent));
return fd;
}
/* Next entry */
lba = rt_le32_to_cpu(dirent->first_sector);
if (rt_device_read(iso->dev, lba, iso->swap, 1) <= 0)
{
goto _fail;
}
dirent = (void *)iso->swap;
sz = 0;
path += len + 1;
len = strchrnul(path, '/') - path;
}
} while (len);
_fail:
if (!it)
{
rt_free(fd);
}
return RT_NULL;
}
static int dfs_iso9660_open(struct dfs_file *fd)
{
struct iso9660 *iso = fd->vnode->fs->data;
fd->vnode->data = iso9660_lookup(iso, fd->vnode->path, RT_NULL);
return fd->vnode->data ? 0 : -EINVAL;
}
static int dfs_iso9660_close(struct dfs_file *fd)
{
struct iso9660_fd *iso_fd = fd->vnode->data;
rt_free(iso_fd);
return 0;
}
static int dfs_iso9660_read(struct dfs_file *fd, void *buf, size_t count)
{
rt_uint32_t pos;
void *buf_ptr;
ssize_t read_blk, toread_blk;
size_t rcount = 0, remain, size;
struct iso9660_fd *iso_fd = fd->vnode->data;
struct iso9660 *iso = iso_fd->iso;
if (fd->pos + count > rt_le32_to_cpu(iso_fd->dirent.size))
{
count = rt_le32_to_cpu(iso_fd->dirent.size) - fd->pos;
}
pos = rt_le32_to_cpu(iso_fd->dirent.first_sector);
/* Align to a sector */
if (fd->pos)
{
pos += fd->pos / ISO9660_BLKSZ;
remain = fd->pos & (ISO9660_BLKSZ - 1);
if (rt_device_read(iso->dev, pos, iso->swap, 1) <= 0)
{
return -EIO;
}
size = rt_min_t(size_t, ISO9660_BLKSZ - remain, count);
rt_memcpy(buf, &iso->swap[remain], size);
rcount += size;
count -= size;
buf += size;
pos += 1;
fd->pos += size;
if (!count)
{
goto _end;
}
}
remain = count & (ISO9660_BLKSZ - 1);
count = rt_max_t(size_t, count / ISO9660_BLKSZ, 1);
while ((ssize_t)count > 0)
{
if (count == 1)
{
buf_ptr = iso->swap;
toread_blk = 1;
}
else
{
buf_ptr = buf;
toread_blk = count;
}
read_blk = rt_device_read(iso->dev, pos, buf_ptr, toread_blk);
if (read_blk <= 0)
{
return (int)read_blk;
}
if (count == 1)
{
size = remain;
rt_memcpy(buf, iso->swap, size);
}
else
{
size = read_blk * ISO9660_BLKSZ;
}
rcount += size;
count -= read_blk;
buf += size;
pos += read_blk;
fd->pos += size;
}
_end:
return rcount;
}
static off_t dfs_iso9660_lseek(struct dfs_file *fd, off_t offset)
{
int ret = -EIO;
if (offset <= fd->vnode->size)
{
fd->pos = offset;
ret = fd->pos;
}
return ret;
}
static int dfs_iso9660_getdents(struct dfs_file *fd, struct dirent *dirp, uint32_t count)
{
struct iso9660_iterate it;
struct iso9660_fd *iso_fd = fd->vnode->data;
count = (count / sizeof(struct dirent));
if (!count)
{
return -EINVAL;
}
it.fd = iso_fd;
it.i = 0;
it.index = fd->pos;
it.count = count;
it.dirp = dirp;
iso9660_lookup(RT_NULL, RT_NULL, &it);
count = it.i - it.index;
if (count > 0)
{
fd->pos += count;
}
count *= sizeof(struct dirent);
return count;
}
static const struct dfs_file_ops _iso9660_fops =
{
.open = dfs_iso9660_open,
.close = dfs_iso9660_close,
.read = dfs_iso9660_read,
.lseek = dfs_iso9660_lseek,
.getdents = dfs_iso9660_getdents,
};
static int dfs_iso9660_mount(struct dfs_filesystem *fs,
unsigned long rwflag, const void *data)
{
int err;
struct iso9660 *iso;
struct iso9660_voldesc *voldesc;
struct rt_device_blk_geometry geometry;
if (!(iso = rt_malloc(sizeof(*iso))))
{
return -RT_ENOMEM;
}
iso->dev = fs->dev_id;
rt_device_control(iso->dev, RT_DEVICE_CTRL_BLK_GETGEOME, &geometry);
if (geometry.bytes_per_sector != ISO9660_BLKSZ)
{
LOG_E("%s: Logical block size = %d is not supported",
iso->dev->parent.name, geometry.bytes_per_sector);
err = -EINVAL;
goto _fail;
}
iso->primary.rootdir.first_sector = 0;
iso->supp.rootdir.first_sector = 0;
/* LBA 0-15 is the bootloader's information */
for (int lba = 16; ; ++lba)
{
if (rt_device_read(iso->dev, lba, &iso->swap, 1) <= 0)
{
err = -EIO;
goto _fail;
}
voldesc = (void *)iso->swap;
if (rt_strncmp((char *)voldesc->magic, "CD001", 5))
{
LOG_E("%s: Invalid magic \"%s\"", voldesc->magic);
err = -EINVAL;
goto _fail;
}
if (voldesc->type == ISO9660_VOLDESC_BOOT)
{
LOG_D("System Name: %s", ((struct iso9660_common_voldesc *)voldesc)->sysname);
LOG_D("Volume Name: %s", ((struct iso9660_common_voldesc *)voldesc)->volname);
}
else if (voldesc->type == ISO9660_VOLDESC_PRIMARY)
{
iso->joliet = 0;
rt_memcpy(&iso->primary, &iso->swap, sizeof(iso->primary));
}
else if (voldesc->type == ISO9660_VOLDESC_SUPP)
{
rt_memcpy(&iso->supp, &iso->swap, sizeof(iso->supp));
if (iso->supp.escape[0] == 0x25 && iso->supp.escape[1] == 0x2f)
{
if (iso->supp.escape[2] == 0x40)
{
iso->joliet = 1;
}
else if (iso->supp.escape[2] == 0x43)
{
iso->joliet = 2;
}
else if (iso->supp.escape[2] == 0x45)
{
iso->joliet = 3;
}
else
{
continue;
}
}
}
else if (voldesc->type == ISO9660_VOLDESC_PART)
{
LOG_D("System Name: %s", ((struct iso9660_common_voldesc *)voldesc)->sysname);
LOG_D("Volume Name: %s", ((struct iso9660_common_voldesc *)voldesc)->volname);
}
else if (voldesc->type == ISO9660_VOLDESC_END)
{
break;
}
}
if (!iso->primary.rootdir.first_sector || !iso->supp.rootdir.first_sector)
{
LOG_E("No primary or secondary partition found");
err = -EINVAL;
goto _fail;
}
fs->data = iso;
return 0;
_fail:
rt_free(iso);
return err;
}
static int dfs_iso9660_unmount(struct dfs_filesystem *fs)
{
struct iso9660 *iso = fs->data;
rt_free(iso);
return 0;
}
static int dfs_iso9660_stat(struct dfs_filesystem *fs,
const char *filename, struct stat *st)
{
struct iso9660 *iso = fs->data;
struct iso9660_fd *fd = iso9660_lookup(iso, filename, RT_NULL);
if (!fd)
{
return -EINVAL;
}
st->st_dev = 0;
st->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH |
S_IWUSR | S_IWGRP | S_IWOTH;
if ((fd->dirent.flags & FLAG_TYPE) == FLAG_TYPE_DIR)
{
st->st_mode &= ~S_IFREG;
st->st_mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH;
}
st->st_atime = iso9660_convert_unixtime(iso->joliet ?
&iso->supp.created : &iso->primary.created);
st->st_mtime = iso9660_convert_unixtime2(&fd->dirent.mtime);
st->st_size = rt_le32_to_cpu(fd->dirent.size);
rt_free(fd);
return 0;
}
static const struct dfs_filesystem_ops _iso9660 =
{
.name = "iso9660",
.flags = DFS_FS_FLAG_DEFAULT,
.fops = &_iso9660_fops,
.mount = dfs_iso9660_mount,
.unmount = dfs_iso9660_unmount,
.stat = dfs_iso9660_stat,
};
int dfs_iso9660_init(void)
{
/* register iso9660 file system */
return dfs_register(&_iso9660);
}
INIT_COMPONENT_EXPORT(dfs_iso9660_init);

View File

@@ -0,0 +1,16 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-02-25 GuEe-GUI the first version
*/
#ifndef __DFS_ISO9660_H__
#define __DFS_ISO9660_H__
int dfs_iso9660_init(void);
#endif /* __DFS_ISO9660_H__ */

View File

@@ -92,7 +92,7 @@ struct ramfs_dirent *dfs_ramfs_lookup(struct dfs_ramfs *ramfs,
return NULL;
}
int dfs_ramfs_read(struct dfs_file *file, void *buf, size_t count)
ssize_t dfs_ramfs_read(struct dfs_file *file, void *buf, size_t count)
{
rt_size_t length;
struct ramfs_dirent *dirent;
@@ -114,7 +114,7 @@ int dfs_ramfs_read(struct dfs_file *file, void *buf, size_t count)
return length;
}
int dfs_ramfs_write(struct dfs_file *fd, const void *buf, size_t count)
ssize_t dfs_ramfs_write(struct dfs_file *fd, const void *buf, size_t count)
{
struct ramfs_dirent *dirent;
struct dfs_ramfs *ramfs;
@@ -151,7 +151,7 @@ int dfs_ramfs_write(struct dfs_file *fd, const void *buf, size_t count)
return count;
}
int dfs_ramfs_lseek(struct dfs_file *file, off_t offset)
off_t dfs_ramfs_lseek(struct dfs_file *file, off_t offset)
{
if (offset <= (off_t)file->vnode->size)
{

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2006-2022, RT-Thread Development Team
* Copyright (c) 2006-2024 RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
@@ -107,7 +107,8 @@ int dfs_init(void)
INIT_PREV_EXPORT(dfs_init);
/**
* this function will lock device file system.
* @brief this function will lock device file system.
* this lock (fslock) is used for protecting filesystem_operation_table and filesystem_table.
*
* @note please don't invoke it on ISR.
*/
@@ -126,6 +127,12 @@ void dfs_lock(void)
}
}
/**
* @brief this function will lock file descriptors.
* this lock (fdlock) is used for protecting fd table (_fdtab).
*
* @note please don't invoke it on ISR.
*/
void dfs_file_lock(void)
{
rt_err_t result = -RT_EBUSY;
@@ -142,7 +149,7 @@ void dfs_file_lock(void)
}
/**
* this function will lock device file system.
* @brief this function will unlock device file system.
*
* @note please don't invoke it on ISR.
*/
@@ -151,33 +158,56 @@ void dfs_unlock(void)
rt_mutex_release(&fslock);
}
#ifdef DFS_USING_POSIX
/**
* @brief this function will unlock fd table.
*/
void dfs_file_unlock(void)
{
rt_mutex_release(&fdlock);
}
#ifdef DFS_USING_POSIX
/**
* @brief Expand the file descriptor table to accommodate a specific file descriptor.
*
* This function ensures that the file descriptor table in the given `dfs_fdtable` structure
* has sufficient capacity to include the specified file descriptor `fd`. If the table
* needs to be expanded, it reallocates memory and initializes new slots to `NULL`.
*
* @param fdt Pointer to the `dfs_fdtable` structure representing the file descriptor table.
* @param fd The file descriptor that the table must accommodate.
* @return int
* - The input file descriptor `fd` if it is within the current or newly expanded table's capacity.
* - `-1` if the requested file descriptor exceeds `DFS_FD_MAX` or memory allocation fails.
*/
static int fd_slot_expand(struct dfs_fdtable *fdt, int fd)
{
int nr;
int index;
struct dfs_file **fds = NULL;
/* If the file descriptor is already within the current capacity, no expansion is needed.*/
if (fd < fdt->maxfd)
{
return fd;
}
/* If the file descriptor exceeds the maximum allowable limit, return an error.*/
if (fd >= DFS_FD_MAX)
{
return -1;
}
/* Calculate the new capacity, rounding up to the nearest multiple of 4.*/
nr = ((fd + 4) & ~3);
/* Ensure the new capacity does not exceed the maximum limit.*/
if (nr > DFS_FD_MAX)
{
nr = DFS_FD_MAX;
}
/* Attempt to reallocate the file descriptor table to the new capacity.*/
fds = (struct dfs_file **)rt_realloc(fdt->fds, nr * sizeof(struct dfs_file *));
if (!fds)
{
@@ -189,12 +219,23 @@ static int fd_slot_expand(struct dfs_fdtable *fdt, int fd)
{
fds[index] = NULL;
}
/* Update the file descriptor table and its capacity.*/
fdt->fds = fds;
fdt->maxfd = nr;
return fd;
}
/**
* @brief Allocate a file descriptor slot starting from a specified index.
*
* @param fdt fdt Pointer to the `dfs_fdtable` structure representing the file descriptor table.
* @param startfd The starting index for the search for an empty slot.
* @return int
* - The index of the first available slot if successful.
* - `-1` if no slot is available or if table expansion fails
*/
static int fd_slot_alloc(struct dfs_fdtable *fdt, int startfd)
{
int idx;
@@ -219,6 +260,17 @@ static int fd_slot_alloc(struct dfs_fdtable *fdt, int startfd)
}
return idx;
}
/**
* @brief Allocate a new file descriptor and associate it with a newly allocated `struct dfs_file`.
*
* @param fdt Pointer to the `dfs_fdtable` structure representing the file descriptor table.
* @param startfd The starting index for searching an available file descriptor slot.
*
* @return
* - The index of the allocated file descriptor if successful.
* - `-1` if no slot is available or memory allocation fails.
*/
static int fd_alloc(struct dfs_fdtable *fdt, int startfd)
{
int idx;
@@ -323,7 +375,11 @@ struct dfs_file *fd_get(int fd)
/**
* @ingroup Fd
*
* This function will put the file descriptor.
* @brief This function will release the file descriptor.
*
* This function releases a file descriptor slot in the file descriptor table, decrements reference
* counts, and cleans up resources associated with the `dfs_file` and `dfs_vnode` structures when applicable.
*
*/
void fdt_fd_release(struct dfs_fdtable* fdt, int fd)
{
@@ -378,6 +434,20 @@ void fd_release(int fd)
fdt_fd_release(fdt, fd);
}
/**
* @brief Duplicates a file descriptor.
*
* This function duplicates an existing file descriptor (`oldfd`) and returns
* a new file descriptor that refers to the same underlying file object.
*
* @param oldfd The file descriptor to duplicate. It must be a valid file
* descriptor within the range of allocated descriptors.
*
* @return The new file descriptor if successful, or a negative value
* (e.g., -1) if an error occurs.
*
* @see sys_dup2()
*/
rt_err_t sys_dup(int oldfd)
{
int newfd = -1;
@@ -470,6 +540,23 @@ int fd_is_open(const char *pathname)
return -1;
}
/**
* @brief Duplicates a file descriptor to a specified file descriptor.
*
* This function duplicates an existing file descriptor (`oldfd`) and assigns it
* to the specified file descriptor (`newfd`).
*
* @param oldfd The file descriptor to duplicate. It must be a valid and open file
* descriptor within the range of allocated descriptors.
* @param newfd The target file descriptor. If `newfd` is already in use, it will
* be closed before duplication. If `newfd` exceeds the current file
* descriptor table size, the table will be expanded to accommodate it.
*
* @return The value of `newfd` on success, or a negative value (e.g., -1) if an
* error occurs.
*
* @see sys_dup()
*/
rt_err_t sys_dup2(int oldfd, int newfd)
{
struct dfs_fdtable *fdt = NULL;
@@ -550,6 +637,10 @@ static int fd_get_fd_index_form_fdt(struct dfs_fdtable *fdt, struct dfs_file *fi
return fd;
}
/**
* @brief get fd (index) by dfs file object.
*
*/
int fd_get_fd_index(struct dfs_file *file)
{
struct dfs_fdtable *fdt;
@@ -558,6 +649,21 @@ int fd_get_fd_index(struct dfs_file *file)
return fd_get_fd_index_form_fdt(fdt, file);
}
/**
* @brief Associates a file descriptor with a file object.
*
* This function associates a given file descriptor (`fd`) with a specified
* file object (`file`) in the file descriptor table (`fdt`).
*
* @param fdt The file descriptor table to operate on. It must be a valid
* and initialized `dfs_fdtable` structure.
* @param fd The file descriptor to associate. It must be within the range
* of allocated file descriptors and currently unoccupied.
* @param file The file object to associate with the file descriptor. It must
* be a valid and initialized `dfs_file` structure.
*
* @return The value of `fd` on success, or -1 if an error occurs.
*/
int fd_associate(struct dfs_fdtable *fdt, int fd, struct dfs_file *file)
{
int retfd = -1;
@@ -591,6 +697,10 @@ exit:
return retfd;
}
/**
* @brief initialize a dfs file object.
*
*/
void fd_init(struct dfs_file *fd)
{
if (fd)

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2006-2021, RT-Thread Development Team
* Copyright (c) 2006-2024 RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
@@ -18,10 +18,12 @@
#define DFS_VNODE_HASH_NR 128
/*dfs vnode manager, for saving and searching vnodes.*/
struct dfs_vnode_mgr
{
struct rt_mutex lock;
rt_list_t head[DFS_VNODE_HASH_NR];
struct rt_mutex lock; /* mutex for protecting dfs vnode lists */
rt_list_t head[DFS_VNODE_HASH_NR]; /* a group of dfs vnode lists, the dfs vnode is inserted to one of the lists
according to path string's hash-value mod DFS_VNODE_HASH_NR. */
};
static struct dfs_vnode_mgr dfs_fm;
@@ -36,6 +38,10 @@ void dfs_fm_unlock(void)
rt_mutex_release(&dfs_fm.lock);
}
/**
* @brief Initialize dfs vnode manager structure, including a lock and hash tables for vnode.
*
*/
void dfs_vnode_mgr_init(void)
{
int i = 0;
@@ -47,6 +53,23 @@ void dfs_vnode_mgr_init(void)
}
}
/**
* @brief Initialize a DFS vnode structure.
*
* @param vnode Pointer to the DFS vnode structure to be initialized.
* The caller must ensure this is a valid, allocated structure.
* @param type The type of the vnode, representing its role or category (e.g., regular file, directory).
* @param fops Pointer to the file operations structure associated with this vnode.
* This structure defines the behavior of the vnode for operations such as open, read, write, etc.
* If `fops` is NULL, the vnode will have no associated file operations.
*
* @return 0 on success, or a negative error code on failure.
*
* @note The caller should ensure that:
* - The `vnode` pointer is valid and properly allocated.
* - The `fops` pointer (if not NULL) points to a valid `struct dfs_file_ops`
* instance, where all necessary function pointers are properly set.
*/
int dfs_vnode_init(struct dfs_vnode *vnode, int type, const struct dfs_file_ops *fops)
{
if (vnode)
@@ -64,7 +87,7 @@ int dfs_vnode_init(struct dfs_vnode *vnode, int type, const struct dfs_file_ops
/* BKDR Hash Function */
static unsigned int bkdr_hash(const char *str)
{
unsigned int seed = 131; // 31 131 1313 13131 131313 etc..
unsigned int seed = 131; /* 31 131 1313 13131 131313 etc..*/
unsigned int hash = 0;
while (*str)
@@ -75,6 +98,22 @@ static unsigned int bkdr_hash(const char *str)
return (hash % DFS_VNODE_HASH_NR);
}
/**
* @brief Find a DFS vnode by its path.
*
* This function searches for a vnode in the vnode hash table using the specified path.
* If found, it returns a pointer to the vnode and updates the hash head if required.
*
* @param path The file path to search for. This should be a valid null-terminated string.
* @param hash_head Pointer to a location where the hash table head associated with the vnode
* can be stored. This can be NULL if the hash head is not needed.
*
* @return Pointer to the DFS vnode if found, or NULL if no vnode matches the specified path.
*
* @note The caller must ensure that:
* - The `path` pointer is valid and points to a properly null-terminated string.
* - If `hash_head` is not NULL, it points to a valid location to store the hash head.
*/
static struct dfs_vnode *dfs_vnode_find(const char *path, rt_list_t **hash_head)
{
struct dfs_vnode *vnode = NULL;
@@ -329,11 +368,12 @@ int dfs_file_close(struct dfs_file *fd)
}
/**
* this function will perform a io control on a file descriptor.
* this function will perform an io control on a file descriptor.
*
* @param fd the file descriptor.
* @param cmd the command to send to file descriptor.
* @param args the argument to send to file descriptor.
* - When `cmd` is `F_SETFL`, an additional integer argument specifies the new status flags.
*
* @return 0 on successful, -1 on failed.
*/
@@ -846,7 +886,7 @@ void cat(const char *filename)
{
buffer[length] = '\0';
rt_device_t out_device = rt_console_get_device();
rt_device_write(out_device, 0, (void *)buffer, sizeof(buffer));
rt_device_write(out_device, 0, (void *)buffer, length);
}
} while (length > 0);
rt_kprintf("\n");
@@ -1026,14 +1066,14 @@ void copy(const char *src, const char *dst)
flag |= FLAG_DST_IS_FILE;
}
//2. check status
/*2. check status*/
if ((flag & FLAG_SRC_IS_DIR) && (flag & FLAG_DST_IS_FILE))
{
rt_kprintf("cp faild, cp dir to file is not permitted!\n");
return ;
}
//3. do copy
/*3. do copy*/
if (flag & FLAG_SRC_IS_FILE)
{
if (flag & FLAG_DST_IS_DIR)
@@ -1053,7 +1093,7 @@ void copy(const char *src, const char *dst)
copyfile(src, dst);
}
}
else //flag & FLAG_SRC_IS_DIR
else /*flag & FLAG_SRC_IS_DIR*/
{
if (flag & FLAG_DST_IS_DIR)
{

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2006-2021, RT-Thread Development Team
* Copyright (c) 2006-2024 RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
@@ -322,8 +322,8 @@ int dfs_mount(const char *device_name,
/* open device, but do not check the status of device */
if (dev_id != NULL)
{
if (rt_device_open(fs->dev_id,
RT_DEVICE_OFLAG_RDWR) != RT_EOK)
if (rt_device_open(fs->dev_id, RT_DEVICE_OFLAG_RDWR) != RT_EOK &&
rt_device_open(fs->dev_id, RT_DEVICE_OFLAG_RDONLY) != RT_EOK)
{
/* The underlying device has error, clear the entry. */
dfs_lock();
@@ -529,7 +529,8 @@ int dfs_mount_device(rt_device_t dev)
{
int index = 0;
if(dev == RT_NULL) {
if(dev == RT_NULL)
{
rt_kprintf("the device is NULL to be mounted.\n");
return -RT_ERROR;
}
@@ -538,7 +539,8 @@ int dfs_mount_device(rt_device_t dev)
{
if (mount_table[index].path == NULL) break;
if(strcmp(mount_table[index].device_name, dev->parent.name) == 0) {
if(strcmp(mount_table[index].device_name, dev->parent.name) == 0)
{
if (dfs_mount(mount_table[index].device_name,
mount_table[index].path,
mount_table[index].filesystemtype,

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2006-2021, RT-Thread Development Team
* Copyright (c) 2006-2024 RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
@@ -28,7 +28,17 @@
* return a file descriptor according specified flags.
*
* @param file the path name of file.
* @param flags the file open flags.
* @param flags the file open flags. Common values include:
* - Access modes (mutually exclusive):
* - `O_RDONLY`: Open for read-only access.
* - `O_WRONLY`: Open for write-only access.
* - `O_RDWR`: Open for both reading and writing.
* - File status flags (can be combined with bitwise OR `|`):
* - `O_CREAT`: Create the file if it does not exist. Requires a `mode` argument.
* - `O_TRUNC`: Truncate the file to zero length if it already exists.
* - `O_APPEND`: Append writes to the end of the file.
* - `O_EXCL`: Ensure that `O_CREAT` creates the file exclusively.
* - Other platform-specific flags
*
* @return the non-negative integer on successful open, others for failed.
*/
@@ -65,6 +75,22 @@ RTM_EXPORT(open);
#ifndef AT_FDCWD
#define AT_FDCWD (-100)
#endif
/**
* @brief Opens a file relative to a directory file descriptor.
*
* @param dirfd The file descriptor of the directory to base the relative path on.
* @param pathname The path to the file to be opened, relative to the directory specified by `dirfd`.
* Can be an absolute path (in which case `dirfd` is ignored).
* @param flags File access and status flags (e.g., `O_RDONLY`, `O_WRONLY`, `O_CREAT`).
*
* @return On success, returns a new file descriptor for the opened file.
* On failure, returns `-1` and sets `errno` to indicate the error.
*
* @note When using relative paths, ensure `dirfd` is a valid directory descriptor.
* When `pathname` is absolute, the `dirfd` argument is ignored.
*
*/
int openat(int dirfd, const char *path, int flag, ...)
{
struct dfs_file *d;
@@ -241,14 +267,22 @@ ssize_t write(int fd, const void *buf, size_t len)
RTM_EXPORT(write);
/**
* this function is a POSIX compliant version, which will seek the offset for
* this function is a POSIX compliant version, which will Reposition the file offset for
* an open file descriptor.
*
* @param fd the file descriptor.
* @param offset the offset to be seeked.
* @param whence the directory of seek.
* The `lseek` function sets the file offset for the file descriptor `fd`
* to a new value, determined by the `offset` and `whence` parameters.
* It can be used to seek to specific positions in a file for reading or writing.
*
* @return the current read/write position in the file, or -1 on failed.
* @param fd the file descriptor.
* @param offset The offset, in bytes, to set the file position.
* The meaning of `offset` depends on the value of `whence`.
* @param whence the directive of seek. It can be one of:
* - `SEEK_SET`: Set the offset to `offset` bytes from the beginning of the file.
* - `SEEK_CUR`: Set the offset to its current location plus `offset` bytes.
* - `SEEK_END`: Set the offset to the size of the file plus `offset` bytes.
*
* @return the resulting read/write position in the file, or -1 on failed.
*/
off_t lseek(int fd, off_t offset, int whence)
{
@@ -436,9 +470,15 @@ RTM_EXPORT(fsync);
* control functions on devices.
*
* @param fildes the file description
* @param cmd the specified command
* @param cmd the specified command, Common values include:
* - `F_DUPFD`: Duplicate a file descriptor.
* - `F_GETFD`: Get the file descriptor flags.
* - `F_SETFD`: Set the file descriptor flags.
* - `F_GETFL`: Get the file status flags.
* - `F_SETFL`: Set the file status flags.
* @param ... represents the additional information that is needed by this
* specific device to perform the requested function.
* specific device to perform the requested function. For example:
* - When `cmd` is `F_SETFL`, an additional integer argument specifies the new status flags.
*
* @return 0 on successful completion. Otherwise, -1 shall be returned and errno
* set to indicate the error.
@@ -595,7 +635,7 @@ RTM_EXPORT(fstatfs);
* this function is a POSIX compliant version, which will make a directory
*
* @param path the directory path to be made.
* @param mode
* @param mode The permission mode for the new directory (unused here, can be set to 0).
*
* @return 0 on successful, others on failed.
*/

View File

@@ -786,18 +786,18 @@ static ssize_t dfs_cromfs_read(struct dfs_file *file, void *buf, size_t count, o
rt_err_t result = RT_EOK;
file_info *fi = NULL;
cromfs_info *ci = NULL;
uint32_t length = 0;
ssize_t length = 0;
ci = (cromfs_info *)file->dentry->mnt->data;
fi = (file_info *)file->vnode->data;
if (count < file->vnode->size - *pos)
if ((off_t)count < (off_t)file->vnode->size - *pos)
{
length = count;
}
else
{
length = file->vnode->size - *pos;
length = (off_t)file->vnode->size - *pos;
}
if (length > 0)

View File

@@ -11,6 +11,9 @@
#ifndef __DFS_CROMFS_H__
#define __DFS_CROMFS_H__
#include <stdint.h>
int dfs_cromfs_init(void);
uint8_t *cromfs_get_partition_data(uint32_t *len);
#endif /*__DFS_CROMFS_H__*/

View File

@@ -77,6 +77,7 @@ static int dfs_devfs_open(struct dfs_file *file)
}
}
}
rt_free(device_name);
}
return ret;
@@ -113,6 +114,29 @@ static int dfs_devfs_close(struct dfs_file *file)
return ret;
}
static rt_ubase_t _get_unit_shift(rt_device_t device)
{
rt_ubase_t shift = 0;
/**
* transfer unit size from POSIX RW(in bytes) to rt_device_R/W
* (block size for blk device, otherwise in bytes).
*/
if (device->type == RT_Device_Class_Block)
{
struct rt_device_blk_geometry geometry = {0};
/* default to 512 */
shift = 9;
if (!rt_device_control(device, RT_DEVICE_CTRL_BLK_GETGEOME, &geometry))
{
shift = __rt_ffs(geometry.block_size) - 1;
}
}
return shift;
}
static ssize_t dfs_devfs_read(struct dfs_file *file, void *buf, size_t count, off_t *pos)
{
ssize_t ret = -RT_EIO;
@@ -135,9 +159,14 @@ static ssize_t dfs_devfs_read(struct dfs_file *file, void *buf, size_t count, of
if (device->ops)
#endif /* RT_USING_POSIX_DEVIO */
{
/* read device data */
ret = rt_device_read(device, *pos, buf, count);
*pos += ret;
rt_ubase_t shift = _get_unit_shift(device);
ret = rt_device_read(device, *pos, buf, count >> shift);
if (ret > 0)
{
ret <<= shift;
*pos += ret;
}
}
}
@@ -169,9 +198,15 @@ static ssize_t dfs_devfs_write(struct dfs_file *file, const void *buf, size_t co
if (device->ops)
#endif /* RT_USING_POSIX_DEVIO */
{
rt_ubase_t shift = _get_unit_shift(device);
/* read device data */
ret = rt_device_write(device, *pos, buf, count);
*pos += ret;
ret = rt_device_write(device, *pos, buf, count >> shift);
if (ret > 0)
{
ret <<= shift;
*pos += ret;
}
}
}
@@ -265,7 +300,7 @@ static int dfs_devfs_flush(struct dfs_file *file)
static off_t dfs_devfs_lseek(struct dfs_file *file, off_t offset, int wherece)
{
off_t ret = 0;
off_t ret = -EPERM;
rt_device_t device;
RT_ASSERT(file != RT_NULL);
@@ -408,16 +443,16 @@ mode_t dfs_devfs_device_to_mode(struct rt_device *device)
switch (device->type)
{
case RT_Device_Class_Char:
mode = S_IFCHR | 0777;
mode = S_IFCHR | 0666;
break;
case RT_Device_Class_Block:
mode = S_IFBLK | 0777;
mode = S_IFBLK | 0666;
break;
case RT_Device_Class_Pipe:
mode = S_IFIFO | 0777;
mode = S_IFIFO | 0666;
break;
default:
mode = S_IFCHR | 0777;
mode = S_IFCHR | 0666;
break;
}

View File

@@ -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);
}

View File

@@ -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`

View File

@@ -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')

View 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);

View File

@@ -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

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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

View File

@@ -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

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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

View File

@@ -262,7 +262,7 @@ ptsno_t ptyfs_register_pts(rt_device_t ptmx, rt_device_t pts)
pts_file = rt_calloc(1, sizeof(struct ptyfs_file));
if (pts_file)
{
snprintf(pts_file->basename, DIRENT_NAME_MAX, "%lu", rc);
snprintf(pts_file->basename, DIRENT_NAME_MAX, "%lu", (unsigned long)rc);
ptyfile_init(pts_file, sb, 0, PTYFS_TYPE_FILE_SLAVE,
PTS_DEFAULT_FILE_MODE, pts);
ptyfile_add_to_root(sb, pts_file);
@@ -296,7 +296,7 @@ rt_err_t ptyfs_unregister_pts(rt_device_t ptmx, ptsno_t ptsno)
else
{
/* get path and findout device */
snprintf(path_buf, sizeof(path_buf), "%lu", ptsno);
snprintf(path_buf, sizeof(path_buf), "%lu", (unsigned long)ptsno);
pts_file = ptyfile_lookup(sb, path_buf);
if (pts_file)
{

View File

@@ -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,

View File

@@ -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;

View File

@@ -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 */

View File

@@ -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,

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -56,7 +56,7 @@ static inline void dfs_seq_setwidth(struct dfs_seq_file *seq, size_t size)
int dfs_seq_open(struct dfs_file *file, const struct dfs_seq_ops *ops);
ssize_t dfs_seq_read(struct dfs_file *file, void *buf, size_t size, off_t *pos);
ssize_t dfs_seq_lseek(struct dfs_file *file, off_t offset, int whence);
off_t dfs_seq_lseek(struct dfs_file *file, off_t offset, int whence);
int dfs_seq_release(struct dfs_file *file);
int dfs_seq_write(struct dfs_seq_file *seq, const void *data, size_t len);

View 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__*/

View File

@@ -537,6 +537,7 @@ int dfs_dup(int oldfd, int startfd)
fdt = dfs_fdtable_get();
if ((oldfd < 0) || (oldfd >= fdt->maxfd))
{
rt_set_errno(-EBADF);
goto exit;
}
if (!fdt->fds[oldfd])
@@ -668,12 +669,17 @@ sysret_t sys_dup(int oldfd)
int sys_dup(int oldfd)
#endif
{
int err = 0;
int newfd = dfs_dup(oldfd, (dfs_fdtable_get() == &_fdtab) ? DFS_STDIO_OFFSET : 0);
if(newfd < 0)
{
err = rt_get_errno();
}
#ifdef RT_USING_SMART
return (sysret_t)newfd;
return err < 0 ? err : newfd;
#else
return newfd;
return err < 0 ? err : newfd;
#endif
}

View File

@@ -1379,6 +1379,7 @@ int dfs_file_link(const char *oldname, const char *newname)
if (dfs_file_isdir(oldname) == 0)
{
rt_set_errno(-EPERM);
return ret;
}
@@ -1567,6 +1568,10 @@ int dfs_file_symlink(const char *target, const char *linkpath)
rt_free(parent);
}
}
else
{
rt_set_errno(-EPERM);
}
if (fullpath != linkpath)
rt_free(fullpath);

View File

@@ -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 <- - - - - - - +
@@ -127,7 +173,7 @@ int dfs_mount(const char *device_name,
}
else
{
rt_set_errno(ENOENT);
rt_set_errno(ENODEV);
ret = -1;
}
@@ -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);
}

View File

@@ -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;

View File

@@ -13,17 +13,19 @@
#define DBG_LVL DBG_WARNING
#include <rtdbg.h>
#include "dfs_pcache.h"
#include "dfs_dentry.h"
#include "dfs_mnt.h"
#include "mm_page.h"
#include <mmu.h>
#include <tlb.h>
#include <dfs_pcache.h>
#include <dfs_dentry.h>
#include <dfs_mnt.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
@@ -160,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;
@@ -175,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);
}
}
@@ -187,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;
@@ -1138,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)
@@ -1380,7 +1404,8 @@ int dfs_aspace_unmap(struct dfs_file *file, struct rt_varea *varea)
rt_varea_unmap_page(map_varea, vaddr);
if (varea->attr == MMU_MAP_U_RWCB && page->fpos < page->aspace->vnode->size)
if (!rt_varea_is_private_locked(varea) &&
page->fpos < page->aspace->vnode->size)
{
dfs_page_dirty(page);
}
@@ -1425,7 +1450,7 @@ int dfs_aspace_page_unmap(struct dfs_file *file, struct rt_varea *varea, void *v
if (map && varea->aspace == map->aspace && vaddr == map->vaddr)
{
if (varea->attr == MMU_MAP_U_RWCB)
if (!rt_varea_is_private_locked(varea))
{
dfs_page_dirty(page);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
* Copyright (c) 2006-2024 RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
@@ -31,7 +31,17 @@
* return a file descriptor according specified flags.
*
* @param file the path name of file.
* @param flags the file open flags.
* @param flags the file open flags. Common values include:
* - Access modes (mutually exclusive):
* - `O_RDONLY`: Open for read-only access.
* - `O_WRONLY`: Open for write-only access.
* - `O_RDWR`: Open for both reading and writing.
* - File status flags (can be combined with bitwise OR `|`):
* - `O_CREAT`: Create the file if it does not exist. Requires a `mode` argument.
* - `O_TRUNC`: Truncate the file to zero length if it already exists.
* - `O_APPEND`: Append writes to the end of the file.
* - `O_EXCL`: Ensure that `O_CREAT` creates the file exclusively.
* - Other platform-specific flags
*
* @return the non-negative integer on successful open, others for failed.
*/
@@ -81,6 +91,22 @@ RTM_EXPORT(open);
#ifndef AT_FDCWD
#define AT_FDCWD (-100)
#endif
/**
* @brief Opens a file relative to a directory file descriptor.
*
* @param dirfd The file descriptor of the directory to base the relative path on.
* @param path The path to the file to be opened, relative to the directory specified by `dirfd`.
* Can be an absolute path (in which case `dirfd` is ignored).
* @param flag File access and status flags (e.g., `O_RDONLY`, `O_WRONLY`, `O_CREAT`).
*
* @return On success, returns a new file descriptor for the opened file.
* On failure, returns `-1` and sets `errno` to indicate the error.
*
* @note When using relative paths, ensure `dirfd` is a valid directory descriptor.
* When `pathname` is absolute, the `dirfd` argument is ignored.
*
*/
int openat(int dirfd, const char *path, int flag, ...)
{
struct dfs_file *d;
@@ -171,7 +197,7 @@ int utimensat(int __fd, const char *__path, const struct timespec __times[2], in
}
}
//update time
/*update time*/
attr.ia_valid = ATTR_ATIME_SET | ATTR_MTIME_SET;
time(&current_time);
if (UTIME_NOW == __times[0].tv_nsec)
@@ -374,14 +400,22 @@ ssize_t write(int fd, const void *buf, size_t len)
RTM_EXPORT(write);
/**
* this function is a POSIX compliant version, which will seek the offset for
* this function is a POSIX compliant version, which will Reposition the file offset for
* an open file descriptor.
*
* @param fd the file descriptor.
* @param offset the offset to be seeked.
* @param whence the directory of seek.
* The `lseek` function sets the file offset for the file descriptor `fd`
* to a new value, determined by the `offset` and `whence` parameters.
* It can be used to seek to specific positions in a file for reading or writing.
*
* @return the current read/write position in the file, or -1 on failed.
* @param fd the file descriptor.
* @param offset The offset, in bytes, to set the file position.
* The meaning of `offset` depends on the value of `whence`.
* @param whence the directive of seek. It can be one of:
* - `SEEK_SET`: Set the offset to `offset` bytes from the beginning of the file.
* - `SEEK_CUR`: Set the offset to its current location plus `offset` bytes.
* - `SEEK_END`: Set the offset to the size of the file plus `offset` bytes.
*
* @return the resulting read/write position in the file, or -1 on failed.
*/
off_t lseek(int fd, off_t offset, int whence)
{
@@ -399,7 +433,7 @@ off_t lseek(int fd, off_t offset, int whence)
result = dfs_file_lseek(file, offset, whence);
if (result < 0)
{
rt_set_errno(result);
rt_set_errno(-EPERM);
return -1;
}
@@ -581,9 +615,15 @@ RTM_EXPORT(fsync);
* control functions on devices.
*
* @param fildes the file description
* @param cmd the specified command
* @param cmd the specified command, Common values include:
* - `F_DUPFD`: Duplicate a file descriptor.
* - `F_GETFD`: Get the file descriptor flags.
* - `F_SETFD`: Set the file descriptor flags.
* - `F_GETFL`: Get the file status flags.
* - `F_SETFL`: Set the file status flags.
* @param ... represents the additional information that is needed by this
* specific device to perform the requested function.
* specific device to perform the requested function. For example:
* - When `cmd` is `F_SETFL`, an additional integer argument specifies the new status flags.
*
* @return 0 on successful completion. Otherwise, -1 shall be returned and errno
* set to indicate the error.
@@ -765,7 +805,7 @@ RTM_EXPORT(fstatfs);
* this function is a POSIX compliant version, which will make a directory
*
* @param path the directory path to be made.
* @param mode
* @param mode The permission mode for the new directory (unused here, can be set to 0).
*
* @return 0 on successful, others on failed.
*/
@@ -827,7 +867,7 @@ int rmdir(const char *pathname)
if (!pathname)
{
rt_set_errno(-RT_ERROR);
rt_set_errno(-EPERM);
return -1;
}
@@ -852,7 +892,7 @@ int rmdir(const char *pathname)
if (dirent)
{
rt_set_errno(-RT_ERROR);
rt_set_errno(-EPERM);
return -1;
}
}
@@ -861,7 +901,7 @@ int rmdir(const char *pathname)
{
if (S_ISLNK(stat.st_mode))
{
rt_set_errno(-RT_ERROR);
rt_set_errno(-EPERM);
return -1;
}
}

View File

@@ -256,7 +256,7 @@ Enomem:
goto Done;
}
ssize_t dfs_seq_lseek(struct dfs_file *file, off_t offset, int whence)
off_t dfs_seq_lseek(struct dfs_file *file, off_t offset, int whence)
{
struct dfs_seq_file *seq = file->data;
off_t retval = -EINVAL;

View File

@@ -21,8 +21,21 @@ rsource "touch/Kconfig"
rsource "graphic/Kconfig"
rsource "hwcrypto/Kconfig"
rsource "wlan/Kconfig"
rsource "led/Kconfig"
rsource "mailbox/Kconfig"
rsource "phye/Kconfig"
rsource "ata/Kconfig"
rsource "nvme/Kconfig"
rsource "block/Kconfig"
rsource "scsi/Kconfig"
rsource "regulator/Kconfig"
rsource "reset/Kconfig"
rsource "thermal/Kconfig"
rsource "virtio/Kconfig"
rsource "dma/Kconfig"
rsource "mfd/Kconfig"
rsource "ofw/Kconfig"
rsource "pci/Kconfig"
rsource "pic/Kconfig"
rsource "pin/Kconfig"
rsource "pinctrl/Kconfig"

View File

@@ -0,0 +1,22 @@
menuconfig RT_USING_ATA
bool "Using Advanced Technology Attachment (ATA) device drivers"
depends on RT_USING_DM
depends on RT_USING_BLK
depends on RT_USING_DMA
default n
config RT_ATA_AHCI
bool "Advanced Host Controller Interface (AHCI)"
depends on RT_USING_ATA
depends on RT_USING_SCSI
default y
config RT_ATA_AHCI_PCI
bool "AHCI support on PCI bus"
depends on RT_ATA_AHCI
depends on RT_USING_PCI
default n
if RT_USING_ATA
osource "$(SOC_DM_ATA_DIR)/Kconfig"
endif

View File

@@ -0,0 +1,21 @@
from building import *
group = []
if not GetDepend(['RT_USING_ATA']):
Return('group')
cwd = GetCurrentDir()
CPPPATH = [cwd + '/../include']
src = []
if GetDepend(['RT_ATA_AHCI']):
src += ['ahci.c']
if GetDepend(['RT_ATA_AHCI_PCI']):
src += ['ahci-pci.c']
group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH)
Return('group')

View File

@@ -0,0 +1,206 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-02-25 GuEe-GUI the first version
*/
#include <rtthread.h>
#include <rtdevice.h>
#define AHCI_REG_BAR 5
struct pci_ahci_quirk
{
int bar_idx;
rt_bool_t bar_offset;
const struct rt_ahci_ops *ops;
};
struct pci_ahci_host
{
struct rt_ahci_host parent;
const struct pci_ahci_quirk *quirk;
rt_bool_t is_msi;
};
#define raw_to_pci_ahci_host(raw) rt_container_of(raw, struct pci_ahci_host, parent)
static rt_err_t pci_ahci_init(struct rt_ahci_host *host)
{
struct rt_pci_device *pdev;
pdev = rt_container_of(host->parent.dev, struct rt_pci_device, parent);
if (pdev->vendor == PCI_VENDOR_ID_JMICRON)
{
rt_pci_write_config_u8(pdev, 0x41, 0xa1);
}
return RT_EOK;
}
static const struct rt_ahci_ops pci_ahci_ops =
{
.host_init = pci_ahci_init,
};
static rt_err_t pci_ahci_intel_init(struct rt_ahci_host *host)
{
rt_uint16_t val;
struct rt_pci_device *pdev;
pdev = rt_container_of(host->parent.dev, struct rt_pci_device, parent);
rt_pci_read_config_u16(pdev, 0x92, &val);
rt_pci_write_config_u16(pdev, 0x92, val & ~0xf);
rt_thread_mdelay(10);
rt_pci_write_config_u16(pdev, 0x92, val | 0xf);
return RT_EOK;
}
static const struct rt_ahci_ops pci_ahci_intel_ops =
{
.host_init = pci_ahci_intel_init,
};
static rt_err_t pci_ahci_probe(struct rt_pci_device *pdev)
{
rt_err_t err;
int bar_idx;
struct rt_ahci_host *ahci;
struct pci_ahci_host *pci_ahci = rt_calloc(1, sizeof(*pci_ahci));
const struct pci_ahci_quirk *quirk = pdev->id->data;
if (!pci_ahci)
{
return -RT_ENOMEM;
}
pci_ahci->quirk = quirk;
ahci = &pci_ahci->parent;
ahci->parent.dev = &pdev->parent;
bar_idx = quirk && quirk->bar_offset ? quirk->bar_idx : AHCI_REG_BAR;
ahci->regs = rt_pci_iomap(pdev, bar_idx);
if (!ahci->regs)
{
err = -RT_EIO;
goto _fail;
}
ahci->ops = quirk && quirk->ops ? quirk->ops : &pci_ahci_ops;
if (rt_pci_msi_enable(pdev) > 0)
{
pci_ahci->is_msi = RT_TRUE;
}
else
{
rt_pci_irq_unmask(pdev);
}
ahci->irq = pdev->irq;
rt_pci_set_master(pdev);
if ((err = rt_ahci_host_register(ahci)))
{
goto _disable;
}
pdev->parent.user_data = pci_ahci;
return RT_EOK;
_disable:
if (pci_ahci->is_msi)
{
rt_pci_msix_disable(pdev);
}
else
{
rt_pci_irq_mask(pdev);
}
rt_pci_clear_master(pdev);
rt_iounmap(ahci->regs);
_fail:
rt_free(pci_ahci);
return err;
}
static rt_err_t pci_ahci_remove(struct rt_pci_device *pdev)
{
struct rt_ahci_host *ahci;
struct pci_ahci_host *pci_ahci = pdev->parent.user_data;
ahci = &pci_ahci->parent;
rt_ahci_host_unregister(ahci);
if (pci_ahci->is_msi)
{
rt_pci_msi_disable(pdev);
}
else
{
/* INTx is shared, don't mask all */
rt_hw_interrupt_umask(pdev->irq);
rt_pci_irq_mask(pdev);
}
rt_pci_clear_master(pdev);
rt_iounmap(ahci->regs);
rt_free(pci_ahci);
return RT_EOK;
}
static rt_err_t pci_ahci_shutdown(struct rt_pci_device *pdev)
{
return pci_ahci_remove(pdev);
}
static struct pci_ahci_quirk intel_quirk =
{
.ops = &pci_ahci_intel_ops,
};
static struct pci_ahci_quirk cavium_sata_quirk =
{
.bar_idx = 0,
.bar_offset = RT_TRUE,
};
static const struct rt_pci_device_id pci_ahci_ids[] =
{
{ RT_PCI_DEVICE_ID(PCI_VENDOR_ID_INTEL, 0x2922), .data = &intel_quirk },
{ RT_PCI_DEVICE_ID(PCI_VENDOR_ID_ASMEDIA, 0x0611) },
{ RT_PCI_DEVICE_ID(PCI_VENDOR_ID_MARVELL, 0x6121) },
{ RT_PCI_DEVICE_ID(PCI_VENDOR_ID_MARVELL, 0x6145) },
{ RT_PCI_DEVICE_ID(PCI_VENDOR_ID_CAVIUM, 0xa01c), .data = &cavium_sata_quirk },
{ RT_PCI_DEVICE_CLASS(PCIS_STORAGE_SATA_AHCI, ~0) },
{ /* sentinel */ }
};
static struct rt_pci_driver pci_ahci_driver =
{
.name = "ahci-pci",
.ids = pci_ahci_ids,
.probe = pci_ahci_probe,
.remove = pci_ahci_remove,
.shutdown = pci_ahci_shutdown,
};
RT_PCI_DRIVER_EXPORT(pci_ahci_driver);

View File

@@ -0,0 +1,896 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-02-25 GuEe-GUI the first version
*/
#include <rthw.h>
#include <rtthread.h>
#include <rtdevice.h>
#define DBG_TAG "rtdm.ahci"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
#define HWREG32_FLUSH(base, value) \
do { \
rt_uint32_t __value = value; \
HWREG32(base) = __value; \
__value = HWREG32(base); \
} while (0)
static void ahci_fill_cmd_slot(struct rt_ahci_port *port, rt_uint32_t opts)
{
rt_ubase_t dma_addr = port->cmd_tbl_dma;
struct rt_ahci_cmd_hdr *cmd_slot = port->cmd_slot;
cmd_slot->opts = rt_cpu_to_le32(opts);
cmd_slot->status = 0;
cmd_slot->tbl_addr_lo = rt_cpu_to_le32(rt_lower_32_bits(dma_addr));
cmd_slot->tbl_addr_hi = rt_cpu_to_le32(rt_upper_32_bits(dma_addr));
}
static int ahci_fill_sg(struct rt_ahci_host *host, int id,
void *buffer, rt_size_t buffer_size)
{
int sg_count;
rt_ubase_t dma_addr;
struct rt_ahci_port *port = &host->ports[id];
struct rt_ahci_sg *ahci_sg = port->cmd_tbl_sg;
sg_count = ((buffer_size - 1) / RT_ACHI_PRDT_BYTES_MAX) + 1;
if (sg_count > RT_AHCI_MAX_SG)
{
return -1;
}
dma_addr = (rt_ubase_t)rt_kmem_v2p(buffer);
for (int i = 0; i < sg_count; ++i, ++ahci_sg)
{
ahci_sg->addr_lo = rt_cpu_to_le32(rt_lower_32_bits(dma_addr));
ahci_sg->addr_hi = rt_cpu_to_le32(rt_upper_32_bits(dma_addr));
if (ahci_sg->addr_hi && !(host->cap & RT_AHCI_CAP_64))
{
return -1;
}
ahci_sg->flags_size = rt_cpu_to_le32(0x3fffff &
(rt_min_t(rt_uint32_t, buffer_size, RT_ACHI_PRDT_BYTES_MAX) - 1));
dma_addr += RT_ACHI_PRDT_BYTES_MAX;
buffer_size -= RT_ACHI_PRDT_BYTES_MAX;
}
return sg_count;
}
static rt_err_t ahci_request_io(struct rt_ahci_host *host, int id,
void *fis, rt_size_t fis_size,
void *buffer, rt_size_t buffer_size, rt_bool_t is_read)
{
int sg_count;
rt_err_t err;
struct rt_ahci_port *port = &host->ports[id];
if ((HWREG32(port->regs + RT_AHCI_PORT_SSTS) & 0xf) != RT_AHCI_PORT_SSTS_DET_PHYRDY)
{
return -RT_EIO;
}
if ((sg_count = ahci_fill_sg(host, id, buffer, buffer_size)) <= 0)
{
return -RT_EINVAL;
}
rt_memcpy(port->cmd_tbl, fis, fis_size);
ahci_fill_cmd_slot(port, (fis_size >> 2) | (sg_count << 16) | (!is_read << 6));
if (!is_read)
{
rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, buffer, buffer_size);
}
HWREG32_FLUSH(port->regs + RT_AHCI_PORT_CI, 1);
err = rt_completion_wait(&port->done, rt_tick_from_millisecond(10000));
if (!err && is_read)
{
rt_hw_cpu_dcache_ops(RT_HW_CACHE_INVALIDATE, buffer, buffer_size);
}
return err;
}
static rt_err_t ahci_scsi_cmd_rw(struct rt_ahci_host *host, int id,
rt_off_t lba, void *buffer, rt_ssize_t size, rt_bool_t is_read)
{
rt_err_t err;
rt_uint8_t fis[20];
struct rt_ahci_port *port = &host->ports[id];
rt_memset(fis, 0, sizeof(fis));
fis[0] = RT_AHCI_FIS_TYPE_REG_H2D;
fis[1] = 1 << 7; /* Command */
fis[2] = is_read ? RT_AHCI_ATA_CMD_READ_EXT : RT_AHCI_ATA_CMD_WRITE_EXT;
while (size > 0)
{
rt_size_t t_size, t_lba;
t_lba = rt_min_t(rt_size_t, host->max_blocks, size);
t_size = port->block_size * t_lba;
fis[3] = 0xe0; /* Features */
fis[4] = (lba >> 0) & 0xff; /* LBA low register */
fis[5] = (lba >> 8) & 0xff; /* LBA mid register */
fis[6] = (lba >> 16) & 0xff; /* LBA high register */
fis[7] = 1 << 6; /* Device */
fis[8] = ((lba >> 24) & 0xff); /* LBA register, 31:24 */
fis[9] = ((lba >> 32) & 0xff); /* LBA register, 39:32 */
fis[10] = ((lba >> 40) & 0xff); /* LBA register, 47:40 */
fis[12] = (t_lba >> 0) & 0xff; /* Count register, 7:0 */
fis[13] = (t_lba >> 8) & 0xff; /* Count register, 15:8 */
if ((err = ahci_request_io(host, id, fis, sizeof(fis), buffer, t_size, is_read)))
{
return err;
}
size -= t_lba;
lba += t_lba;
buffer += t_size;
}
return RT_EOK;
}
static rt_err_t ahci_scsi_synchronize_cache(struct rt_ahci_host *host, int id,
rt_off_t lba, rt_size_t size)
{
rt_uint8_t fis[20];
rt_uint16_t *ataid;
struct rt_ahci_port *port = &host->ports[id];
ataid = port->ataid;
if (!rt_ahci_ata_id_wcache_enabled(ataid) &&
!rt_ahci_ata_id_has_flush(ataid) &&
!rt_ahci_ata_id_has_flush_ext(ataid))
{
return -RT_ENOSYS;
}
rt_memset(fis, 0, sizeof(fis));
fis[0] = RT_AHCI_FIS_TYPE_REG_H2D;
fis[1] = 1 << 7; /* Command */
if (rt_ahci_ata_id_has_flush_ext(ataid))
{
fis[2] = RT_AHCI_ATA_CMD_FLUSH_EXT;
}
else
{
fis[2] = RT_AHCI_ATA_CMD_FLUSH;
}
rt_memcpy(port->cmd_tbl, fis, 20);
ahci_fill_cmd_slot(port, 5);
HWREG32_FLUSH(port->regs + RT_AHCI_PORT_CI, 1);
return rt_completion_wait(&port->done, rt_tick_from_millisecond(5000));
}
static rt_err_t ahci_scsi_cmd_write_same(struct rt_ahci_host *host, int id,
rt_off_t lba, rt_size_t size)
{
rt_uint8_t fis[20];
struct rt_ahci_port *port = &host->ports[id];
rt_memset(fis, 0, sizeof(fis));
fis[0] = RT_AHCI_FIS_TYPE_REG_H2D;
fis[1] = 1 << 7; /* Command */
fis[2] = RT_AHCI_ATA_CMD_DSM;
fis[3] = RT_AHCI_ATA_DSM_TRIM; /* Features */
fis[4] = (lba >> 0) & 0xff; /* LBA low register */
fis[5] = (lba >> 8) & 0xff; /* LBA mid register */
fis[6] = (lba >> 16) & 0xff; /* LBA high register */
fis[7] = 1 << 6; /* Device */
fis[8] = ((lba >> 24) & 0xff); /* LBA register, 31:24 */
fis[9] = ((lba >> 32) & 0xff); /* LBA register, 39:32 */
fis[10] = ((lba >> 40) & 0xff); /* LBA register, 47:40 */
fis[12] = (size >> 0) & 0xff; /* Count register, 7:0 */
fis[13] = (size >> 8) & 0xff; /* Count register, 15:8 */
HWREG32_FLUSH(port->regs + RT_AHCI_PORT_CI, 1);
return rt_completion_wait(&port->done, rt_tick_from_millisecond(5000));
}
static rt_err_t ahci_scsi_cmd_read_capacity(struct rt_ahci_host *host, int id,
rt_size_t *out_last_block, rt_size_t *out_block_size)
{
struct rt_ahci_port *port = &host->ports[id];
if (!port->ataid)
{
return -RT_EIO;
}
*out_last_block = rt_ahci_ata_id_n_sectors(port->ataid) - 1;
*out_block_size = port->block_size;
return RT_EOK;
}
static rt_err_t ahci_scsi_cmd_test_unit_ready(struct rt_ahci_host *host, int id)
{
struct rt_ahci_port *port = &host->ports[id];
return port->ataid ? RT_EOK : -RT_EIO;
}
static rt_err_t ahci_scsi_cmd_inquiry(struct rt_ahci_host *host, int id,
char *prodid, rt_size_t prodid_len, char *prodrev, rt_size_t prodrev_len)
{
rt_err_t err;
rt_uint8_t fis[20];
rt_uint16_t *ataid;
struct rt_ahci_port *port = &host->ports[id];
if (!port->link)
{
return -RT_EIO;
}
if (!port->ataid && !(port->ataid = rt_malloc(RT_AHCI_ATA_ID_WORDS * 2)))
{
return -RT_ENOMEM;
}
ataid = port->ataid;
rt_memset(fis, 0, sizeof(fis));
fis[0] = RT_AHCI_FIS_TYPE_REG_H2D;
fis[1] = 1 << 7; /* Command */
fis[2] = RT_AHCI_ATA_CMD_ID_ATA;
if ((err = ahci_request_io(host, id, fis, sizeof(fis),
ataid, RT_AHCI_ATA_ID_WORDS * 2, RT_TRUE)))
{
return err;
}
for (int i = 0; i < RT_AHCI_ATA_ID_WORDS; ++i)
{
ataid[i] = rt_le16_to_cpu(ataid[i]);
}
for (int i = 0; i < prodid_len / 2; ++i)
{
rt_uint16_t src = ataid[RT_AHCI_ATA_ID_PROD + i];
prodid[i] = (src & 0x00ff) << 8 | (src & 0xff00) >> 8;
}
for (int i = 0; i < prodrev_len / 2; ++i)
{
rt_uint16_t src = ataid[RT_AHCI_ATA_ID_FW_REV + i];
prodrev[i] = (src & 0x00ff) << 8 | (src & 0xff00) >> 8;
}
return err;
}
static rt_err_t ahci_scsi_transfer(struct rt_scsi_device *sdev,
struct rt_scsi_cmd *cmd)
{
rt_err_t err;
struct rt_ahci_host *host;
host = rt_container_of(sdev->host, struct rt_ahci_host, parent);
switch (cmd->op.unknow.opcode)
{
case RT_SCSI_CMD_REQUEST_SENSE:
{
struct rt_scsi_request_sense_data *request_sense = &cmd->data.request_sense;
request_sense->error_code = 0x72;
err = RT_EOK;
}
break;
case RT_SCSI_CMD_READ10:
{
struct rt_scsi_read10 *read10 = &cmd->op.read10;
err = ahci_scsi_cmd_rw(host, sdev->id,
rt_be32_to_cpu(read10->lba),
cmd->data.ptr,
rt_be16_to_cpu(read10->size),
RT_TRUE);
}
break;
case RT_SCSI_CMD_READ16:
{
struct rt_scsi_read16 *read16 = &cmd->op.read16;
err = ahci_scsi_cmd_rw(host, sdev->id,
rt_be64_to_cpu(read16->lba),
cmd->data.ptr,
rt_be32_to_cpu(read16->size),
RT_TRUE);
}
break;
case RT_SCSI_CMD_READ12:
{
struct rt_scsi_read12 *read12 = &cmd->op.read12;
err = ahci_scsi_cmd_rw(host, sdev->id,
rt_be32_to_cpu(read12->lba),
cmd->data.ptr,
rt_be32_to_cpu(read12->size),
RT_TRUE);
}
break;
case RT_SCSI_CMD_WRITE10:
{
struct rt_scsi_write10 *write10 = &cmd->op.write10;
err = ahci_scsi_cmd_rw(host, sdev->id,
rt_be32_to_cpu(write10->lba),
cmd->data.ptr,
rt_be16_to_cpu(write10->size),
RT_FALSE);
}
break;
case RT_SCSI_CMD_WRITE16:
{
struct rt_scsi_write16 *write16 = &cmd->op.write16;
err = ahci_scsi_cmd_rw(host, sdev->id,
rt_be64_to_cpu(write16->lba),
cmd->data.ptr,
rt_be32_to_cpu(write16->size),
RT_FALSE);
}
break;
case RT_SCSI_CMD_WRITE12:
{
struct rt_scsi_write12 *write12 = &cmd->op.write12;
err = ahci_scsi_cmd_rw(host, sdev->id,
rt_be32_to_cpu(write12->lba),
cmd->data.ptr,
rt_be32_to_cpu(write12->size),
RT_FALSE);
}
break;
case RT_SCSI_CMD_SYNCHRONIZE_CACHE10:
{
struct rt_scsi_synchronize_cache10 *synchronize_cache10 = &cmd->op.synchronize_cache10;
err = ahci_scsi_synchronize_cache(host, sdev->id,
rt_be32_to_cpu(synchronize_cache10->lba),
rt_be16_to_cpu(synchronize_cache10->size));
}
break;
case RT_SCSI_CMD_SYNCHRONIZE_CACHE16:
{
struct rt_scsi_synchronize_cache16 *synchronize_cache16 = &cmd->op.synchronize_cache16;
err = ahci_scsi_synchronize_cache(host, sdev->id,
rt_be64_to_cpu(synchronize_cache16->lba),
rt_be32_to_cpu(synchronize_cache16->size));
}
break;
case RT_SCSI_CMD_WRITE_SAME10:
{
struct rt_scsi_write_same10 *write_same10 = &cmd->op.write_same10;
err = ahci_scsi_cmd_write_same(host, sdev->id,
rt_be32_to_cpu(write_same10->lba), rt_be16_to_cpu(write_same10->size));
}
break;
case RT_SCSI_CMD_WRITE_SAME16:
{
struct rt_scsi_write_same16 *write_same16 = &cmd->op.write_same16;
err = ahci_scsi_cmd_write_same(host, sdev->id,
rt_be64_to_cpu(write_same16->lba), rt_be32_to_cpu(write_same16->size));
}
break;
case RT_SCSI_CMD_READ_CAPACITY10:
{
rt_size_t last_block, block_size;
struct rt_scsi_read_capacity10_data *data = &cmd->data.read_capacity10;
err = ahci_scsi_cmd_read_capacity(host, sdev->id, &last_block, &block_size);
if (!err)
{
if (last_block > 0x100000000ULL)
{
last_block = 0xffffffff;
}
data->last_block = rt_cpu_to_be32(last_block);
data->block_size = rt_cpu_to_be32(block_size);
}
}
break;
case RT_SCSI_CMD_READ_CAPACITY16:
{
rt_size_t last_block, block_size;
struct rt_scsi_read_capacity16_data *data = &cmd->data.read_capacity16;
err = ahci_scsi_cmd_read_capacity(host, sdev->id, &last_block, &block_size);
if (!err)
{
data->last_block = rt_cpu_to_be64(last_block);
data->block_size = rt_cpu_to_be32(block_size);
}
}
break;
case RT_SCSI_CMD_TEST_UNIT_READY:
err = ahci_scsi_cmd_test_unit_ready(host, sdev->id);
break;
case RT_SCSI_CMD_INQUIRY:
{
struct rt_ahci_port *port = &host->ports[sdev->id];
struct rt_scsi_inquiry_data *inquiry = &cmd->data.inquiry;
err = ahci_scsi_cmd_inquiry(host, sdev->id,
inquiry->prodid, sizeof(inquiry->prodid),
inquiry->prodrev, sizeof(inquiry->prodrev));
if (!err)
{
rt_memcpy(inquiry->vendor, "ATA ", sizeof(inquiry->vendor));
if (HWREG32(port->regs + RT_AHCI_PORT_SIG) != RT_AHCI_PORT_SIG_SATA_CDROM)
{
port->block_size = 512;
inquiry->devtype = SCSI_DEVICE_TYPE_DIRECT;
}
else
{
port->block_size = 2048;
inquiry->devtype = SCSI_DEVICE_TYPE_CDROM;
}
inquiry->rmb = 0;
inquiry->length = 95 - 4;
}
}
break;
case RT_SCSI_CMD_MODE_SENSE:
case RT_SCSI_CMD_MODE_SENSE10:
case RT_SCSI_CMD_MODE_SELECT:
case RT_SCSI_CMD_MODE_SELECT10:
return -RT_ENOSYS;
default:
return -RT_EINVAL;
}
return err;
}
static struct rt_scsi_ops ahci_scsi_ops =
{
.transfer = ahci_scsi_transfer,
};
static void ahci_isr(int irqno, void *param)
{
int id;
rt_uint32_t isr;
rt_bitmap_t int_map;
struct rt_ahci_port *port;
struct rt_ahci_host *host = param;
int_map = HWREG32(host->regs + RT_AHCI_HBA_INTS);
rt_bitmap_for_each_set_bit(&int_map, id, host->ports_nr)
{
port = &host->ports[id];
isr = HWREG32(port->regs + RT_AHCI_PORT_INTS);
if (port->link)
{
if (host->ops->port_isr)
{
host->ops->port_isr(host, port, isr);
}
rt_completion_done(&port->done);
}
HWREG32(port->regs + RT_AHCI_PORT_INTS) = isr;
}
HWREG32(host->regs + RT_AHCI_HBA_INTS) = int_map;
}
rt_err_t rt_ahci_host_register(struct rt_ahci_host *host)
{
rt_err_t err;
rt_uint32_t value;
char dev_name[RT_NAME_MAX];
struct rt_scsi_host *scsi;
if (!host || !host->parent.dev || !host->ops)
{
return -RT_EINVAL;
}
host->max_blocks = host->max_blocks ? : 0x80;
/*
* 1. Reset HBA.
*/
err = -RT_EIO;
value = HWREG32(host->regs + RT_AHCI_HBA_GHC);
if (!(value & RT_AHCI_GHC_RESET))
{
HWREG32_FLUSH(host->regs + RT_AHCI_HBA_GHC, value | RT_AHCI_GHC_RESET);
}
for (int i = 0; i < 5; ++i)
{
rt_thread_mdelay(200);
if (!(HWREG32(host->regs + RT_AHCI_HBA_GHC) & RT_AHCI_GHC_RESET))
{
err = RT_EOK;
break;
}
}
if (err)
{
goto _fail;
}
/*
* 2. Enable AHCI and get the ports' information.
*/
HWREG32_FLUSH(host->regs + RT_AHCI_HBA_GHC, RT_AHCI_GHC_AHCI_EN);
host->cap = HWREG32(host->regs + RT_AHCI_HBA_CAP);
host->cap &= RT_AHCI_CAP_SPM | RT_AHCI_CAP_SSS | RT_AHCI_CAP_SIS;
HWREG32(host->regs + RT_AHCI_HBA_CAP) = host->cap;
host->cap = HWREG32(host->regs + RT_AHCI_HBA_CAP);
HWREG32_FLUSH(host->regs + RT_AHCI_HBA_PI, 0xf);
if (host->ops->host_init && (err = host->ops->host_init(host)))
{
goto _fail;
}
host->ports_nr = (host->cap & RT_AHCI_CAP_NP) + 1;
host->ports_map = HWREG32(host->regs + RT_AHCI_HBA_PI);
/* Check implemented in firmware */
rt_dm_dev_prop_read_u32(host->parent.dev, "ports-implemented", &host->ports_map);
for (int i = 0; i < host->ports_nr; ++i)
{
struct rt_ahci_port *port;
if (!(host->ports_map & RT_BIT(i)))
{
continue;
}
port = &host->ports[i];
/*
* 3. Alloc port io memory.
*/
port->regs = host->regs + 0x100 + (i * 0x80);
/*
* 4. Make port stop.
*/
value = HWREG32(port->regs + RT_AHCI_PORT_CMD);
if (value & (RT_AHCI_PORT_CMD_LIST_ON | RT_AHCI_PORT_CMD_FIS_ON |
RT_AHCI_PORT_CMD_FIS_RX | RT_AHCI_PORT_CMD_START))
{
value &= ~(RT_AHCI_PORT_CMD_LIST_ON | RT_AHCI_PORT_CMD_FIS_ON |
RT_AHCI_PORT_CMD_FIS_RX | RT_AHCI_PORT_CMD_START);
HWREG32_FLUSH(port->regs + RT_AHCI_PORT_CMD, value);
rt_thread_mdelay(500);
}
if (host->ops->port_init && (err = host->ops->port_init(host, port)))
{
LOG_E("Init port[%d] error = %s", rt_strerror(err));
continue;
}
value = HWREG32(port->regs + RT_AHCI_PORT_CMD);
value |= RT_AHCI_PORT_CMD_SPIN_UP;
HWREG32(port->regs + RT_AHCI_PORT_CMD) = value;
/*
* 5. Enable port's SATA link.
*/
if (host->ops->port_link_up)
{
err = host->ops->port_link_up(host, port);
}
else
{
err = -RT_ETIMEOUT;
for (int retry = 0; retry < 5; ++retry)
{
value = HWREG32(port->regs + RT_AHCI_PORT_SSTS);
if ((value & RT_AHCI_PORT_SSTS_DET_MASK) == RT_AHCI_PORT_SSTS_DET_PHYRDY)
{
err = RT_EOK;
break;
}
rt_thread_mdelay(2);
}
}
if (err)
{
if (HWREG32(port->regs + RT_AHCI_PORT_SSTS) & RT_AHCI_PORT_SSTS_DET_MASK)
{
LOG_E("SATA[%d] link error = %s", i, rt_strerror(err));
}
else
{
LOG_D("SATA[%d] not device", i);
}
continue;
}
/* Clear error status */
if ((value = HWREG32(port->regs + RT_AHCI_PORT_SERR)))
{
HWREG32(port->regs + RT_AHCI_PORT_SERR) = value;
}
for (int retry = 0; retry < 5; ++retry)
{
value = HWREG32(port->regs + RT_AHCI_PORT_TFD);
if (!(value & (RT_AHCI_PORT_TFDATA_BSY | RT_AHCI_PORT_TFDATA_DRQ)))
{
break;
}
rt_thread_mdelay(2);
value = HWREG32(port->regs + RT_AHCI_PORT_SSTS);
if ((value & RT_AHCI_PORT_SSTS_DET_MASK) == RT_AHCI_PORT_SSTS_DET_PHYRDY)
{
break;
}
}
value = HWREG32(port->regs + RT_AHCI_PORT_SSTS) & RT_AHCI_PORT_SSTS_DET_MASK;
if (value == RT_AHCI_PORT_SSTS_DET_COMINIT)
{
/* Retry to setup */
--i;
continue;
}
/* Clear error */
value = HWREG32(port->regs + RT_AHCI_PORT_SERR);
HWREG32(port->regs + RT_AHCI_PORT_SERR) = value;
/* Clear pending IRQ */
if ((value = HWREG32(port->regs + RT_AHCI_PORT_INTS)))
{
HWREG32(port->regs + RT_AHCI_PORT_INTS) = value;
}
HWREG32(host->regs + RT_AHCI_HBA_INTS) = RT_BIT(i);
value = HWREG32(port->regs + RT_AHCI_PORT_SSTS);
if ((value & RT_AHCI_PORT_SSTS_DET_MASK) == RT_AHCI_PORT_SSTS_DET_PHYRDY)
{
port->link = RT_TRUE;
}
}
HWREG32(host->regs + RT_AHCI_HBA_GHC) |= RT_AHCI_GHC_IRQ_EN;
for (int i = 0; i < host->ports_nr; ++i)
{
void *dma;
rt_ubase_t dma_addr;
rt_tick_t timeout;
struct rt_ahci_port *port = &host->ports[i];
if (!port->link)
{
continue;
}
/*
* 6. Alloc transport memory, Port x Command List and FIS Base Address.
*/
port->dma = rt_dma_alloc_coherent(host->parent.dev,
RT_AHCI_DMA_SIZE, &port->dma_handle);
if (!port->dma)
{
LOG_E("No memory to setup port[%d]", i);
break;
}
dma = port->dma;
rt_memset(dma, 0, RT_AHCI_DMA_SIZE);
port->cmd_slot = dma;
dma += (RT_AHCI_CMD_SLOT_SIZE + 224);
port->rx_fis = dma;
dma += RT_AHCI_RX_FIS_SIZE;
port->cmd_tbl = dma;
port->cmd_tbl_dma = (rt_ubase_t)rt_kmem_v2p(dma);
dma += RT_AHCI_CMD_TBL_HDR;
port->cmd_tbl_sg = dma;
dma_addr = (rt_ubase_t)rt_kmem_v2p(port->cmd_slot);
HWREG32_FLUSH(port->regs + RT_AHCI_PORT_CLB, rt_lower_32_bits(dma_addr));
HWREG32_FLUSH(port->regs + RT_AHCI_PORT_CLBU, rt_upper_32_bits(dma_addr));
dma_addr = (rt_ubase_t)rt_kmem_v2p(port->rx_fis);
HWREG32_FLUSH(port->regs + RT_AHCI_PORT_FB, rt_lower_32_bits(dma_addr));
HWREG32_FLUSH(port->regs + RT_AHCI_PORT_FBU, rt_upper_32_bits(dma_addr));
if (host->ops->port_dma_init && (err = host->ops->port_dma_init(host, port)))
{
LOG_E("Init port[%d] DMA error = %s", rt_strerror(err));
}
HWREG32_FLUSH(port->regs + RT_AHCI_PORT_CMD, RT_AHCI_PORT_CMD_ACTIVE |
RT_AHCI_PORT_CMD_FIS_RX | RT_AHCI_PORT_CMD_POWER_ON |
RT_AHCI_PORT_CMD_SPIN_UP | RT_AHCI_PORT_CMD_START);
/* Wait spinup */
err = -RT_ETIMEOUT;
timeout = rt_tick_from_millisecond(20000);
timeout += rt_tick_get();
do {
if (!(HWREG32(port->regs + RT_AHCI_PORT_TFD) & RT_AHCI_PORT_TFDATA_BSY))
{
err = RT_EOK;
break;
}
rt_hw_cpu_relax();
} while (rt_tick_get() < timeout);
if (err)
{
rt_dma_free_coherent(host->parent.dev, RT_AHCI_DMA_SIZE, port->dma,
port->dma_handle);
port->dma = RT_NULL;
LOG_E("Start up port[%d] fail", i);
continue;
}
port->int_enabled |= RT_AHCI_PORT_INTE_HBUS_ERR | RT_AHCI_PORT_INTE_IF_ERR |
RT_AHCI_PORT_INTE_CONNECT | RT_AHCI_PORT_INTE_PHYRDY |
RT_AHCI_PORT_INTE_UNK_FIS | RT_AHCI_PORT_INTE_BAD_PMP |
RT_AHCI_PORT_INTE_TF_ERR | RT_AHCI_PORT_INTE_HBUS_DATA_ERR |
RT_AHCI_PORT_INTE_SG_DONE | RT_AHCI_PORT_INTE_SDB_FIS |
RT_AHCI_PORT_INTE_DMAS_FIS | RT_AHCI_PORT_INTE_PIOS_FIS |
RT_AHCI_PORT_INTE_D2H_REG_FIS;
HWREG32(port->regs + RT_AHCI_PORT_INTE) = port->int_enabled;
rt_completion_init(&port->done);
}
rt_snprintf(dev_name, sizeof(dev_name), "ahci-%s",
rt_dm_dev_get_name(host->parent.dev));
rt_hw_interrupt_install(host->irq, ahci_isr, host, dev_name);
rt_hw_interrupt_umask(host->irq);
scsi = &host->parent;
scsi->max_lun = rt_max_t(rt_size_t, scsi->max_lun, 1);
scsi->max_id = host->ports_nr;
scsi->ops = &ahci_scsi_ops;
if ((err = rt_scsi_host_register(scsi)))
{
goto _fail;
}
return RT_EOK;
_fail:
rt_hw_interrupt_mask(host->irq);
rt_pic_detach_irq(host->irq, host);
return err;
}
rt_err_t rt_ahci_host_unregister(struct rt_ahci_host *host)
{
rt_err_t err;
struct rt_scsi_host *scsi;
if (!host)
{
return -RT_EINVAL;
}
scsi = &host->parent;
if ((err = rt_scsi_host_unregister(scsi)))
{
return err;
}
rt_hw_interrupt_mask(host->irq);
rt_pic_detach_irq(host->irq, host);
for (int i = 0; i < host->ports_nr; ++i)
{
struct rt_ahci_port *port = &host->ports[i];
if (port->ataid)
{
rt_free(port->ataid);
}
HWREG32(port->regs) &= ~(RT_AHCI_PORT_CMD_ACTIVE | RT_AHCI_PORT_CMD_POWER_ON |
RT_AHCI_PORT_CMD_SPIN_UP | RT_AHCI_PORT_CMD_START);
if (port->dma)
{
rt_dma_free_coherent(host->parent.dev, RT_AHCI_DMA_SIZE, port->dma,
port->dma_handle);
}
}
HWREG32(host->regs + RT_AHCI_HBA_GHC) &= ~(RT_AHCI_GHC_AHCI_EN | RT_AHCI_GHC_IRQ_EN);
return RT_EOK;
}

View File

@@ -0,0 +1,612 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2017-05-09 Urey first version
* 2019-07-09 Zero-Free improve device ops interface and data flows
*/
#include <stdio.h>
#include <string.h>
#include <rthw.h>
#include <rtdevice.h>
#define DBG_TAG "audio"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
#ifndef MIN
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#endif
enum
{
REPLAY_EVT_NONE = 0x00,
REPLAY_EVT_START = 0x01,
REPLAY_EVT_STOP = 0x02,
};
static rt_err_t _audio_send_replay_frame(struct rt_audio_device *audio)
{
rt_err_t result = RT_EOK;
rt_uint8_t *data;
rt_size_t dst_size, src_size;
rt_uint16_t position, remain_bytes = 0, index = 0;
struct rt_audio_buf_info *buf_info;
RT_ASSERT(audio != RT_NULL);
buf_info = &audio->replay->buf_info;
/* save current pos */
position = audio->replay->pos;
dst_size = buf_info->block_size;
/* check replay queue is empty */
if (rt_data_queue_peek(&audio->replay->queue, (const void **)&data, &src_size) != RT_EOK)
{
/* ack stop event */
if (audio->replay->event & REPLAY_EVT_STOP)
rt_completion_done(&audio->replay->cmp);
/* send zero frames */
rt_memset(&buf_info->buffer[audio->replay->pos], 0, dst_size);
audio->replay->pos += dst_size;
audio->replay->pos %= buf_info->total_size;
}
else
{
rt_memset(&buf_info->buffer[audio->replay->pos], 0, dst_size);
/* copy data from memory pool to hardware device fifo */
while (index < dst_size)
{
result = rt_data_queue_peek(&audio->replay->queue, (const void **)&data, &src_size);
if (result != RT_EOK)
{
LOG_D("under run %d, remain %d", audio->replay->pos, remain_bytes);
audio->replay->pos -= remain_bytes;
audio->replay->pos += dst_size;
audio->replay->pos %= buf_info->total_size;
audio->replay->read_index = 0;
result = -RT_EEMPTY;
break;
}
remain_bytes = MIN((dst_size - index), (src_size - audio->replay->read_index));
rt_memcpy(&buf_info->buffer[audio->replay->pos],
&data[audio->replay->read_index], remain_bytes);
index += remain_bytes;
audio->replay->read_index += remain_bytes;
audio->replay->pos += remain_bytes;
audio->replay->pos %= buf_info->total_size;
if (audio->replay->read_index == src_size)
{
/* free memory */
audio->replay->read_index = 0;
rt_data_queue_pop(&audio->replay->queue, (const void **)&data, &src_size, RT_WAITING_NO);
rt_mp_free(data);
/* notify transmitted complete. */
if (audio->parent.tx_complete != RT_NULL)
audio->parent.tx_complete(&audio->parent, (void *)data);
}
}
}
if (audio->ops->transmit != RT_NULL)
{
if (audio->ops->transmit(audio, &buf_info->buffer[position], RT_NULL, dst_size) != dst_size)
result = -RT_ERROR;
}
return result;
}
static rt_err_t _audio_flush_replay_frame(struct rt_audio_device *audio)
{
rt_err_t result = RT_EOK;
if (audio->replay->write_index)
{
result = rt_data_queue_push(&audio->replay->queue,
(const void **)audio->replay->write_data,
audio->replay->write_index,
RT_WAITING_FOREVER);
audio->replay->write_index = 0;
}
return result;
}
static rt_err_t _aduio_replay_start(struct rt_audio_device *audio)
{
rt_err_t result = RT_EOK;
if (audio->replay->activated != RT_TRUE)
{
/* start playback hardware device */
if (audio->ops->start)
result = audio->ops->start(audio, AUDIO_STREAM_REPLAY);
audio->replay->activated = RT_TRUE;
LOG_D("start audio replay device");
}
return result;
}
static rt_err_t _aduio_replay_stop(struct rt_audio_device *audio)
{
rt_err_t result = RT_EOK;
if (audio->replay->activated == RT_TRUE)
{
/* flush replay remian frames */
_audio_flush_replay_frame(audio);
/* notify irq(or thread) to stop the data transmission */
audio->replay->event |= REPLAY_EVT_STOP;
/* waiting for the remaining data transfer to complete */
rt_completion_init(&audio->replay->cmp);
rt_completion_wait(&audio->replay->cmp, RT_WAITING_FOREVER);
audio->replay->event &= ~REPLAY_EVT_STOP;
/* stop playback hardware device */
if (audio->ops->stop)
result = audio->ops->stop(audio, AUDIO_STREAM_REPLAY);
audio->replay->activated = RT_FALSE;
LOG_D("stop audio replay device");
}
return result;
}
static rt_err_t _audio_record_start(struct rt_audio_device *audio)
{
rt_err_t result = RT_EOK;
if (audio->record->activated != RT_TRUE)
{
/* open audio record pipe */
rt_device_open(RT_DEVICE(&audio->record->pipe), RT_DEVICE_OFLAG_RDONLY);
/* start record hardware device */
if (audio->ops->start)
result = audio->ops->start(audio, AUDIO_STREAM_RECORD);
audio->record->activated = RT_TRUE;
LOG_D("start audio record device");
}
return result;
}
static rt_err_t _audio_record_stop(struct rt_audio_device *audio)
{
rt_err_t result = RT_EOK;
if (audio->record->activated == RT_TRUE)
{
/* stop record hardware device */
if (audio->ops->stop)
result = audio->ops->stop(audio, AUDIO_STREAM_RECORD);
/* close audio record pipe */
rt_device_close(RT_DEVICE(&audio->record->pipe));
audio->record->activated = RT_FALSE;
LOG_D("stop audio record device");
}
return result;
}
static rt_err_t _audio_dev_init(struct rt_device *dev)
{
rt_err_t result = RT_EOK;
struct rt_audio_device *audio;
RT_ASSERT(dev != RT_NULL);
audio = (struct rt_audio_device *) dev;
/* initialize replay & record */
audio->replay = RT_NULL;
audio->record = RT_NULL;
/* initialize replay */
if (dev->flag & RT_DEVICE_FLAG_WRONLY)
{
struct rt_audio_replay *replay = (struct rt_audio_replay *) rt_malloc(sizeof(struct rt_audio_replay));
if (replay == RT_NULL)
return -RT_ENOMEM;
rt_memset(replay, 0, sizeof(struct rt_audio_replay));
/* init memory pool for replay */
replay->mp = rt_mp_create("adu_mp", RT_AUDIO_REPLAY_MP_BLOCK_COUNT, RT_AUDIO_REPLAY_MP_BLOCK_SIZE);
if (replay->mp == RT_NULL)
{
rt_free(replay);
LOG_E("create memory pool for replay failed");
return -RT_ENOMEM;
}
/* init queue for audio replay */
rt_data_queue_init(&replay->queue, CFG_AUDIO_REPLAY_QUEUE_COUNT, 0, RT_NULL);
/* init mutex lock for audio replay */
rt_mutex_init(&replay->lock, "replay", RT_IPC_FLAG_PRIO);
replay->activated = RT_FALSE;
audio->replay = replay;
}
/* initialize record */
if (dev->flag & RT_DEVICE_FLAG_RDONLY)
{
struct rt_audio_record *record = (struct rt_audio_record *) rt_malloc(sizeof(struct rt_audio_record));
rt_uint8_t *buffer;
if (record == RT_NULL)
return -RT_ENOMEM;
rt_memset(record, 0, sizeof(struct rt_audio_record));
/* init pipe for record*/
buffer = rt_malloc(RT_AUDIO_RECORD_PIPE_SIZE);
if (buffer == RT_NULL)
{
rt_free(record);
LOG_E("malloc memory for for record pipe failed");
return -RT_ENOMEM;
}
rt_audio_pipe_init(&record->pipe, "record",
(rt_int32_t)(RT_PIPE_FLAG_FORCE_WR | RT_PIPE_FLAG_BLOCK_RD),
buffer,
RT_AUDIO_RECORD_PIPE_SIZE);
record->activated = RT_FALSE;
audio->record = record;
}
/* initialize hardware configuration */
if (audio->ops->init)
audio->ops->init(audio);
/* get replay buffer information */
if (audio->ops->buffer_info)
audio->ops->buffer_info(audio, &audio->replay->buf_info);
return result;
}
static rt_err_t _audio_dev_open(struct rt_device *dev, rt_uint16_t oflag)
{
struct rt_audio_device *audio;
RT_ASSERT(dev != RT_NULL);
audio = (struct rt_audio_device *) dev;
/* check device flag with the open flag */
if ((oflag & RT_DEVICE_OFLAG_RDONLY) && !(dev->flag & RT_DEVICE_FLAG_RDONLY))
return -RT_EIO;
if ((oflag & RT_DEVICE_OFLAG_WRONLY) && !(dev->flag & RT_DEVICE_FLAG_WRONLY))
return -RT_EIO;
/* get open flags */
dev->open_flag = oflag & 0xff;
/* initialize the Rx/Tx structure according to open flag */
if (oflag & RT_DEVICE_OFLAG_WRONLY)
{
if (audio->replay->activated != RT_TRUE)
{
LOG_D("open audio replay device, oflag = %x\n", oflag);
audio->replay->write_index = 0;
audio->replay->read_index = 0;
audio->replay->pos = 0;
audio->replay->event = REPLAY_EVT_NONE;
}
dev->open_flag |= RT_DEVICE_OFLAG_WRONLY;
}
if (oflag & RT_DEVICE_OFLAG_RDONLY)
{
/* open record pipe */
if (audio->record->activated != RT_TRUE)
{
LOG_D("open audio record device ,oflag = %x\n", oflag);
_audio_record_start(audio);
audio->record->activated = RT_TRUE;
}
dev->open_flag |= RT_DEVICE_OFLAG_RDONLY;
}
return RT_EOK;
}
static rt_err_t _audio_dev_close(struct rt_device *dev)
{
struct rt_audio_device *audio;
RT_ASSERT(dev != RT_NULL);
audio = (struct rt_audio_device *) dev;
if (dev->open_flag & RT_DEVICE_OFLAG_WRONLY)
{
/* stop replay stream */
_aduio_replay_stop(audio);
dev->open_flag &= ~RT_DEVICE_OFLAG_WRONLY;
}
if (dev->open_flag & RT_DEVICE_OFLAG_RDONLY)
{
/* stop record stream */
_audio_record_stop(audio);
dev->open_flag &= ~RT_DEVICE_OFLAG_RDONLY;
}
return RT_EOK;
}
static rt_ssize_t _audio_dev_read(struct rt_device *dev, rt_off_t pos, void *buffer, rt_size_t size)
{
struct rt_audio_device *audio;
RT_ASSERT(dev != RT_NULL);
audio = (struct rt_audio_device *) dev;
if (!(dev->open_flag & RT_DEVICE_OFLAG_RDONLY) || (audio->record == RT_NULL))
return 0;
return rt_device_read(RT_DEVICE(&audio->record->pipe), pos, buffer, size);
}
static rt_ssize_t _audio_dev_write(struct rt_device *dev, rt_off_t pos, const void *buffer, rt_size_t size)
{
struct rt_audio_device *audio;
rt_uint8_t *ptr;
rt_uint16_t block_size, remain_bytes, index = 0;
RT_ASSERT(dev != RT_NULL);
audio = (struct rt_audio_device *) dev;
if (!(dev->open_flag & RT_DEVICE_OFLAG_WRONLY) || (audio->replay == RT_NULL))
return 0;
/* push a new frame to replay data queue */
ptr = (rt_uint8_t *)buffer;
block_size = RT_AUDIO_REPLAY_MP_BLOCK_SIZE;
rt_mutex_take(&audio->replay->lock, RT_WAITING_FOREVER);
while (index < size)
{
/* request buffer from replay memory pool */
if (audio->replay->write_index % block_size == 0)
{
audio->replay->write_data = rt_mp_alloc(audio->replay->mp, RT_WAITING_FOREVER);
rt_memset(audio->replay->write_data, 0, block_size);
}
/* copy data to replay memory pool */
remain_bytes = MIN((block_size - audio->replay->write_index), (size - index));
rt_memcpy(&audio->replay->write_data[audio->replay->write_index], &ptr[index], remain_bytes);
index += remain_bytes;
audio->replay->write_index += remain_bytes;
audio->replay->write_index %= block_size;
if (audio->replay->write_index == 0)
{
rt_data_queue_push(&audio->replay->queue,
audio->replay->write_data,
block_size,
RT_WAITING_FOREVER);
}
}
rt_mutex_release(&audio->replay->lock);
/* check replay state */
if (audio->replay->activated != RT_TRUE)
{
_aduio_replay_start(audio);
audio->replay->activated = RT_TRUE;
}
return index;
}
static rt_err_t _audio_dev_control(struct rt_device *dev, int cmd, void *args)
{
rt_err_t result = RT_EOK;
struct rt_audio_device *audio;
RT_ASSERT(dev != RT_NULL);
audio = (struct rt_audio_device *) dev;
/* dev stat...*/
switch (cmd)
{
case AUDIO_CTL_GETCAPS:
{
struct rt_audio_caps *caps = (struct rt_audio_caps *) args;
LOG_D("AUDIO_CTL_GETCAPS: main_type = %d,sub_type = %d", caps->main_type, caps->sub_type);
if (audio->ops->getcaps != RT_NULL)
{
result = audio->ops->getcaps(audio, caps);
}
break;
}
case AUDIO_CTL_CONFIGURE:
{
struct rt_audio_caps *caps = (struct rt_audio_caps *) args;
LOG_D("AUDIO_CTL_CONFIGURE: main_type = %d,sub_type = %d", caps->main_type, caps->sub_type);
if (audio->ops->configure != RT_NULL)
{
result = audio->ops->configure(audio, caps);
}
break;
}
case AUDIO_CTL_START:
{
int stream = *(int *) args;
LOG_D("AUDIO_CTL_START: stream = %d", stream);
if (stream == AUDIO_STREAM_REPLAY)
{
result = _aduio_replay_start(audio);
}
else
{
result = _audio_record_start(audio);
}
break;
}
case AUDIO_CTL_STOP:
{
int stream = *(int *) args;
LOG_D("AUDIO_CTL_STOP: stream = %d", stream);
if (stream == AUDIO_STREAM_REPLAY)
{
result = _aduio_replay_stop(audio);
}
else
{
result = _audio_record_stop(audio);
}
break;
}
default:
break;
}
return result;
}
#ifdef RT_USING_DEVICE_OPS
const static struct rt_device_ops audio_ops =
{
_audio_dev_init,
_audio_dev_open,
_audio_dev_close,
_audio_dev_read,
_audio_dev_write,
_audio_dev_control
};
#endif
rt_err_t rt_audio_register(struct rt_audio_device *audio, const char *name, rt_uint32_t flag, void *data)
{
rt_err_t result = RT_EOK;
struct rt_device *device;
RT_ASSERT(audio != RT_NULL);
device = &(audio->parent);
device->type = RT_Device_Class_Sound;
device->rx_indicate = RT_NULL;
device->tx_complete = RT_NULL;
#ifdef RT_USING_DEVICE_OPS
device->ops = &audio_ops;
#else
device->init = _audio_dev_init;
device->open = _audio_dev_open;
device->close = _audio_dev_close;
device->read = _audio_dev_read;
device->write = _audio_dev_write;
device->control = _audio_dev_control;
#endif
device->user_data = data;
/* register a character device */
result = rt_device_register(device, name, flag | RT_DEVICE_FLAG_REMOVABLE);
/* initialize audio device */
if (result == RT_EOK)
result = rt_device_init(device);
return result;
}
int rt_audio_samplerate_to_speed(rt_uint32_t bitValue)
{
int speed = 0;
switch (bitValue)
{
case AUDIO_SAMP_RATE_8K:
speed = 8000;
break;
case AUDIO_SAMP_RATE_11K:
speed = 11052;
break;
case AUDIO_SAMP_RATE_16K:
speed = 16000;
break;
case AUDIO_SAMP_RATE_22K:
speed = 22050;
break;
case AUDIO_SAMP_RATE_32K:
speed = 32000;
break;
case AUDIO_SAMP_RATE_44K:
speed = 44100;
break;
case AUDIO_SAMP_RATE_48K:
speed = 48000;
break;
case AUDIO_SAMP_RATE_96K:
speed = 96000;
break;
case AUDIO_SAMP_RATE_128K:
speed = 128000;
break;
case AUDIO_SAMP_RATE_160K:
speed = 160000;
break;
case AUDIO_SAMP_RATE_172K:
speed = 176400;
break;
case AUDIO_SAMP_RATE_192K:
speed = 192000;
break;
default:
break;
}
return speed;
}
void rt_audio_tx_complete(struct rt_audio_device *audio)
{
/* try to send next frame */
_audio_send_replay_frame(audio);
}
void rt_audio_rx_done(struct rt_audio_device *audio, rt_uint8_t *pbuf, rt_size_t len)
{
/* save data to record pipe */
rt_device_write(RT_DEVICE(&audio->record->pipe), 0, pbuf, len);
/* invoke callback */
if (audio->parent.rx_indicate != RT_NULL)
audio->parent.rx_indicate(&audio->parent, len);
}

View File

@@ -0,0 +1,296 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2012-09-30 Bernard first version.
*/
#include <rthw.h>
#include <rtdevice.h>
#include "dev_audio_pipe.h"
static void _rt_audio_pipe_resume_writer(struct rt_audio_pipe *pipe)
{
if (!rt_list_isempty(&pipe->suspended_write_list))
{
rt_thread_t thread;
RT_ASSERT(pipe->flag & RT_PIPE_FLAG_BLOCK_WR);
/* get suspended thread */
thread = RT_THREAD_LIST_NODE_ENTRY(pipe->suspended_write_list.next);
/* resume the write thread */
rt_thread_resume(thread);
rt_schedule();
}
}
static rt_ssize_t rt_audio_pipe_read(rt_device_t dev,
rt_off_t pos,
void *buffer,
rt_size_t size)
{
rt_base_t level;
rt_thread_t thread;
struct rt_audio_pipe *pipe;
rt_size_t read_nbytes;
pipe = (struct rt_audio_pipe *)dev;
RT_ASSERT(pipe != RT_NULL);
if (!(pipe->flag & RT_PIPE_FLAG_BLOCK_RD))
{
level = rt_hw_interrupt_disable();
read_nbytes = rt_ringbuffer_get(&(pipe->ringbuffer), (rt_uint8_t *)buffer, size);
/* if the ringbuffer is empty, there won't be any writer waiting */
if (read_nbytes)
_rt_audio_pipe_resume_writer(pipe);
rt_hw_interrupt_enable(level);
return read_nbytes;
}
thread = rt_thread_self();
/* current context checking */
RT_DEBUG_NOT_IN_INTERRUPT;
do
{
level = rt_hw_interrupt_disable();
read_nbytes = rt_ringbuffer_get(&(pipe->ringbuffer), (rt_uint8_t *)buffer, size);
if (read_nbytes == 0)
{
rt_thread_suspend(thread);
/* waiting on suspended read list */
rt_list_insert_before(&(pipe->suspended_read_list),
&RT_THREAD_LIST_NODE(thread));
rt_hw_interrupt_enable(level);
rt_schedule();
}
else
{
_rt_audio_pipe_resume_writer(pipe);
rt_hw_interrupt_enable(level);
break;
}
}
while (read_nbytes == 0);
return read_nbytes;
}
static void _rt_audio_pipe_resume_reader(struct rt_audio_pipe *pipe)
{
if (pipe->parent.rx_indicate)
pipe->parent.rx_indicate(&pipe->parent,
rt_ringbuffer_data_len(&pipe->ringbuffer));
if (!rt_list_isempty(&pipe->suspended_read_list))
{
rt_thread_t thread;
RT_ASSERT(pipe->flag & RT_PIPE_FLAG_BLOCK_RD);
/* get suspended thread */
thread = RT_THREAD_LIST_NODE_ENTRY(pipe->suspended_read_list.next);
/* resume the read thread */
rt_thread_resume(thread);
rt_schedule();
}
}
static rt_ssize_t rt_audio_pipe_write(rt_device_t dev,
rt_off_t pos,
const void *buffer,
rt_size_t size)
{
rt_base_t level;
rt_thread_t thread;
struct rt_audio_pipe *pipe;
rt_size_t write_nbytes;
pipe = (struct rt_audio_pipe *)dev;
RT_ASSERT(pipe != RT_NULL);
if ((pipe->flag & RT_PIPE_FLAG_FORCE_WR) ||
!(pipe->flag & RT_PIPE_FLAG_BLOCK_WR))
{
level = rt_hw_interrupt_disable();
if (pipe->flag & RT_PIPE_FLAG_FORCE_WR)
write_nbytes = rt_ringbuffer_put_force(&(pipe->ringbuffer),
(const rt_uint8_t *)buffer, size);
else
write_nbytes = rt_ringbuffer_put(&(pipe->ringbuffer),
(const rt_uint8_t *)buffer, size);
_rt_audio_pipe_resume_reader(pipe);
rt_hw_interrupt_enable(level);
return write_nbytes;
}
thread = rt_thread_self();
/* current context checking */
RT_DEBUG_NOT_IN_INTERRUPT;
do
{
level = rt_hw_interrupt_disable();
write_nbytes = rt_ringbuffer_put(&(pipe->ringbuffer), (const rt_uint8_t *)buffer, size);
if (write_nbytes == 0)
{
/* pipe full, waiting on suspended write list */
rt_thread_suspend(thread);
/* waiting on suspended read list */
rt_list_insert_before(&(pipe->suspended_write_list),
&RT_THREAD_LIST_NODE(thread));
rt_hw_interrupt_enable(level);
rt_schedule();
}
else
{
_rt_audio_pipe_resume_reader(pipe);
rt_hw_interrupt_enable(level);
break;
}
}
while (write_nbytes == 0);
return write_nbytes;
}
static rt_err_t rt_audio_pipe_control(rt_device_t dev, int cmd, void *args)
{
struct rt_audio_pipe *pipe;
pipe = (struct rt_audio_pipe *)dev;
if (cmd == PIPE_CTRL_GET_SPACE && args)
*(rt_size_t *)args = rt_ringbuffer_space_len(&pipe->ringbuffer);
return RT_EOK;
}
#ifdef RT_USING_DEVICE_OPS
const static struct rt_device_ops audio_pipe_ops =
{
RT_NULL,
RT_NULL,
RT_NULL,
rt_audio_pipe_read,
rt_audio_pipe_write,
rt_audio_pipe_control
};
#endif
/**
* This function will initialize a pipe device and put it under control of
* resource management.
*
* @param pipe the pipe device
* @param name the name of pipe device
* @param flag the attribute of the pipe device
* @param buf the buffer of pipe device
* @param size the size of pipe device buffer
*
* @return the operation status, RT_EOK on successful
*/
rt_err_t rt_audio_pipe_init(struct rt_audio_pipe *pipe,
const char *name,
rt_int32_t flag,
rt_uint8_t *buf,
rt_size_t size)
{
RT_ASSERT(pipe);
RT_ASSERT(buf);
/* initialize suspended list */
rt_list_init(&pipe->suspended_read_list);
rt_list_init(&pipe->suspended_write_list);
/* initialize ring buffer */
rt_ringbuffer_init(&pipe->ringbuffer, buf, size);
pipe->flag = flag;
/* create pipe */
pipe->parent.type = RT_Device_Class_Pipe;
#ifdef RT_USING_DEVICE_OPS
pipe->parent.ops = &audio_pipe_ops;
#else
pipe->parent.init = RT_NULL;
pipe->parent.open = RT_NULL;
pipe->parent.close = RT_NULL;
pipe->parent.read = rt_audio_pipe_read;
pipe->parent.write = rt_audio_pipe_write;
pipe->parent.control = rt_audio_pipe_control;
#endif
return rt_device_register(&(pipe->parent), name, RT_DEVICE_FLAG_RDWR);
}
/**
* This function will detach a pipe device from resource management
*
* @param pipe the pipe device
*
* @return the operation status, RT_EOK on successful
*/
rt_err_t rt_audio_pipe_detach(struct rt_audio_pipe *pipe)
{
return rt_device_unregister(&pipe->parent);
}
#ifdef RT_USING_HEAP
rt_err_t rt_audio_pipe_create(const char *name, rt_int32_t flag, rt_size_t size)
{
rt_uint8_t *rb_memptr = RT_NULL;
struct rt_audio_pipe *pipe = RT_NULL;
/* get aligned size */
size = RT_ALIGN(size, RT_ALIGN_SIZE);
pipe = (struct rt_audio_pipe *)rt_calloc(1, sizeof(struct rt_audio_pipe));
if (pipe == RT_NULL)
return -RT_ENOMEM;
/* create ring buffer of pipe */
rb_memptr = (rt_uint8_t *)rt_malloc(size);
if (rb_memptr == RT_NULL)
{
rt_free(pipe);
return -RT_ENOMEM;
}
return rt_audio_pipe_init(pipe, name, flag, rb_memptr, size);
}
void rt_audio_pipe_destroy(struct rt_audio_pipe *pipe)
{
if (pipe == RT_NULL)
return;
/* un-register pipe device */
rt_audio_pipe_detach(pipe);
/* release memory */
rt_free(pipe->ringbuffer.buffer_ptr);
rt_free(pipe);
return;
}
#endif /* RT_USING_HEAP */

View File

@@ -0,0 +1,75 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
*/
#ifndef __DEV_AUDIO_PIPE_H__
#define __DEV_AUDIO_PIPE_H__
/**
* Pipe Device
*/
#include <rtdevice.h>
#ifndef RT_PIPE_BUFSZ
#define PIPE_BUFSZ 512
#else
#define PIPE_BUFSZ RT_PIPE_BUFSZ
#endif
/* portal device */
struct rt_audio_portal_device
{
struct rt_device parent;
struct rt_device *write_dev;
struct rt_device *read_dev;
};
enum rt_audio_pipe_flag
{
/* both read and write won't block */
RT_PIPE_FLAG_NONBLOCK_RDWR = 0x00,
/* read would block */
RT_PIPE_FLAG_BLOCK_RD = 0x01,
/* write would block */
RT_PIPE_FLAG_BLOCK_WR = 0x02,
/* write to this pipe will discard some data when the pipe is full.
* When this flag is set, RT_PIPE_FLAG_BLOCK_WR will be ignored since write
* operation will always be success. */
RT_PIPE_FLAG_FORCE_WR = 0x04,
};
struct rt_audio_pipe
{
struct rt_device parent;
/* ring buffer in pipe device */
struct rt_ringbuffer ringbuffer;
rt_int32_t flag;
/* suspended list */
rt_list_t suspended_read_list;
rt_list_t suspended_write_list;
struct rt_audio_portal_device *write_portal;
struct rt_audio_portal_device *read_portal;
};
#define PIPE_CTRL_GET_SPACE 0x14 /**< get the remaining size of a pipe device */
rt_err_t rt_audio_pipe_init(struct rt_audio_pipe *pipe,
const char *name,
rt_int32_t flag,
rt_uint8_t *buf,
rt_size_t size);
rt_err_t rt_audio_pipe_detach(struct rt_audio_pipe *pipe);
#ifdef RT_USING_HEAP
rt_err_t rt_audio_pipe_create(const char *name, rt_int32_t flag, rt_size_t size);
void rt_audio_pipe_destroy(struct rt_audio_pipe *pipe);
#endif /* RT_USING_HEAP */
#endif /* __DEV_AUDIO_PIPE_H__ */

View File

@@ -0,0 +1,7 @@
menuconfig RT_USING_BLK
bool "Using Block device drivers"
default n
if RT_USING_BLK
rsource "partitions/Kconfig"
endif

View File

@@ -0,0 +1,23 @@
from building import *
group = []
objs = []
if not GetDepend(['RT_USING_BLK']):
Return('group')
cwd = GetCurrentDir()
list = os.listdir(cwd)
CPPPATH = [cwd + '/../include']
src = ['blk.c', 'blk_dev.c', 'blk_dfs.c', 'blk_partition.c']
group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH)
for d in list:
path = os.path.join(cwd, d)
if os.path.isfile(os.path.join(path, 'SConscript')):
objs = objs + SConscript(os.path.join(d, 'SConscript'))
objs = objs + group
Return('objs')

View File

@@ -0,0 +1,573 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-02-25 GuEe-GUI the first version
*/
#define DBG_TAG "rtdm.blk"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
#include "blk_dev.h"
#include "blk_dfs.h"
static void blk_remove_all(struct rt_blk_disk *disk)
{
struct rt_blk_device *blk, *blk_next;
/* Remove all partitions */
rt_list_for_each_entry_safe(blk, blk_next, &disk->part_nodes, list)
{
disk_remove_blk_dev(blk, RT_TRUE);
}
}
static rt_err_t blk_open(rt_device_t dev, rt_uint16_t oflag)
{
struct rt_blk_disk *disk = to_blk_disk(dev);
if (disk->read_only && (oflag & RT_DEVICE_OFLAG_WRONLY))
{
return -RT_EINVAL;
}
return RT_EOK;
}
static rt_err_t blk_close(rt_device_t dev)
{
return RT_EOK;
}
static rt_ssize_t blk_read(rt_device_t dev, rt_off_t sector,
void *buffer, rt_size_t sector_count)
{
rt_ssize_t res;
struct rt_blk_disk *disk = to_blk_disk(dev);
rt_sem_take(&disk->usr_lock, RT_WAITING_FOREVER);
res = disk->ops->read(disk, sector, buffer, sector_count);
rt_sem_release(&disk->usr_lock);
return res;
}
static rt_ssize_t blk_write(rt_device_t dev, rt_off_t sector,
const void *buffer, rt_size_t sector_count)
{
rt_ssize_t res;
struct rt_blk_disk *disk = to_blk_disk(dev);
if (!disk->read_only)
{
rt_sem_take(&disk->usr_lock, RT_WAITING_FOREVER);
res = disk->ops->write(disk, sector, buffer, sector_count);
rt_sem_release(&disk->usr_lock);
return res;
}
return -RT_ENOSYS;
}
static rt_ssize_t blk_parallel_read(rt_device_t dev, rt_off_t sector,
void *buffer, rt_size_t sector_count)
{
struct rt_blk_disk *disk = to_blk_disk(dev);
return disk->ops->read(disk, sector, buffer, sector_count);
}
static rt_ssize_t blk_parallel_write(rt_device_t dev, rt_off_t sector,
const void *buffer, rt_size_t sector_count)
{
struct rt_blk_disk *disk = to_blk_disk(dev);
if (!disk->read_only)
{
return disk->ops->write(disk, sector, buffer, sector_count);
}
return -RT_ENOSYS;
}
static rt_err_t blk_control(rt_device_t dev, int cmd, void *args)
{
rt_err_t err;
struct rt_blk_disk *disk = to_blk_disk(dev);
switch (cmd)
{
case RT_DEVICE_CTRL_BLK_GETGEOME:
if (args)
{
err = disk->ops->getgeome(disk, args);
}
else
{
err = -RT_EINVAL;
}
break;
case RT_DEVICE_CTRL_BLK_SYNC:
if (disk->ops->sync)
{
rt_sem_take(&disk->usr_lock, RT_WAITING_FOREVER);
spin_lock(&disk->lock);
err = disk->ops->sync(disk);
spin_unlock(&disk->lock);
rt_sem_release(&disk->usr_lock);
}
else
{
err = -RT_ENOSYS;
}
break;
case RT_DEVICE_CTRL_BLK_ERASE:
if (disk->ops->erase)
{
rt_sem_take(&disk->usr_lock, RT_WAITING_FOREVER);
spin_lock(&disk->lock);
if (disk->parent.ref_count != 1)
{
err = -RT_EBUSY;
goto _unlock;
}
blk_remove_all(disk);
err = disk->ops->erase(disk);
_unlock:
spin_unlock(&disk->lock);
rt_sem_release(&disk->usr_lock);
}
else
{
err = -RT_ENOSYS;
}
break;
case RT_DEVICE_CTRL_BLK_AUTOREFRESH:
if (disk->ops->autorefresh)
{
err = disk->ops->autorefresh(disk, !!args);
}
else
{
err = -RT_ENOSYS;
}
break;
case RT_DEVICE_CTRL_BLK_PARTITION:
err = -RT_EINVAL;
break;
case RT_DEVICE_CTRL_BLK_SSIZEGET:
device_get_blk_ssize(dev, args);
err = RT_EOK;
break;
case RT_DEVICE_CTRL_ALL_BLK_SSIZEGET:
device_get_all_blk_ssize(dev, args);
err = RT_EOK;
break;
default:
if (disk->ops->control)
{
err = disk->ops->control(disk, RT_NULL, cmd, args);
}
else
{
err = -RT_ENOSYS;
}
break;
}
return err;
}
#ifdef RT_USING_DEVICE_OPS
const static struct rt_device_ops blk_ops =
{
.open = blk_open,
.close = blk_close,
.read = blk_read,
.write = blk_write,
.control = blk_control,
};
const static struct rt_device_ops blk_parallel_ops =
{
.open = blk_open,
.close = blk_close,
.read = blk_parallel_read,
.write = blk_parallel_write,
.control = blk_control,
};
#endif /* RT_USING_DEVICE_OPS */
rt_err_t rt_hw_blk_disk_register(struct rt_blk_disk *disk)
{
rt_err_t err;
#ifdef RT_USING_DM
int device_id;
#endif
const char *disk_name;
rt_uint16_t flags = RT_DEVICE_FLAG_RDONLY;
if (!disk || !disk->ops)
{
return -RT_EINVAL;
}
#ifdef RT_USING_DM
if (!disk->ida)
{
return -RT_EINVAL;
}
#endif
#if RT_NAME_MAX > 0
if (disk->parent.parent.name[0] == '\0')
#else
if (disk->parent.parent.name)
#endif
{
return -RT_EINVAL;
}
#ifdef RT_USING_DM
if ((device_id = rt_dm_ida_alloc(disk->ida)) < 0)
{
return -RT_EFULL;
}
#endif
disk->__magic = RT_BLK_DISK_MAGIC;
disk_name = to_disk_name(disk);
err = rt_sem_init(&disk->usr_lock, disk_name, 1, RT_IPC_FLAG_PRIO);
if (err)
{
#ifdef RT_USING_DM
rt_dm_ida_free(disk->ida, device_id);
#endif
LOG_E("%s: Init user mutex error = %s", rt_strerror(err));
return err;
}
rt_list_init(&disk->part_nodes);
rt_spin_lock_init(&disk->lock);
disk->parent.type = RT_Device_Class_Block;
#ifdef RT_USING_DEVICE_OPS
if (disk->parallel_io)
{
disk->parent.ops = &blk_parallel_ops;
}
else
{
disk->parent.ops = &blk_ops;
}
#else
disk->parent.open = blk_open;
disk->parent.close = blk_close;
if (disk->parallel_io)
{
disk->parent.read = blk_parallel_read;
disk->parent.write = blk_parallel_write;
}
else
{
disk->parent.read = blk_read;
disk->parent.write = blk_write;
}
disk->parent.control = blk_control;
#endif
if (!disk->ops->write)
{
disk->read_only = RT_TRUE;
}
if (!disk->read_only)
{
flags |= RT_DEVICE_FLAG_WRONLY;
}
#ifdef RT_USING_DM
disk->parent.master_id = disk->ida->master_id;
disk->parent.device_id = device_id;
#endif
device_set_blk_fops(&disk->parent);
err = rt_device_register(&disk->parent, disk_name, flags);
if (err)
{
rt_sem_detach(&disk->usr_lock);
}
/* Ignore partition scanning errors */
rt_blk_disk_probe_partition(disk);
return err;
}
rt_err_t rt_hw_blk_disk_unregister(struct rt_blk_disk *disk)
{
rt_err_t err;
if (!disk)
{
return -RT_EINVAL;
}
spin_lock(&disk->lock);
if (disk->parent.ref_count > 0)
{
err = -RT_EBUSY;
goto _unlock;
}
/* Flush all data */
if (disk->ops->sync)
{
err = disk->ops->sync(disk);
if (err)
{
LOG_E("%s: Sync error = %s", to_disk_name(disk), rt_strerror(err));
goto _unlock;
}
}
rt_sem_detach(&disk->usr_lock);
blk_remove_all(disk);
#ifdef RT_USING_DM
rt_dm_ida_free(disk->ida, disk->parent.device_id);
#endif
err = rt_device_unregister(&disk->parent);
_unlock:
spin_unlock(&disk->lock);
return err;
}
rt_ssize_t rt_blk_disk_get_capacity(struct rt_blk_disk *disk)
{
rt_ssize_t res;
struct rt_device_blk_geometry geometry;
if (!disk)
{
return -RT_EINVAL;
}
res = disk->ops->getgeome(disk, &geometry);
if (!res)
{
return geometry.sector_count;
}
return res;
}
rt_ssize_t rt_blk_disk_get_logical_block_size(struct rt_blk_disk *disk)
{
rt_ssize_t res;
struct rt_device_blk_geometry geometry;
if (!disk)
{
return -RT_EINVAL;
}
res = disk->ops->getgeome(disk, &geometry);
if (!res)
{
return geometry.bytes_per_sector;
}
return res;
}
#ifdef RT_USING_DFS_MNTTABLE
static int blk_dfs_mnt_table(void)
{
rt_ubase_t level;
struct rt_object *obj;
struct rt_device *dev;
struct rt_blk_disk *disk;
struct rt_blk_device *blk_dev;
struct rt_object_information *info = rt_object_get_information(RT_Object_Class_Device);
level = rt_hw_interrupt_disable();
rt_list_for_each_entry(obj, &info->object_list, list)
{
dev = rt_container_of(obj, struct rt_device, parent);
if (dev->type != RT_Device_Class_Block)
{
continue;
}
disk = to_blk_disk(dev);
if (disk->__magic != RT_BLK_DISK_MAGIC)
{
continue;
}
if (disk->max_partitions == RT_BLK_PARTITION_NONE)
{
dfs_mount_device(&disk->parent);
continue;
}
rt_list_for_each_entry(blk_dev, &disk->part_nodes, list)
{
dfs_mount_device(&blk_dev->parent);
}
}
rt_hw_interrupt_enable(level);
return 0;
}
INIT_ENV_EXPORT(blk_dfs_mnt_table);
#endif /* RT_USING_DFS_MNTTABLE */
#if defined(RT_USING_CONSOLE) && defined(RT_USING_MSH)
const char *convert_size(struct rt_device_blk_geometry *geome,
rt_size_t sector_count, rt_size_t *out_cap, rt_size_t *out_minor)
{
rt_size_t cap, minor = 0;
int size_index = 0;
const char *size_name[] = { "B", "K", "M", "G", "T", "P", "E" };
cap = geome->bytes_per_sector * sector_count;
for (size_index = 0; size_index < RT_ARRAY_SIZE(size_name) - 1; ++size_index)
{
if (cap < 1024)
{
break;
}
/* Only one decimal point */
minor = (cap % 1024) * 10 / 1024;
cap = cap / 1024;
}
*out_cap = cap;
*out_minor = minor;
return size_name[size_index];
}
static int list_blk(int argc, char**argv)
{
rt_ubase_t level;
rt_size_t cap, minor;
const char *size_name;
struct rt_object *obj;
struct rt_device *dev;
struct rt_blk_disk *disk;
struct rt_blk_device *blk_dev;
struct rt_device_blk_geometry geome;
struct rt_object_information *info = rt_object_get_information(RT_Object_Class_Device);
level = rt_hw_interrupt_disable();
rt_kprintf("%-*.s MAJ:MIN RM SIZE\tRO TYPE MOUNTPOINT\n", RT_NAME_MAX, "NAME");
rt_list_for_each_entry(obj, &info->object_list, list)
{
dev = rt_container_of(obj, struct rt_device, parent);
if (dev->type != RT_Device_Class_Block)
{
continue;
}
disk = to_blk_disk(dev);
if (disk->__magic != RT_BLK_DISK_MAGIC)
{
continue;
}
if (disk->ops->getgeome(disk, &geome))
{
continue;
}
size_name = convert_size(&geome, geome.sector_count, &cap, &minor);
rt_kprintf("%-*.s %3u.%-3u %u %u.%u%s\t%u disk %s\n",
RT_NAME_MAX, to_disk_name(disk),
#ifdef RT_USING_DM
disk->parent.master_id, disk->parent.device_id,
#else
0, 0,
#endif
disk->removable, cap, minor, size_name, disk->read_only,
disk->max_partitions != RT_BLK_PARTITION_NONE ? "\b" :
(dfs_filesystem_get_mounted_path(&disk->parent) ? : "\b"));
rt_list_for_each_entry(blk_dev, &disk->part_nodes, list)
{
size_name = convert_size(&geome, blk_dev->sector_count, &cap, &minor);
rt_kprintf("%c--%-*.s %3u.%-3u %u %u.%u%s\t%u part %s\n",
blk_dev->list.next != &disk->part_nodes ? '|' : '`',
RT_NAME_MAX - 3, to_blk_name(blk_dev),
#ifdef RT_USING_DM
blk_dev->parent.master_id, blk_dev->parent.device_id,
#else
0, 0,
#endif
disk->removable, cap, minor, size_name, disk->read_only,
dfs_filesystem_get_mounted_path(&blk_dev->parent) ? : "");
}
}
rt_hw_interrupt_enable(level);
return 0;
}
MSH_CMD_EXPORT(list_blk, dump all of blks information);
#endif /* RT_USING_CONSOLE && RT_USING_MSH */

View File

@@ -0,0 +1,297 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-02-25 GuEe-GUI first version
*/
#include "blk_dev.h"
#include "blk_dfs.h"
#define DBG_TAG "blk.dm"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
#ifdef RT_USING_DFS
#include <dfs_fs.h>
#endif
static rt_err_t blk_dev_open(rt_device_t dev, rt_uint16_t oflag)
{
struct rt_blk_device *blk = to_blk(dev);
return rt_device_open(&blk->disk->parent, oflag);
}
static rt_err_t blk_dev_close(rt_device_t dev)
{
struct rt_blk_device *blk = to_blk(dev);
return rt_device_close(&blk->disk->parent);
}
static rt_ssize_t blk_dev_read(rt_device_t dev, rt_off_t sector,
void *buffer, rt_size_t sector_count)
{
struct rt_blk_device *blk = to_blk(dev);
if (sector <= blk->sector_start + blk->sector_count &&
sector_count <= blk->sector_count)
{
return rt_device_read(&blk->disk->parent,
blk->sector_start + sector, buffer, sector_count);
}
return -RT_EINVAL;
}
static rt_ssize_t blk_dev_write(rt_device_t dev, rt_off_t sector,
const void *buffer, rt_size_t sector_count)
{
struct rt_blk_device *blk = to_blk(dev);
if (sector <= blk->sector_start + blk->sector_count &&
sector_count <= blk->sector_count)
{
return rt_device_write(&blk->disk->parent,
blk->sector_start + sector, buffer, sector_count);
}
return -RT_EINVAL;
}
static rt_err_t blk_dev_control(rt_device_t dev, int cmd, void *args)
{
rt_err_t err = -RT_EINVAL;
struct rt_blk_device *blk = to_blk(dev);
struct rt_blk_disk *disk = blk->disk;
struct rt_device_blk_geometry disk_geometry, *geometry;
switch (cmd)
{
case RT_DEVICE_CTRL_BLK_GETGEOME:
if ((geometry = args))
{
if (!(err = disk->ops->getgeome(disk, &disk_geometry)))
{
geometry->bytes_per_sector = disk_geometry.bytes_per_sector;
geometry->block_size = disk_geometry.block_size;
geometry->sector_count = blk->sector_count;
}
}
else
{
err = -RT_EINVAL;
}
break;
case RT_DEVICE_CTRL_BLK_SYNC:
rt_device_control(&disk->parent, cmd, args);
break;
case RT_DEVICE_CTRL_BLK_ERASE:
case RT_DEVICE_CTRL_BLK_AUTOREFRESH:
if (disk->partitions <= 1)
{
rt_device_control(&disk->parent, cmd, args);
}
else
{
err = -RT_EIO;
}
break;
case RT_DEVICE_CTRL_BLK_PARTITION:
if (args)
{
rt_memcpy(args, &blk->partition, sizeof(blk->partition));
}
else
{
err = -RT_EINVAL;
}
break;
case RT_DEVICE_CTRL_BLK_SSIZEGET:
device_get_blk_ssize(dev, args);
err = RT_EOK;
break;
case RT_DEVICE_CTRL_ALL_BLK_SSIZEGET:
device_get_all_blk_ssize(dev, args);
err = RT_EOK;
break;
default:
if (disk->ops->control)
{
err = disk->ops->control(disk, blk, cmd, args);
}
break;
}
return err;
}
#ifdef RT_USING_DEVICE_OPS
const static struct rt_device_ops blk_dev_ops =
{
.open = blk_dev_open,
.close = blk_dev_close,
.read = blk_dev_read,
.write = blk_dev_write,
.control = blk_dev_control,
};
#endif
rt_err_t blk_dev_initialize(struct rt_blk_device *blk)
{
struct rt_device *dev;
if (!blk)
{
return -RT_EINVAL;
}
dev = &blk->parent;
dev->type = RT_Device_Class_Block;
#ifdef RT_USING_DEVICE_OPS
dev->ops = &blk_dev_ops;
#else
dev->open = blk_dev_open;
dev->close = blk_dev_close;
dev->read = blk_dev_read;
dev->write = blk_dev_write;
dev->control = blk_dev_control;
#endif
return RT_EOK;
}
rt_err_t disk_add_blk_dev(struct rt_blk_disk *disk, struct rt_blk_device *blk)
{
rt_err_t err;
#ifdef RT_USING_DM
int device_id;
#endif
const char *disk_name, *name_fmt;
if (!disk || !blk)
{
return -RT_EINVAL;
}
#ifdef RT_USING_DM
if ((device_id = rt_dm_ida_alloc(disk->ida)) < 0)
{
return -RT_EFULL;
}
#endif
blk->disk = disk;
rt_list_init(&blk->list);
disk_name = to_disk_name(disk);
/* End is [a-zA-Z] or [0-9] */
if (disk_name[rt_strlen(disk_name) - 1] < 'a')
{
name_fmt = "%sp%d";
}
else
{
name_fmt = "%s%d";
}
#ifdef RT_USING_DM
rt_dm_dev_set_name(&blk->parent, name_fmt, disk_name, blk->partno);
blk->parent.master_id = disk->ida->master_id;
blk->parent.device_id = device_id;
#else
rt_snprintf(blk->parent.parent.name, RT_NAME_MAX, name_fmt, disk_name, blk->partno);
#endif
device_set_blk_fops(&blk->parent);
err = rt_device_register(&blk->parent, to_blk_name(blk),
disk->parent.flag & RT_DEVICE_FLAG_RDWR);
if (err)
{
#ifdef RT_USING_DM
rt_dm_ida_free(disk->ida, device_id);
#endif
return err;
}
spin_lock(&disk->lock);
rt_list_insert_before(&disk->part_nodes, &blk->list);
spin_unlock(&disk->lock);
return RT_EOK;
}
rt_err_t disk_remove_blk_dev(struct rt_blk_device *blk, rt_bool_t lockless)
{
struct rt_blk_disk *disk;
if (!blk)
{
return -RT_EINVAL;
}
disk = blk->disk;
if (!disk)
{
return -RT_EINVAL;
}
else
{
#ifdef RT_USING_DFS
const char *mountpath;
if ((mountpath = dfs_filesystem_get_mounted_path(&blk->parent)))
{
dfs_unmount(mountpath);
LOG_D("%s: Unmount file system on %s",
to_blk_name(blk), mountpath);
}
#endif
}
#ifdef RT_USING_DM
rt_dm_ida_free(disk->ida, blk->parent.device_id);
#endif
rt_device_unregister(&blk->parent);
if (!lockless)
{
spin_lock(&disk->lock);
}
rt_list_remove(&blk->list);
if (!lockless)
{
spin_unlock(&disk->lock);
}
--disk->partitions;
return RT_EOK;
}
rt_uint32_t blk_request_ioprio(void)
{
struct rt_thread *task = rt_thread_self();
return task ? RT_SCHED_PRIV(task).current_priority : 0;
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-02-25 GuEe-GUI first version
*/
#ifndef __BLK_DEV_H__
#define __BLK_DEV_H__
#include <rthw.h>
#include <rtthread.h>
#include <drivers/blk.h>
#include <drivers/misc.h>
#define to_blk_disk(dev) rt_container_of(dev, struct rt_blk_disk, parent)
#define to_blk(dev) rt_container_of(dev, struct rt_blk_device, parent)
#ifdef RT_USING_DM
#define to_disk_name(disk) rt_dm_dev_get_name(&(disk)->parent)
#define to_blk_name(blk) rt_dm_dev_get_name(&(blk)->parent)
#else
#define to_disk_name(disk) (disk)->parent.parent.name
#define to_blk_name(blk) (blk)->parent.parent.name
#endif
/* %c%c name */
#define letter_name(n) ('a' + (n) / ((n) >= 26 ? (26 * 2) : 1)), ((n) >= 26 ? 'a' + (n) % 26 : '\0')
rt_inline void spin_lock(struct rt_spinlock *spinlock)
{
rt_hw_spin_lock(&spinlock->lock);
}
rt_inline void spin_unlock(struct rt_spinlock *spinlock)
{
rt_hw_spin_unlock(&spinlock->lock);
}
rt_err_t blk_dev_initialize(struct rt_blk_device *blk);
rt_err_t disk_add_blk_dev(struct rt_blk_disk *disk, struct rt_blk_device *blk);
rt_err_t disk_remove_blk_dev(struct rt_blk_device *blk, rt_bool_t lockless);
rt_uint32_t blk_request_ioprio(void);
#endif /* __BLK_DEV_H__ */

View File

@@ -0,0 +1,274 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-08-08 GuEe-GUI first version
*/
#include "blk_dfs.h"
#include <dfs_file.h>
#include <drivers/classes/block.h>
#if defined(RT_USING_POSIX_DEVIO) && defined(RT_USING_DFS_V2)
struct blk_fops_data
{
struct rt_device_blk_geometry geometry;
};
static int blk_fops_open(struct dfs_file *file)
{
struct rt_device *dev = file->vnode->data;
struct blk_fops_data *data = rt_malloc(sizeof(*data));
if (!data)
{
return (int)-RT_ENOMEM;
}
dev->user_data = data;
rt_device_control(dev, RT_DEVICE_CTRL_BLK_GETGEOME, &data->geometry);
rt_device_control(dev, RT_DEVICE_CTRL_ALL_BLK_SSIZEGET, &file->vnode->size);
return 0;
}
static int blk_fops_close(struct dfs_file *file)
{
struct rt_device *dev = file->vnode->data;
rt_free(dev->user_data);
dev->user_data = RT_NULL;
return 0;
}
static int blk_fops_ioctl(struct dfs_file *file, int cmd, void *arg)
{
struct rt_device *dev = file->vnode->data;
return (int)rt_device_control(dev, cmd, arg);
}
static ssize_t blk_fops_read(struct dfs_file *file, void *buf, size_t count, off_t *pos)
{
void *rbuf;
rt_ssize_t res = 0;
int bytes_per_sector, blk_pos, first_offs, rsize = 0;
struct rt_device *dev = file->vnode->data;
struct blk_fops_data *data = dev->user_data;
bytes_per_sector = data->geometry.bytes_per_sector;
blk_pos = *pos / bytes_per_sector;
first_offs = *pos % bytes_per_sector;
if ((rbuf = rt_malloc(bytes_per_sector)))
{
/*
** #1: read first unalign block size.
*/
res = rt_device_read(dev, blk_pos, rbuf, 1);
if (res == 1)
{
if (count > bytes_per_sector - first_offs)
{
rsize = bytes_per_sector - first_offs;
}
else
{
rsize = count;
}
rt_memcpy(buf, rbuf + first_offs, rsize);
++blk_pos;
/*
** #2: read continuous block size.
*/
while (rsize < count)
{
res = rt_device_read(dev, blk_pos++, rbuf, 1);
if (res != 1)
{
break;
}
if (count - rsize >= bytes_per_sector)
{
rt_memcpy(buf + rsize, rbuf, bytes_per_sector);
rsize += bytes_per_sector;
}
else
{
rt_memcpy(buf + rsize, rbuf, count - rsize);
rsize = count;
}
}
*pos += rsize;
}
rt_free(rbuf);
}
return rsize;
}
static ssize_t blk_fops_write(struct dfs_file *file, const void *buf, size_t count, off_t *pos)
{
void *rbuf;
rt_ssize_t res = 0;
int bytes_per_sector, blk_pos, first_offs, wsize = 0;
struct rt_device *dev = file->vnode->data;
struct blk_fops_data *data = dev->user_data;
bytes_per_sector = data->geometry.bytes_per_sector;
blk_pos = *pos / bytes_per_sector;
first_offs = *pos % bytes_per_sector;
/*
** #1: write first unalign block size.
*/
if (first_offs != 0)
{
if (count > bytes_per_sector - first_offs)
{
wsize = bytes_per_sector - first_offs;
}
else
{
wsize = count;
}
if ((rbuf = rt_malloc(bytes_per_sector)))
{
res = rt_device_read(dev, blk_pos, rbuf, 1);
if (res == 1)
{
rt_memcpy(rbuf + first_offs, buf, wsize);
res = rt_device_write(dev, blk_pos, (const void *)rbuf, 1);
if (res == 1)
{
blk_pos += 1;
rt_free(rbuf);
goto _goon;
}
}
rt_free(rbuf);
}
return 0;
}
_goon:
/*
** #2: write continuous block size.
*/
if ((count - wsize) / bytes_per_sector != 0)
{
res = rt_device_write(dev, blk_pos, buf + wsize, (count - wsize) / bytes_per_sector);
wsize += res * bytes_per_sector;
blk_pos += res;
if (res != (count - wsize) / bytes_per_sector)
{
*pos += wsize;
return wsize;
}
}
/*
** # 3: write last unalign block size.
*/
if ((count - wsize) != 0)
{
if ((rbuf = rt_malloc(bytes_per_sector)))
{
res = rt_device_read(dev, blk_pos, rbuf, 1);
if (res == 1)
{
rt_memcpy(rbuf, buf + wsize, count - wsize);
res = rt_device_write(dev, blk_pos, (const void *)rbuf, 1);
if (res == 1)
{
wsize += count - wsize;
}
}
rt_free(rbuf);
}
}
*pos += wsize;
return wsize;
}
static int blk_fops_flush(struct dfs_file *file)
{
struct rt_device *dev = file->vnode->data;
return (int)rt_device_control(dev, RT_DEVICE_CTRL_BLK_SYNC, RT_NULL);
}
static int blk_fops_poll(struct dfs_file *file, struct rt_pollreq *req)
{
int mask = 0;
return mask;
}
const static struct dfs_file_ops blk_fops =
{
.open = blk_fops_open,
.close = blk_fops_close,
.ioctl = blk_fops_ioctl,
.read = blk_fops_read,
.write = blk_fops_write,
.flush = blk_fops_flush,
.lseek = generic_dfs_lseek,
.poll = blk_fops_poll
};
void device_set_blk_fops(struct rt_device *dev)
{
dev->fops = &blk_fops;
}
#else
void device_set_blk_fops(struct rt_device *dev)
{
}
#endif /* RT_USING_POSIX_DEVIO && RT_USING_DFS_V2 */
void device_get_blk_ssize(struct rt_device *dev, void *args)
{
rt_uint32_t bytes_per_sector;
struct rt_device_blk_geometry geometry;
rt_device_control(dev, RT_DEVICE_CTRL_BLK_GETGEOME, &geometry);
bytes_per_sector = geometry.bytes_per_sector;
RT_ASSERT(sizeof(bytes_per_sector) == sizeof(geometry.bytes_per_sector));
rt_memcpy(args, &bytes_per_sector, sizeof(bytes_per_sector));
}
void device_get_all_blk_ssize(struct rt_device *dev, void *args)
{
rt_uint64_t count_mul_per;
struct rt_device_blk_geometry geometry;
rt_device_control(dev, RT_DEVICE_CTRL_BLK_GETGEOME, &geometry);
count_mul_per = geometry.bytes_per_sector * geometry.sector_count;
rt_memcpy(args, &count_mul_per, sizeof(count_mul_per));
}

View File

@@ -0,0 +1,23 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-08-08 GuEe-GUI first version
*/
#ifndef __BLK_DFS_H__
#define __BLK_DFS_H__
#include <rtdef.h>
#define RT_DEVICE_CTRL_BLK_SSIZEGET 0x00001268 /**< get number of bytes per sector */
#define RT_DEVICE_CTRL_ALL_BLK_SSIZEGET 0x80081272 /**< get number of bytes per sector * sector counts */
void device_set_blk_fops(struct rt_device *dev);
void device_get_blk_ssize(struct rt_device *dev, void *args);
void device_get_all_blk_ssize(struct rt_device *dev, void *args);
#endif /* __BLK_DFS_H__ */

View File

@@ -0,0 +1,154 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-02-25 GuEe-GUI the first version
*/
#define DBG_TAG "blk.part"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
#include "blk_partition.h"
static rt_err_t (*partition_list[])(struct rt_blk_disk *) =
{
#ifdef RT_BLK_PARTITION_EFI
efi_partition,
#endif
#ifdef RT_BLK_PARTITION_DFS
dfs_partition,
#endif
};
rt_err_t blk_put_partition(struct rt_blk_disk *disk, const char *type,
rt_size_t start, rt_size_t count, int partno)
{
rt_err_t err;
struct rt_blk_device *blk = rt_calloc(1, sizeof(*blk));
if (type && rt_strcmp(type, "dfs"))
{
rt_uint32_t ssz = rt_blk_disk_get_logical_block_size(disk);
rt_kprintf("found part[%u], begin: %lu, size: ", partno, start * ssz);
if ((count >> 11) == 0)
{
rt_kprintf("%u%cB\n", count >> 1, 'K'); /* KB */
}
else
{
rt_uint32_t size_mb = count >> 11; /* MB */
if ((size_mb >> 10) == 0)
{
rt_kprintf("%u.%u%cB\n", size_mb, (count >> 1) & 0x3ff, 'M');
}
else
{
rt_kprintf("%u.%u%cB\n", size_mb >> 10, size_mb & 0x3ff, 'G');
}
}
}
if (!blk)
{
err = -RT_ENOMEM;
goto _fail;
}
err = blk_dev_initialize(blk);
if (err)
{
goto _fail;
}
blk->partno = partno;
blk->sector_start = start;
blk->sector_count = count;
blk->partition.offset = start;
blk->partition.size = count;
blk->partition.lock = &disk->usr_lock;
err = disk_add_blk_dev(disk, blk);
if (err)
{
goto _fail;
}
++disk->partitions;
return RT_EOK;
_fail:
LOG_E("%s: Put partition.%s[%u] start = %lu count = %lu error = %s",
to_disk_name(disk), type, partno, start, count, rt_strerror(err));
if (blk)
{
rt_free(blk);
}
return err;
}
rt_err_t rt_blk_disk_probe_partition(struct rt_blk_disk *disk)
{
rt_err_t err = RT_EOK;
if (!disk)
{
return -RT_EINVAL;
}
LOG_D("%s: Probing disk partitions", to_disk_name(disk));
if (disk->partitions)
{
return err;
}
err = -RT_EEMPTY;
if (disk->max_partitions == RT_BLK_PARTITION_NONE)
{
LOG_D("%s: Unsupported partitions", to_disk_name(disk));
return err;
}
for (int i = 0; i < RT_ARRAY_SIZE(partition_list); ++i)
{
rt_err_t part_err = partition_list[i](disk);
if (part_err == -RT_ENOMEM)
{
err = part_err;
break;
}
if (!part_err)
{
err = RT_EOK;
break;
}
}
if ((err && err != -RT_ENOMEM) || disk->partitions == 0)
{
/* No partition found */
rt_size_t total_sectors = rt_blk_disk_get_capacity(disk);
err = blk_put_partition(disk, RT_NULL, 0, total_sectors, 0);
}
return err;
}

View File

@@ -0,0 +1,22 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-02-25 GuEe-GUI first version
*/
#ifndef __BLK_PARTITION_H__
#define __BLK_PARTITION_H__
#include "blk_dev.h"
rt_err_t blk_put_partition(struct rt_blk_disk *disk, const char *type,
rt_size_t start, rt_size_t count, int partno);
rt_err_t dfs_partition(struct rt_blk_disk *disk);
rt_err_t efi_partition(struct rt_blk_disk *disk);
#endif /* __BLK_PARTITION_H__ */

View File

@@ -0,0 +1,12 @@
menu "Partition Types"
config RT_BLK_PARTITION_DFS
bool "DFS Partition support"
depends on RT_USING_DFS
default y
config RT_BLK_PARTITION_EFI
bool "EFI Globally Unique Identifier (GUID) Partition support"
default y
endmenu

View File

@@ -0,0 +1,18 @@
from building import *
group = []
cwd = GetCurrentDir()
CPPPATH = [cwd + '/../../include']
src = []
if GetDepend(['RT_BLK_PARTITION_DFS']):
src += ['dfs.c']
if GetDepend(['RT_BLK_PARTITION_EFI']):
src += ['efi.c']
group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH)
Return('group')

View File

@@ -0,0 +1,55 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2011-07-25 weety first version
* 2023-02-25 GuEe-GUI make blk interface
*/
#include "efi.h"
#define DBG_TAG "blk.part.dfs"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
rt_err_t dfs_partition(struct rt_blk_disk *disk)
{
rt_ssize_t res;
struct dfs_partition part;
rt_uint8_t *sector = rt_malloc(rt_blk_disk_get_logical_block_size(disk));
if (!sector)
{
return -RT_ENOMEM;
}
res = disk->ops->read(disk, 0, sector, 1);
if (res < 0)
{
rt_free(sector);
return res;
}
for (rt_size_t i = 0; i < disk->max_partitions; ++i)
{
res = dfs_filesystem_get_partition(&part, sector, i);
if (res)
{
break;
}
if (blk_put_partition(disk, "dfs", part.offset, part.size, i) == -RT_ENOMEM)
{
break;
}
}
rt_free(sector);
return RT_EOK;
}

View File

@@ -0,0 +1,738 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2022-05-05 linzhenxing first version
* 2023-02-25 GuEe-GUI make blk interface
*/
#include "efi.h"
#define DBG_TAG "blk.part.efi"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
static rt_bool_t force_gpt = 0;
static int force_gpt_setup(void)
{
#ifdef RT_USING_OFW
force_gpt = !!rt_ofw_bootargs_select("gpt", 0);
#endif
return 0;
}
INIT_CORE_EXPORT(force_gpt_setup);
/**
* @brief This function is EFI version of crc32 function.
*
* @param buf the buffer to calculate crc32 of.
* @param len the length of buf.
* @return EFI-style CRC32 value for @buf.
*/
rt_inline rt_uint32_t efi_crc32(const rt_uint8_t *buf, rt_size_t len)
{
rt_ubase_t crc = 0xffffffffUL;
for (rt_size_t i = 0; i < len; ++i)
{
crc ^= buf[i];
for (int j = 0; j < 8; ++j)
{
crc = (crc >> 1) ^ ((crc & 1) ? 0xedb88320L : 0);
}
}
return ~crc;
}
/**
* @brief This function get number of last logical block of device.
*
* @param disk the blk of disk.
* @return last LBA value on success, 0 on error.
* This is stored (by sd and ide-geometry) in
* the part[0] entry for this disk, and is the number of
* physical sectors available on the disk.
*/
static rt_size_t last_lba(struct rt_blk_disk *disk)
{
return rt_blk_disk_get_capacity(disk) - 1ULL;
}
rt_inline int pmbr_part_valid(gpt_mbr_record *part)
{
if (part->os_type != EFI_PMBR_OSTYPE_EFI_GPT)
{
return 0;
}
/* set to 0x00000001 (i.e., the LBA of the GPT Partition Header) */
if (rt_le32_to_cpu(part->starting_lba) != GPT_PRIMARY_PARTITION_TABLE_LBA)
{
return 0;
}
return GPT_MBR_PROTECTIVE;
}
/**
* @brief This function test Protective MBR for validity.
*
* @param mbr the pointer to a legacy mbr structure.
* @param total_sectors the amount of sectors in the device
* @return
* 0 -> Invalid MBR
* 1 -> GPT_MBR_PROTECTIVE
* 2 -> GPT_MBR_HYBRID
*/
static int is_pmbr_valid(legacy_mbr *mbr, rt_size_t total_sectors)
{
rt_uint32_t sz = 0;
int part = 0, ret = 0; /* invalid by default */
if (!mbr || rt_le16_to_cpu(mbr->signature) != MSDOS_MBR_SIGNATURE)
{
goto _done;
}
for (int i = 0; i < 4; ++i)
{
ret = pmbr_part_valid(&mbr->partition_record[i]);
if (ret == GPT_MBR_PROTECTIVE)
{
part = i;
/*
* Ok, we at least know that there's a protective MBR,
* now check if there are other partition types for
* hybrid MBR.
*/
goto _check_hybrid;
}
}
if (ret != GPT_MBR_PROTECTIVE)
{
goto _done;
}
_check_hybrid:
for (int i = 0; i < 4; i++)
{
if (mbr->partition_record[i].os_type != EFI_PMBR_OSTYPE_EFI_GPT &&
mbr->partition_record[i].os_type != 0x00)
{
ret = GPT_MBR_HYBRID;
}
}
/*
* Protective MBRs take up the lesser of the whole disk
* or 2 TiB (32bit LBA), ignoring the rest of the disk.
* Some partitioning programs, nonetheless, choose to set
* the size to the maximum 32-bit limitation, disregarding
* the disk size.
*
* Hybrid MBRs do not necessarily comply with this.
*
* Consider a bad value here to be a warning to support dd'ing
* an image from a smaller disk to a larger disk.
*/
if (ret == GPT_MBR_PROTECTIVE)
{
sz = rt_le32_to_cpu(mbr->partition_record[part].size_in_lba);
if (sz != (rt_uint32_t)total_sectors - 1 && sz != 0xffffffff)
{
LOG_W("GPT: mbr size in lba (%u) different than whole disk (%u)",
sz, rt_min_t(rt_uint32_t, total_sectors - 1, 0xffffffff));
}
}
_done:
return ret;
}
/**
* @brief This function read bytes from disk, starting at given LBA.
*
* @param disk the blk of disk.
* @param lba the Logical Block Address of the partition table.
* @param buffer the destination buffer.
* @param count the bytes to read.
* @return number of bytes read on success, 0 on error.
*/
static rt_size_t read_lba(struct rt_blk_disk *disk,
rt_uint64_t lba, rt_uint8_t *buffer, rt_size_t count)
{
rt_size_t totalreadcount = 0;
if (!buffer || lba > last_lba(disk))
{
return 0;
}
for (rt_uint64_t n = lba; count; ++n)
{
int copied = 512;
disk->ops->read(disk, n, buffer, 1);
if (copied > count)
{
copied = count;
}
buffer += copied;
totalreadcount += copied;
count -= copied;
}
return totalreadcount;
}
/**
* @brief This function reads partition entries from disk.
*
* @param disk the blk of disk.
* @param gpt the GPT header
* @return ptes on success, null on error.
*/
static gpt_entry *alloc_read_gpt_entries(struct rt_blk_disk *disk,
gpt_header *gpt)
{
rt_size_t count;
gpt_entry *pte;
rt_uint64_t entry_lba;
if (!gpt)
{
return RT_NULL;
}
count = (rt_size_t)rt_le32_to_cpu(gpt->num_partition_entries) *
rt_le32_to_cpu(gpt->sizeof_partition_entry);
if (!count)
{
return RT_NULL;
}
pte = rt_malloc(count);
if (!pte)
{
return RT_NULL;
}
entry_lba = rt_le64_to_cpu(gpt->partition_entry_lba);
if (read_lba(disk, entry_lba, (rt_uint8_t *)pte, count) < count)
{
rt_free(pte);
pte = RT_NULL;
return RT_NULL;
}
/* Remember to free pte when done */
return pte;
}
/**
* @brief This function allocates GPT header, reads into it from disk.
*
* @param disk the blk of disk.
* @param lba the Logical Block Address of the partition table
* @return GPT header on success, null on error.
*/
static gpt_header *alloc_read_gpt_header(struct rt_blk_disk *disk, rt_uint64_t lba)
{
gpt_header *gpt;
rt_uint32_t ssz = rt_blk_disk_get_logical_block_size(disk);
gpt = rt_malloc(ssz);
if (!gpt)
{
return RT_NULL;
}
if (read_lba(disk, lba, (rt_uint8_t *)gpt, ssz) < ssz)
{
rt_free(gpt);
gpt = RT_NULL;
return RT_NULL;
}
/* Remember to free gpt when finished with it */
return gpt;
}
/**
* @brief This function tests one GPT header and PTEs for validity.
*
* @param disk the blk of disk.
* @param lba the Logical Block Address of the GPT header to test.
* @param gpt the GPT header ptr, filled on return.
* @param ptes the PTEs ptr, filled on return.
* @returns true if valid, false on error.
* If valid, returns pointers to newly allocated GPT header and PTEs.
*/
static rt_bool_t is_gpt_valid(struct rt_blk_disk *disk,
rt_uint64_t lba, gpt_header **gpt, gpt_entry **ptes)
{
rt_uint32_t crc, origcrc;
rt_uint64_t lastlba, pt_size;
rt_ssize_t logical_block_size;
if (!ptes)
{
return RT_FALSE;
}
if (!(*gpt = alloc_read_gpt_header(disk, lba)))
{
return RT_FALSE;
}
/* Check the GUID Partition Table signature */
if (rt_le64_to_cpu((*gpt)->signature) != GPT_HEADER_SIGNATURE)
{
LOG_D("%s: GUID Partition Table Header signature is wrong: %lld != %lld",
to_disk_name(disk),
(rt_uint64_t)rt_le64_to_cpu((*gpt)->signature),
(rt_uint64_t)GPT_HEADER_SIGNATURE);
goto _fail;
}
/* Check the GUID Partition Table header size is too big */
logical_block_size = rt_blk_disk_get_logical_block_size(disk);
if (rt_le32_to_cpu((*gpt)->header_size) > logical_block_size)
{
LOG_D("%s: GUID Partition Table Header size is too large: %u > %u",
to_disk_name(disk),
rt_le32_to_cpu((*gpt)->header_size),
logical_block_size);
goto _fail;
}
/* Check the GUID Partition Table header size is too small */
if (rt_le32_to_cpu((*gpt)->header_size) < sizeof(gpt_header))
{
LOG_D("%s: GUID Partition Table Header size is too small: %u < %u",
to_disk_name(disk),
rt_le32_to_cpu((*gpt)->header_size),
sizeof(gpt_header));
goto _fail;
}
/* Check the GUID Partition Table CRC */
origcrc = rt_le32_to_cpu((*gpt)->header_crc32);
(*gpt)->header_crc32 = 0;
crc = efi_crc32((const rt_uint8_t *)(*gpt), rt_le32_to_cpu((*gpt)->header_size));
if (crc != origcrc)
{
LOG_D("%s: GUID Partition Table Header CRC is wrong: %x != %x",
to_disk_name(disk), crc, origcrc);
goto _fail;
}
(*gpt)->header_crc32 = rt_cpu_to_le32(origcrc);
/*
* Check that the start_lba entry points to the LBA that contains
* the GUID Partition Table
*/
if (rt_le64_to_cpu((*gpt)->start_lba) != lba)
{
LOG_D("%s: GPT start_lba incorrect: %lld != %lld",
to_disk_name(disk),
(rt_uint64_t)rt_le64_to_cpu((*gpt)->start_lba),
(rt_uint64_t)lba);
goto _fail;
}
/* Check the first_usable_lba and last_usable_lba are within the disk */
lastlba = last_lba(disk);
if (rt_le64_to_cpu((*gpt)->first_usable_lba) > lastlba)
{
LOG_D("%s: GPT: first_usable_lba incorrect: %lld > %lld",
to_disk_name(disk),
(rt_uint64_t)rt_le64_to_cpu((*gpt)->first_usable_lba),
(rt_uint64_t)lastlba);
goto _fail;
}
if (rt_le64_to_cpu((*gpt)->last_usable_lba) > lastlba)
{
LOG_D("%s: GPT: last_usable_lba incorrect: %lld > %lld",
to_disk_name(disk),
(rt_uint64_t)rt_le64_to_cpu((*gpt)->last_usable_lba),
(rt_uint64_t)lastlba);
goto _fail;
}
if (rt_le64_to_cpu((*gpt)->last_usable_lba) < rt_le64_to_cpu((*gpt)->first_usable_lba))
{
LOG_D("%s: GPT: last_usable_lba incorrect: %lld > %lld",
to_disk_name(disk),
(rt_uint64_t)rt_le64_to_cpu((*gpt)->last_usable_lba),
(rt_uint64_t)rt_le64_to_cpu((*gpt)->first_usable_lba));
goto _fail;
}
/* Check that sizeof_partition_entry has the correct value */
if (rt_le32_to_cpu((*gpt)->sizeof_partition_entry) != sizeof(gpt_entry))
{
LOG_D("%s: GUID Partition Entry Size check failed", to_disk_name(disk));
goto _fail;
}
/* Sanity check partition table size */
pt_size = (rt_uint64_t)rt_le32_to_cpu((*gpt)->num_partition_entries) *
rt_le32_to_cpu((*gpt)->sizeof_partition_entry);
if (!(*ptes = alloc_read_gpt_entries(disk, *gpt)))
{
goto _fail;
}
/* Check the GUID Partition Entry Array CRC */
crc = efi_crc32((const rt_uint8_t *)(*ptes), pt_size);
if (crc != rt_le32_to_cpu((*gpt)->partition_entry_array_crc32))
{
LOG_D("%s: GUID Partition Entry Array CRC check failed", to_disk_name(disk));
goto _fail_ptes;
}
/* We're done, all's well */
return RT_TRUE;
_fail_ptes:
rt_free(*ptes);
*ptes = RT_NULL;
_fail:
rt_free(*gpt);
*gpt = RT_NULL;
return RT_FALSE;
}
/**
* @brief This function tests one PTE for validity.
*
* @param pte the pte to check.
* @param lastlba the last lba of the disk.
* @return valid boolean of pte.
*/
rt_inline rt_bool_t is_pte_valid(const gpt_entry *pte, const rt_size_t lastlba)
{
if ((!efi_guidcmp(pte->partition_type_guid, NULL_GUID)) ||
rt_le64_to_cpu(pte->starting_lba) > lastlba ||
rt_le64_to_cpu(pte->ending_lba) > lastlba)
{
return RT_FALSE;
}
return RT_TRUE;
}
/**
* @brief This function search disk for valid GPT headers and PTEs.
*
* @param disk the blk of disk.
* @param pgpt the primary GPT header.
* @param agpt the alternate GPT header.
* @param lastlba the last LBA number.
*/
static void compare_gpts(struct rt_blk_disk *disk,
gpt_header *pgpt, gpt_header *agpt, rt_uint64_t lastlba)
{
int error_found = 0;
if (!pgpt || !agpt)
{
return;
}
if (rt_le64_to_cpu(pgpt->start_lba) != rt_le64_to_cpu(agpt->alternate_lba))
{
LOG_W("%s: GPT:Primary header LBA(%lld) != Alt(%lld), header alternate_lba",
to_disk_name(disk),
(rt_uint64_t)rt_le64_to_cpu(pgpt->start_lba),
(rt_uint64_t)rt_le64_to_cpu(agpt->alternate_lba));
++error_found;
}
if (rt_le64_to_cpu(pgpt->alternate_lba) != rt_le64_to_cpu(agpt->start_lba))
{
LOG_W("%s: GPT:Primary header alternate_lba(%lld) != Alt(%lld), header start_lba",
to_disk_name(disk),
(rt_uint64_t)rt_le64_to_cpu(pgpt->alternate_lba),
(rt_uint64_t)rt_le64_to_cpu(agpt->start_lba));
++error_found;
}
if (rt_le64_to_cpu(pgpt->first_usable_lba) != rt_le64_to_cpu(agpt->first_usable_lba))
{
LOG_W("%s: GPT:first_usable_lbas don't match %lld != %lld",
to_disk_name(disk),
(rt_uint64_t)rt_le64_to_cpu(pgpt->first_usable_lba),
(rt_uint64_t)rt_le64_to_cpu(agpt->first_usable_lba));
++error_found;
}
if (rt_le64_to_cpu(pgpt->last_usable_lba) != rt_le64_to_cpu(agpt->last_usable_lba))
{
LOG_W("%s: GPT:last_usable_lbas don't match %lld != %lld",
to_disk_name(disk),
(rt_uint64_t)rt_le64_to_cpu(pgpt->last_usable_lba),
(rt_uint64_t)rt_le64_to_cpu(agpt->last_usable_lba));
++error_found;
}
if (efi_guidcmp(pgpt->disk_guid, agpt->disk_guid))
{
LOG_W("%s: GPT:disk_guids don't match", to_disk_name(disk));
++error_found;
}
if (rt_le32_to_cpu(pgpt->num_partition_entries) !=
rt_le32_to_cpu(agpt->num_partition_entries))
{
LOG_W("%s: GPT:num_partition_entries don't match: 0x%x != 0x%x",
to_disk_name(disk),
rt_le32_to_cpu(pgpt->num_partition_entries),
rt_le32_to_cpu(agpt->num_partition_entries));
++error_found;
}
if (rt_le32_to_cpu(pgpt->sizeof_partition_entry) !=
rt_le32_to_cpu(agpt->sizeof_partition_entry))
{
LOG_W("%s: GPT:sizeof_partition_entry values don't match: 0x%x != 0x%x",
to_disk_name(disk),
rt_le32_to_cpu(pgpt->sizeof_partition_entry),
rt_le32_to_cpu(agpt->sizeof_partition_entry));
++error_found;
}
if (rt_le32_to_cpu(pgpt->partition_entry_array_crc32) !=
rt_le32_to_cpu(agpt->partition_entry_array_crc32))
{
LOG_W("%s: GPT:partition_entry_array_crc32 values don't match: 0x%x != 0x%x",
to_disk_name(disk),
rt_le32_to_cpu(pgpt->partition_entry_array_crc32),
rt_le32_to_cpu(agpt->partition_entry_array_crc32));
++error_found;
}
if (rt_le64_to_cpu(pgpt->alternate_lba) != lastlba)
{
LOG_W("%s: GPT:Primary header thinks Alt. header is not at the end of the disk: %lld != %lld",
to_disk_name(disk),
(rt_uint64_t)rt_le64_to_cpu(pgpt->alternate_lba),
(rt_uint64_t)lastlba);
++error_found;
}
if (rt_le64_to_cpu(agpt->start_lba) != lastlba)
{
LOG_W("%s: GPT:Alternate GPT header not at the end of the disk: %lld != %lld",
to_disk_name(disk),
(rt_uint64_t)rt_le64_to_cpu(agpt->start_lba),
(rt_uint64_t)lastlba);
++error_found;
}
if (error_found)
{
LOG_W("GPT: Use GNU Parted to correct GPT errors");
}
}
/**
* @brief This function search disk for valid GPT headers and PTEs.
*
* @param disk the disk parsed partitions.
* @param gpt the GPT header ptr, filled on return.
* @param ptes the PTEs ptr, filled on return.
* @return 1 if valid, 0 on error.
* If valid, returns pointers to newly allocated GPT header and PTEs.
* Validity depends on PMBR being valid (or being overridden by the
* 'gpt' kernel command line option) and finding either the Primary
* GPT header and PTEs valid, or the Alternate GPT header and PTEs
* valid. If the Primary GPT header is not valid, the Alternate GPT header
* is not checked unless the 'gpt' kernel command line option is passed.
* This protects against devices which misreport their size, and forces
* the user to decide to use the Alternate GPT.
*/
static rt_bool_t find_valid_gpt(struct rt_blk_disk *disk,
gpt_header **gpt, gpt_entry **ptes)
{
int good_pgpt = 0, good_agpt = 0, good_pmbr = 0;
gpt_header *pgpt = RT_NULL, *agpt = RT_NULL;
gpt_entry *pptes = RT_NULL, *aptes = RT_NULL;
legacy_mbr *legacymbr;
rt_size_t total_sectors = rt_blk_disk_get_capacity(disk);
rt_size_t lastlba;
if (!ptes)
{
return RT_FALSE;
}
lastlba = last_lba(disk);
if (!force_gpt)
{
/* This will be added to the EFI Spec. per Intel after v1.02. */
legacymbr = rt_malloc(sizeof(*legacymbr));
if (!legacymbr)
{
return RT_FALSE;
}
read_lba(disk, 0, (rt_uint8_t *)legacymbr, sizeof(*legacymbr));
good_pmbr = is_pmbr_valid(legacymbr, total_sectors);
rt_free(legacymbr);
if (!good_pmbr)
{
return RT_FALSE;
}
LOG_D("%s: Device has a %s MBR", to_disk_name(disk),
good_pmbr == GPT_MBR_PROTECTIVE ? "protective" : "hybrid");
}
good_pgpt = is_gpt_valid(disk, GPT_PRIMARY_PARTITION_TABLE_LBA, &pgpt, &pptes);
if (good_pgpt)
{
good_agpt = is_gpt_valid(disk, rt_le64_to_cpu(pgpt->alternate_lba), &agpt, &aptes);
}
if (!good_agpt && force_gpt)
{
good_agpt = is_gpt_valid(disk, lastlba, &agpt, &aptes);
}
/* The obviously unsuccessful case */
if (!good_pgpt && !good_agpt)
{
goto _fail;
}
compare_gpts(disk, pgpt, agpt, lastlba);
/* The good cases */
if (good_pgpt)
{
*gpt = pgpt;
*ptes = pptes;
rt_free(agpt);
rt_free(aptes);
if (!good_agpt)
{
LOG_D("%s: Alternate GPT is invalid, using primary GPT", to_disk_name(disk));
}
return RT_TRUE;
}
else if (good_agpt)
{
*gpt = agpt;
*ptes = aptes;
rt_free(pgpt);
rt_free(pptes);
LOG_D("%s: Primary GPT is invalid, using alternate GPT", to_disk_name(disk));
return RT_TRUE;
}
_fail:
rt_free(pgpt);
rt_free(agpt);
rt_free(pptes);
rt_free(aptes);
*gpt = RT_NULL;
*ptes = RT_NULL;
return RT_FALSE;
}
rt_err_t efi_partition(struct rt_blk_disk *disk)
{
rt_uint32_t entries_nr;
gpt_header *gpt = RT_NULL;
gpt_entry *ptes = RT_NULL;
if (!find_valid_gpt(disk, &gpt, &ptes) || !gpt || !ptes)
{
rt_free(gpt);
rt_free(ptes);
return -RT_EINVAL;
}
entries_nr = rt_le32_to_cpu(gpt->num_partition_entries);
for (int i = 0; i < entries_nr && i < disk->max_partitions; ++i)
{
rt_uint64_t start = rt_le64_to_cpu(ptes[i].starting_lba);
rt_uint64_t size = rt_le64_to_cpu(ptes[i].ending_lba) -
rt_le64_to_cpu(ptes[i].starting_lba) + 1ULL;
if (!is_pte_valid(&ptes[i], last_lba(disk)))
{
continue;
}
if (blk_put_partition(disk, "gpt", start, size, i) == -RT_ENOMEM)
{
break;
}
}
rt_free(gpt);
rt_free(ptes);
return RT_EOK;
}

View File

@@ -0,0 +1,141 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2022-05-05 linzhenxing first version
* 2023-02-25 GuEe-GUI make blk interface
*/
#ifndef __PARTITIONS_EFI_H__
#define __PARTITIONS_EFI_H__
#include "../blk_partition.h"
#include <drivers/misc.h>
#include <drivers/byteorder.h>
#define MSDOS_MBR_SIGNATURE 0xaa55
#define EFI_PMBR_OSTYPE_EFI 0xef
#define EFI_PMBR_OSTYPE_EFI_GPT 0xee
#define GPT_MBR_PROTECTIVE 1
#define GPT_MBR_HYBRID 2
#define GPT_HEADER_SIGNATURE 0x5452415020494645ULL
#define GPT_HEADER_REVISION_V1 0x00010000
#define GPT_PRIMARY_PARTITION_TABLE_LBA 1
#ifndef __UUID_H__
#define UUID_SIZE 16
typedef struct
{
rt_uint8_t b[UUID_SIZE];
} guid_t;
#endif /* __UUID_H__ */
#ifndef __EFI_H__
typedef guid_t efi_guid_t rt_align(4);
#define EFI_GUID(a, b, c, d...) (efi_guid_t) \
{{ \
(a) & 0xff, ((a) >> 8) & 0xff, ((a) >> 16) & 0xff, ((a) >> 24) & 0xff, \
(b) & 0xff, ((b) >> 8) & 0xff, \
(c) & 0xff, ((c) >> 8) & 0xff, \
d \
}}
#define NULL_GUID \
EFI_GUID(0x00000000, 0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)
rt_inline int efi_guidcmp(efi_guid_t left, efi_guid_t right)
{
return rt_memcmp(&left, &right, sizeof (efi_guid_t));
}
#endif /* __EFI_H__ */
#define PARTITION_SYSTEM_GUID \
EFI_GUID(0xc12a7328, 0xf81f, 0x11d2, 0xba, 0x4b, 0x00, 0xa0, 0xc9, 0x3e, 0xc9, 0x3b)
#define LEGACY_MBR_PARTITION_GUID \
EFI_GUID(0x024dee41, 0x33e7, 0x11d3, 0x9d, 0x69, 0x00, 0x08, 0xc7, 0x81, 0xf3, 0x9f)
#define PARTITION_MSFT_RESERVED_GUID \
EFI_GUID(0xe3c9e316, 0x0b5c, 0x4db8, 0x81, 0x7d, 0xf9, 0x2d, 0xf0, 0x02, 0x15, 0xae)
#define PARTITION_BASIC_DATA_GUID \
EFI_GUID(0xebd0a0a2, 0xb9e5, 0x4433, 0x87, 0xc0, 0x68, 0xb6, 0xb7, 0x26, 0x99, 0xc7)
rt_packed(struct _gpt_header
{
rt_le64_t signature;
rt_le32_t revision;
rt_le32_t header_size;
rt_le32_t header_crc32;
rt_le32_t reserved1;
rt_le64_t start_lba;
rt_le64_t alternate_lba;
rt_le64_t first_usable_lba;
rt_le64_t last_usable_lba;
efi_guid_t disk_guid;
rt_le64_t partition_entry_lba;
rt_le32_t num_partition_entries;
rt_le32_t sizeof_partition_entry;
rt_le32_t partition_entry_array_crc32;
/*
* The rest of the logical block is reserved by UEFI and must be zero.
* EFI standard handles this by:
*
* uint8_t reserved2[BlockSize - 92];
*/
});
typedef struct _gpt_header gpt_header;
rt_packed(struct _gpt_entry_attributes
{
rt_uint64_t required_to_function:1;
rt_uint64_t reserved:47;
rt_uint64_t type_guid_specific:16;
});
typedef struct _gpt_entry_attributes gpt_entry_attributes;
rt_packed(struct _gpt_entry
{
efi_guid_t partition_type_guid;
efi_guid_t unique_partition_guid;
rt_le64_t starting_lba;
rt_le64_t ending_lba;
gpt_entry_attributes attributes;
rt_le16_t partition_name[72/sizeof(rt_le16_t)];
});
typedef struct _gpt_entry gpt_entry;
rt_packed(struct _gpt_mbr_record
{
rt_uint8_t boot_indicator; /* unused by EFI, set to 0x80 for bootable */
rt_uint8_t start_head; /* unused by EFI, pt start in CHS */
rt_uint8_t start_sector; /* unused by EFI, pt start in CHS */
rt_uint8_t start_track;
rt_uint8_t os_type; /* EFI and legacy non-EFI OS types */
rt_uint8_t end_head; /* unused by EFI, pt end in CHS */
rt_uint8_t end_sector; /* unused by EFI, pt end in CHS */
rt_uint8_t end_track; /* unused by EFI, pt end in CHS */
rt_le32_t starting_lba; /* used by EFI - start addr of the on disk pt */
rt_le32_t size_in_lba; /* used by EFI - size of pt in LBA */
});
typedef struct _gpt_mbr_record gpt_mbr_record;
rt_packed(struct _legacy_mbr
{
rt_uint8_t boot_code[440];
rt_le32_t unique_mbr_signature;
rt_le16_t unknown;
gpt_mbr_record partition_record[4];
rt_le16_t signature;
});
typedef struct _legacy_mbr legacy_mbr;
#endif /* __PARTITIONS_EFI_H__ */

View File

@@ -0,0 +1,974 @@
/*
* Copyright (c) 2006-2024 RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2015-05-14 aubrcool@qq.com first version
* 2015-07-06 Bernard code cleanup and remove RT_CAN_USING_LED;
*/
#include <rthw.h>
#include <rtthread.h>
#include <rtdevice.h>
#define CAN_LOCK(can) rt_mutex_take(&(can->lock), RT_WAITING_FOREVER)
#define CAN_UNLOCK(can) rt_mutex_release(&(can->lock))
static rt_err_t rt_can_init(struct rt_device *dev)
{
rt_err_t result = RT_EOK;
struct rt_can_device *can;
RT_ASSERT(dev != RT_NULL);
can = (struct rt_can_device *)dev;
/* initialize rx/tx */
can->can_rx = RT_NULL;
can->can_tx = RT_NULL;
#ifdef RT_CAN_USING_HDR
can->hdr = RT_NULL;
#endif
/* apply configuration */
if (can->ops->configure)
result = can->ops->configure(can, &can->config);
else
result = -RT_ENOSYS;
return result;
}
/*
* can interrupt routines
*/
rt_inline rt_ssize_t _can_int_rx(struct rt_can_device *can, struct rt_can_msg *data, rt_ssize_t msgs)
{
rt_ssize_t size;
struct rt_can_rx_fifo *rx_fifo;
RT_ASSERT(can != RT_NULL);
size = msgs;
rx_fifo = (struct rt_can_rx_fifo *) can->can_rx;
RT_ASSERT(rx_fifo != RT_NULL);
/* read from software FIFO */
while (msgs / sizeof(struct rt_can_msg) > 0)
{
rt_base_t level;
#ifdef RT_CAN_USING_HDR
rt_int8_t hdr;
#endif /*RT_CAN_USING_HDR*/
struct rt_can_msg_list *listmsg = RT_NULL;
/* disable interrupt */
level = rt_hw_interrupt_disable();
#ifdef RT_CAN_USING_HDR
hdr = data->hdr_index;
if (hdr >= 0 && can->hdr && hdr < can->config.maxhdr && !rt_list_isempty(&can->hdr[hdr].list))
{
listmsg = rt_list_entry(can->hdr[hdr].list.next, struct rt_can_msg_list, hdrlist);
rt_list_remove(&listmsg->list);
rt_list_remove(&listmsg->hdrlist);
if (can->hdr[hdr].msgs)
{
can->hdr[hdr].msgs--;
}
listmsg->owner = RT_NULL;
}
else if (hdr == -1)
#endif /*RT_CAN_USING_HDR*/
{
if (!rt_list_isempty(&rx_fifo->uselist))
{
listmsg = rt_list_entry(rx_fifo->uselist.next, struct rt_can_msg_list, list);
rt_list_remove(&listmsg->list);
#ifdef RT_CAN_USING_HDR
rt_list_remove(&listmsg->hdrlist);
if (listmsg->owner != RT_NULL && listmsg->owner->msgs)
{
listmsg->owner->msgs--;
}
listmsg->owner = RT_NULL;
#endif /*RT_CAN_USING_HDR*/
}
else
{
/* no data, enable interrupt and break out */
rt_hw_interrupt_enable(level);
break;
}
}
/* enable interrupt */
rt_hw_interrupt_enable(level);
if (listmsg != RT_NULL)
{
rt_memcpy(data, &listmsg->data, sizeof(struct rt_can_msg));
level = rt_hw_interrupt_disable();
rt_list_insert_before(&rx_fifo->freelist, &listmsg->list);
rx_fifo->freenumbers++;
RT_ASSERT(rx_fifo->freenumbers <= can->config.msgboxsz);
rt_hw_interrupt_enable(level);
listmsg = RT_NULL;
}
else
{
break;
}
data ++;
msgs -= sizeof(struct rt_can_msg);
}
return (size - msgs);
}
rt_inline int _can_int_tx(struct rt_can_device *can, const struct rt_can_msg *data, int msgs)
{
int size;
struct rt_can_tx_fifo *tx_fifo;
RT_ASSERT(can != RT_NULL);
size = msgs;
tx_fifo = (struct rt_can_tx_fifo *) can->can_tx;
RT_ASSERT(tx_fifo != RT_NULL);
while (msgs)
{
rt_base_t level;
rt_uint32_t no;
rt_uint32_t result;
struct rt_can_sndbxinx_list *tx_tosnd = RT_NULL;
rt_sem_take(&(tx_fifo->sem), RT_WAITING_FOREVER);
level = rt_hw_interrupt_disable();
tx_tosnd = rt_list_entry(tx_fifo->freelist.next, struct rt_can_sndbxinx_list, list);
RT_ASSERT(tx_tosnd != RT_NULL);
rt_list_remove(&tx_tosnd->list);
rt_hw_interrupt_enable(level);
no = ((rt_ubase_t)tx_tosnd - (rt_ubase_t)tx_fifo->buffer) / sizeof(struct rt_can_sndbxinx_list);
tx_tosnd->result = RT_CAN_SND_RESULT_WAIT;
rt_completion_init(&tx_tosnd->completion);
if (can->ops->sendmsg(can, data, no) != RT_EOK)
{
/* send failed. */
level = rt_hw_interrupt_disable();
rt_list_insert_before(&tx_fifo->freelist, &tx_tosnd->list);
rt_hw_interrupt_enable(level);
rt_sem_release(&(tx_fifo->sem));
goto err_ret;
}
can->status.sndchange = 1;
rt_completion_wait(&(tx_tosnd->completion), RT_WAITING_FOREVER);
level = rt_hw_interrupt_disable();
result = tx_tosnd->result;
if (!rt_list_isempty(&tx_tosnd->list))
{
rt_list_remove(&tx_tosnd->list);
}
rt_list_insert_before(&tx_fifo->freelist, &tx_tosnd->list);
rt_hw_interrupt_enable(level);
rt_sem_release(&(tx_fifo->sem));
if (result == RT_CAN_SND_RESULT_OK)
{
level = rt_hw_interrupt_disable();
can->status.sndpkg++;
rt_hw_interrupt_enable(level);
data ++;
msgs -= sizeof(struct rt_can_msg);
if (!msgs) break;
}
else
{
err_ret:
level = rt_hw_interrupt_disable();
can->status.dropedsndpkg++;
rt_hw_interrupt_enable(level);
break;
}
}
return (size - msgs);
}
rt_inline int _can_int_tx_priv(struct rt_can_device *can, const struct rt_can_msg *data, int msgs)
{
int size;
rt_base_t level;
rt_uint32_t no, result;
struct rt_can_tx_fifo *tx_fifo;
RT_ASSERT(can != RT_NULL);
size = msgs;
tx_fifo = (struct rt_can_tx_fifo *) can->can_tx;
RT_ASSERT(tx_fifo != RT_NULL);
while (msgs)
{
no = data->priv;
if (no >= can->config.sndboxnumber)
{
break;
}
level = rt_hw_interrupt_disable();
if ((tx_fifo->buffer[no].result != RT_CAN_SND_RESULT_OK))
{
rt_hw_interrupt_enable(level);
rt_completion_wait(&(tx_fifo->buffer[no].completion), RT_WAITING_FOREVER);
continue;
}
tx_fifo->buffer[no].result = RT_CAN_SND_RESULT_WAIT;
rt_hw_interrupt_enable(level);
if (can->ops->sendmsg(can, data, no) != RT_EOK)
{
continue;
}
can->status.sndchange = 1;
rt_completion_wait(&(tx_fifo->buffer[no].completion), RT_WAITING_FOREVER);
result = tx_fifo->buffer[no].result;
if (result == RT_CAN_SND_RESULT_OK)
{
level = rt_hw_interrupt_disable();
can->status.sndpkg++;
rt_hw_interrupt_enable(level);
data ++;
msgs -= sizeof(struct rt_can_msg);
if (!msgs) break;
}
else
{
level = rt_hw_interrupt_disable();
can->status.dropedsndpkg++;
rt_hw_interrupt_enable(level);
break;
}
}
return (size - msgs);
}
static rt_err_t rt_can_open(struct rt_device *dev, rt_uint16_t oflag)
{
struct rt_can_device *can;
char tmpname[16];
RT_ASSERT(dev != RT_NULL);
can = (struct rt_can_device *)dev;
CAN_LOCK(can);
/* get open flags */
dev->open_flag = oflag & 0xff;
if (can->can_rx == RT_NULL)
{
if (oflag & RT_DEVICE_FLAG_INT_RX)
{
int i = 0;
struct rt_can_rx_fifo *rx_fifo;
rx_fifo = (struct rt_can_rx_fifo *) rt_malloc(sizeof(struct rt_can_rx_fifo) +
can->config.msgboxsz * sizeof(struct rt_can_msg_list));
RT_ASSERT(rx_fifo != RT_NULL);
rx_fifo->buffer = (struct rt_can_msg_list *)(rx_fifo + 1);
rt_memset(rx_fifo->buffer, 0, can->config.msgboxsz * sizeof(struct rt_can_msg_list));
rt_list_init(&rx_fifo->freelist);
rt_list_init(&rx_fifo->uselist);
rx_fifo->freenumbers = can->config.msgboxsz;
for (i = 0; i < can->config.msgboxsz; i++)
{
rt_list_insert_before(&rx_fifo->freelist, &rx_fifo->buffer[i].list);
#ifdef RT_CAN_USING_HDR
rt_list_init(&rx_fifo->buffer[i].hdrlist);
rx_fifo->buffer[i].owner = RT_NULL;
#endif
}
can->can_rx = rx_fifo;
dev->open_flag |= RT_DEVICE_FLAG_INT_RX;
/* open can rx interrupt */
can->ops->control(can, RT_DEVICE_CTRL_SET_INT, (void *)RT_DEVICE_FLAG_INT_RX);
}
}
if (can->can_tx == RT_NULL)
{
if (oflag & RT_DEVICE_FLAG_INT_TX)
{
int i = 0;
struct rt_can_tx_fifo *tx_fifo;
tx_fifo = (struct rt_can_tx_fifo *) rt_malloc(sizeof(struct rt_can_tx_fifo) +
can->config.sndboxnumber * sizeof(struct rt_can_sndbxinx_list));
RT_ASSERT(tx_fifo != RT_NULL);
tx_fifo->buffer = (struct rt_can_sndbxinx_list *)(tx_fifo + 1);
rt_memset(tx_fifo->buffer, 0,
can->config.sndboxnumber * sizeof(struct rt_can_sndbxinx_list));
rt_list_init(&tx_fifo->freelist);
for (i = 0; i < can->config.sndboxnumber; i++)
{
rt_list_insert_before(&tx_fifo->freelist, &tx_fifo->buffer[i].list);
rt_completion_init(&(tx_fifo->buffer[i].completion));
tx_fifo->buffer[i].result = RT_CAN_SND_RESULT_OK;
}
rt_sprintf(tmpname, "%stl", dev->parent.name);
rt_sem_init(&(tx_fifo->sem), tmpname, can->config.sndboxnumber, RT_IPC_FLAG_FIFO);
can->can_tx = tx_fifo;
dev->open_flag |= RT_DEVICE_FLAG_INT_TX;
/* open can tx interrupt */
can->ops->control(can, RT_DEVICE_CTRL_SET_INT, (void *)RT_DEVICE_FLAG_INT_TX);
}
}
can->ops->control(can, RT_DEVICE_CTRL_SET_INT, (void *)RT_DEVICE_CAN_INT_ERR);
#ifdef RT_CAN_USING_HDR
if (can->hdr == RT_NULL)
{
int i = 0;
struct rt_can_hdr *phdr;
phdr = (struct rt_can_hdr *) rt_malloc(can->config.maxhdr * sizeof(struct rt_can_hdr));
RT_ASSERT(phdr != RT_NULL);
rt_memset(phdr, 0, can->config.maxhdr * sizeof(struct rt_can_hdr));
for (i = 0; i < can->config.maxhdr; i++)
{
rt_list_init(&phdr[i].list);
}
can->hdr = phdr;
}
#endif
if (!can->timerinitflag)
{
can->timerinitflag = 1;
rt_timer_start(&can->timer);
}
CAN_UNLOCK(can);
return RT_EOK;
}
static rt_err_t rt_can_close(struct rt_device *dev)
{
struct rt_can_device *can;
RT_ASSERT(dev != RT_NULL);
can = (struct rt_can_device *)dev;
CAN_LOCK(can);
/* this device has more reference count */
if (dev->ref_count > 1)
{
CAN_UNLOCK(can);
return RT_EOK;
}
if (can->timerinitflag)
{
can->timerinitflag = 0;
rt_timer_stop(&can->timer);
}
can->status_indicate.ind = RT_NULL;
can->status_indicate.args = RT_NULL;
#ifdef RT_CAN_USING_HDR
if (can->hdr != RT_NULL)
{
rt_free(can->hdr);
can->hdr = RT_NULL;
}
#endif
if (dev->open_flag & RT_DEVICE_FLAG_INT_RX)
{
struct rt_can_rx_fifo *rx_fifo;
/* clear can rx interrupt */
can->ops->control(can, RT_DEVICE_CTRL_CLR_INT, (void *)RT_DEVICE_FLAG_INT_RX);
rx_fifo = (struct rt_can_rx_fifo *)can->can_rx;
RT_ASSERT(rx_fifo != RT_NULL);
rt_free(rx_fifo);
dev->open_flag &= ~RT_DEVICE_FLAG_INT_RX;
can->can_rx = RT_NULL;
}
if (dev->open_flag & RT_DEVICE_FLAG_INT_TX)
{
struct rt_can_tx_fifo *tx_fifo;
/* clear can tx interrupt */
can->ops->control(can, RT_DEVICE_CTRL_CLR_INT, (void *)RT_DEVICE_FLAG_INT_TX);
tx_fifo = (struct rt_can_tx_fifo *)can->can_tx;
RT_ASSERT(tx_fifo != RT_NULL);
rt_sem_detach(&(tx_fifo->sem));
rt_free(tx_fifo);
dev->open_flag &= ~RT_DEVICE_FLAG_INT_TX;
can->can_tx = RT_NULL;
}
can->ops->control(can, RT_DEVICE_CTRL_CLR_INT, (void *)RT_DEVICE_CAN_INT_ERR);
can->ops->control(can, RT_CAN_CMD_START, RT_FALSE);
CAN_UNLOCK(can);
return RT_EOK;
}
static rt_ssize_t rt_can_read(struct rt_device *dev,
rt_off_t pos,
void *buffer,
rt_size_t size)
{
struct rt_can_device *can;
RT_ASSERT(dev != RT_NULL);
if (size == 0) return 0;
can = (struct rt_can_device *)dev;
if ((dev->open_flag & RT_DEVICE_FLAG_INT_RX) && (dev->ref_count > 0))
{
return _can_int_rx(can, buffer, size);
}
return 0;
}
static rt_ssize_t rt_can_write(struct rt_device *dev,
rt_off_t pos,
const void *buffer,
rt_size_t size)
{
struct rt_can_device *can;
RT_ASSERT(dev != RT_NULL);
if (size == 0) return 0;
can = (struct rt_can_device *)dev;
if ((dev->open_flag & RT_DEVICE_FLAG_INT_TX) && (dev->ref_count > 0))
{
if (can->config.privmode)
{
return _can_int_tx_priv(can, buffer, size);
}
else
{
return _can_int_tx(can, buffer, size);
}
}
return 0;
}
static rt_err_t rt_can_control(struct rt_device *dev,
int cmd,
void *args)
{
struct rt_can_device *can;
rt_err_t res;
res = RT_EOK;
RT_ASSERT(dev != RT_NULL);
can = (struct rt_can_device *)dev;
switch (cmd)
{
case RT_DEVICE_CTRL_SUSPEND:
/* suspend device */
dev->flag |= RT_DEVICE_FLAG_SUSPENDED;
break;
case RT_DEVICE_CTRL_RESUME:
/* resume device */
dev->flag &= ~RT_DEVICE_FLAG_SUSPENDED;
break;
case RT_DEVICE_CTRL_CONFIG:
/* configure device */
res = can->ops->configure(can, (struct can_configure *)args);
break;
case RT_CAN_CMD_SET_PRIV:
/* configure device */
if ((rt_uint32_t)(rt_ubase_t)args != can->config.privmode)
{
int i;
rt_base_t level;
struct rt_can_tx_fifo *tx_fifo;
res = can->ops->control(can, cmd, args);
if (res != RT_EOK) return res;
tx_fifo = (struct rt_can_tx_fifo *) can->can_tx;
if (can->config.privmode)
{
for (i = 0; i < can->config.sndboxnumber; i++)
{
level = rt_hw_interrupt_disable();
if(rt_list_isempty(&tx_fifo->buffer[i].list))
{
rt_sem_release(&(tx_fifo->sem));
}
else
{
rt_list_remove(&tx_fifo->buffer[i].list);
}
rt_hw_interrupt_enable(level);
}
}
else
{
for (i = 0; i < can->config.sndboxnumber; i++)
{
level = rt_hw_interrupt_disable();
if (tx_fifo->buffer[i].result == RT_CAN_SND_RESULT_OK)
{
rt_list_insert_before(&tx_fifo->freelist, &tx_fifo->buffer[i].list);
}
rt_hw_interrupt_enable(level);
}
}
}
break;
case RT_CAN_CMD_SET_STATUS_IND:
can->status_indicate.ind = ((rt_can_status_ind_type_t)args)->ind;
can->status_indicate.args = ((rt_can_status_ind_type_t)args)->args;
break;
#ifdef RT_CAN_USING_HDR
case RT_CAN_CMD_SET_FILTER:
res = can->ops->control(can, cmd, args);
if (res != RT_EOK || can->hdr == RT_NULL)
{
return res;
}
struct rt_can_filter_config *pfilter;
struct rt_can_filter_item *pitem;
rt_uint32_t count;
rt_base_t level;
pfilter = (struct rt_can_filter_config *)args;
RT_ASSERT(pfilter);
count = pfilter->count;
pitem = pfilter->items;
if (pfilter->actived)
{
while (count)
{
if (pitem->hdr_bank >= can->config.maxhdr || pitem->hdr_bank < 0)
{
count--;
pitem++;
continue;
}
level = rt_hw_interrupt_disable();
if (!can->hdr[pitem->hdr_bank].connected)
{
rt_hw_interrupt_enable(level);
rt_memcpy(&can->hdr[pitem->hdr_bank].filter, pitem,
sizeof(struct rt_can_filter_item));
level = rt_hw_interrupt_disable();
can->hdr[pitem->hdr_bank].connected = 1;
can->hdr[pitem->hdr_bank].msgs = 0;
rt_list_init(&can->hdr[pitem->hdr_bank].list);
}
rt_hw_interrupt_enable(level);
count--;
pitem++;
}
}
else
{
while (count)
{
if (pitem->hdr_bank >= can->config.maxhdr || pitem->hdr_bank < 0)
{
count--;
pitem++;
continue;
}
level = rt_hw_interrupt_disable();
if (can->hdr[pitem->hdr_bank].connected)
{
can->hdr[pitem->hdr_bank].connected = 0;
can->hdr[pitem->hdr_bank].msgs = 0;
if (!rt_list_isempty(&can->hdr[pitem->hdr_bank].list))
{
rt_list_remove(can->hdr[pitem->hdr_bank].list.next);
}
rt_hw_interrupt_enable(level);
rt_memset(&can->hdr[pitem->hdr_bank].filter, 0,
sizeof(struct rt_can_filter_item));
}
else
{
rt_hw_interrupt_enable(level);
}
count--;
pitem++;
}
}
break;
#endif /*RT_CAN_USING_HDR*/
#ifdef RT_CAN_USING_BUS_HOOK
case RT_CAN_CMD_SET_BUS_HOOK:
can->bus_hook = (rt_can_bus_hook) args;
break;
#endif /*RT_CAN_USING_BUS_HOOK*/
default :
/* control device */
if (can->ops->control != RT_NULL)
{
res = can->ops->control(can, cmd, args);
}
else
{
res = -RT_ENOSYS;
}
break;
}
return res;
}
/*
* can timer
*/
static void cantimeout(void *arg)
{
rt_can_t can;
can = (rt_can_t)arg;
RT_ASSERT(can);
rt_device_control((rt_device_t)can, RT_CAN_CMD_GET_STATUS, (void *)&can->status);
if (can->status_indicate.ind != RT_NULL)
{
can->status_indicate.ind(can, can->status_indicate.args);
}
#ifdef RT_CAN_USING_BUS_HOOK
if(can->bus_hook)
{
can->bus_hook(can);
}
#endif /*RT_CAN_USING_BUS_HOOK*/
if (can->timerinitflag == 1)
{
can->timerinitflag = 0xFF;
}
}
#ifdef RT_USING_DEVICE_OPS
const static struct rt_device_ops can_device_ops =
{
rt_can_init,
rt_can_open,
rt_can_close,
rt_can_read,
rt_can_write,
rt_can_control
};
#endif
/*
* can register
*/
rt_err_t rt_hw_can_register(struct rt_can_device *can,
const char *name,
const struct rt_can_ops *ops,
void *data)
{
struct rt_device *device;
RT_ASSERT(can != RT_NULL);
device = &(can->parent);
device->type = RT_Device_Class_CAN;
device->rx_indicate = RT_NULL;
device->tx_complete = RT_NULL;
#ifdef RT_CAN_USING_HDR
can->hdr = RT_NULL;
#endif
can->can_rx = RT_NULL;
can->can_tx = RT_NULL;
rt_mutex_init(&(can->lock), "can", RT_IPC_FLAG_PRIO);
#ifdef RT_CAN_USING_BUS_HOOK
can->bus_hook = RT_NULL;
#endif /*RT_CAN_USING_BUS_HOOK*/
#ifdef RT_USING_DEVICE_OPS
device->ops = &can_device_ops;
#else
device->init = rt_can_init;
device->open = rt_can_open;
device->close = rt_can_close;
device->read = rt_can_read;
device->write = rt_can_write;
device->control = rt_can_control;
#endif
can->ops = ops;
can->status_indicate.ind = RT_NULL;
can->status_indicate.args = RT_NULL;
rt_memset(&can->status, 0, sizeof(can->status));
device->user_data = data;
can->timerinitflag = 0;
rt_timer_init(&can->timer,
name,
cantimeout,
(void *)can,
can->config.ticks,
RT_TIMER_FLAG_PERIODIC);
/* register a character device */
return rt_device_register(device, name, RT_DEVICE_FLAG_RDWR);
}
/* ISR for can interrupt */
void rt_hw_can_isr(struct rt_can_device *can, int event)
{
switch (event & 0xff)
{
case RT_CAN_EVENT_RXOF_IND:
{
rt_base_t level;
level = rt_hw_interrupt_disable();
can->status.dropedrcvpkg++;
rt_hw_interrupt_enable(level);
}
case RT_CAN_EVENT_RX_IND:
{
struct rt_can_msg tmpmsg;
struct rt_can_rx_fifo *rx_fifo;
struct rt_can_msg_list *listmsg = RT_NULL;
#ifdef RT_CAN_USING_HDR
rt_int8_t hdr;
#endif
int ch = -1;
rt_base_t level;
rt_uint32_t no;
rx_fifo = (struct rt_can_rx_fifo *)can->can_rx;
RT_ASSERT(rx_fifo != RT_NULL);
/* interrupt mode receive */
RT_ASSERT(can->parent.open_flag & RT_DEVICE_FLAG_INT_RX);
no = event >> 8;
ch = can->ops->recvmsg(can, &tmpmsg, no);
if (ch == -1) break;
/* disable interrupt */
level = rt_hw_interrupt_disable();
can->status.rcvpkg++;
can->status.rcvchange = 1;
if (!rt_list_isempty(&rx_fifo->freelist))
{
listmsg = rt_list_entry(rx_fifo->freelist.next, struct rt_can_msg_list, list);
rt_list_remove(&listmsg->list);
#ifdef RT_CAN_USING_HDR
rt_list_remove(&listmsg->hdrlist);
if (listmsg->owner != RT_NULL && listmsg->owner->msgs)
{
listmsg->owner->msgs--;
}
listmsg->owner = RT_NULL;
#endif /*RT_CAN_USING_HDR*/
RT_ASSERT(rx_fifo->freenumbers > 0);
rx_fifo->freenumbers--;
}
else if (!rt_list_isempty(&rx_fifo->uselist))
{
listmsg = rt_list_entry(rx_fifo->uselist.next, struct rt_can_msg_list, list);
can->status.dropedrcvpkg++;
rt_list_remove(&listmsg->list);
#ifdef RT_CAN_USING_HDR
rt_list_remove(&listmsg->hdrlist);
if (listmsg->owner != RT_NULL && listmsg->owner->msgs)
{
listmsg->owner->msgs--;
}
listmsg->owner = RT_NULL;
#endif
}
/* enable interrupt */
rt_hw_interrupt_enable(level);
if (listmsg != RT_NULL)
{
rt_memcpy(&listmsg->data, &tmpmsg, sizeof(struct rt_can_msg));
level = rt_hw_interrupt_disable();
rt_list_insert_before(&rx_fifo->uselist, &listmsg->list);
#ifdef RT_CAN_USING_HDR
hdr = tmpmsg.hdr_index;
if (can->hdr != RT_NULL)
{
RT_ASSERT(hdr < can->config.maxhdr && hdr >= 0);
if (can->hdr[hdr].connected)
{
rt_list_insert_before(&can->hdr[hdr].list, &listmsg->hdrlist);
listmsg->owner = &can->hdr[hdr];
can->hdr[hdr].msgs++;
}
}
#endif
rt_hw_interrupt_enable(level);
}
/* invoke callback */
#ifdef RT_CAN_USING_HDR
if (can->hdr != RT_NULL && can->hdr[hdr].connected && can->hdr[hdr].filter.ind)
{
rt_size_t rx_length;
RT_ASSERT(hdr < can->config.maxhdr && hdr >= 0);
level = rt_hw_interrupt_disable();
rx_length = can->hdr[hdr].msgs * sizeof(struct rt_can_msg);
rt_hw_interrupt_enable(level);
if (rx_length)
{
can->hdr[hdr].filter.ind(&can->parent, can->hdr[hdr].filter.args, hdr, rx_length);
}
}
else
#endif
{
if (can->parent.rx_indicate != RT_NULL)
{
rt_size_t rx_length;
level = rt_hw_interrupt_disable();
/* get rx length */
rx_length = rt_list_len(&rx_fifo->uselist)* sizeof(struct rt_can_msg);
rt_hw_interrupt_enable(level);
if (rx_length)
{
can->parent.rx_indicate(&can->parent, rx_length);
}
}
}
break;
}
case RT_CAN_EVENT_TX_DONE:
case RT_CAN_EVENT_TX_FAIL:
{
struct rt_can_tx_fifo *tx_fifo;
rt_uint32_t no;
no = event >> 8;
tx_fifo = (struct rt_can_tx_fifo *) can->can_tx;
RT_ASSERT(tx_fifo != RT_NULL);
if ((event & 0xff) == RT_CAN_EVENT_TX_DONE)
{
tx_fifo->buffer[no].result = RT_CAN_SND_RESULT_OK;
}
else
{
tx_fifo->buffer[no].result = RT_CAN_SND_RESULT_ERR;
}
rt_completion_done(&(tx_fifo->buffer[no].completion));
break;
}
}
}
#ifdef RT_USING_FINSH
#include <finsh.h>
int cmd_canstat(int argc, void **argv)
{
static const char *ErrCode[] =
{
"No Error!",
"Warning !",
"Passive !",
"Bus Off !"
};
if (argc >= 2)
{
struct rt_can_status status;
rt_device_t candev = rt_device_find(argv[1]);
if (!candev)
{
rt_kprintf(" Can't find can device %s\n", argv[1]);
return -1;
}
rt_kprintf(" Found can device: %s...", argv[1]);
rt_device_control(candev, RT_CAN_CMD_GET_STATUS, &status);
rt_kprintf("\n Receive...error..count: %010ld. Send.....error....count: %010ld.",
status.rcverrcnt, status.snderrcnt);
rt_kprintf("\n Bit..pad..error..count: %010ld. Format...error....count: %010ld",
status.bitpaderrcnt, status.formaterrcnt);
rt_kprintf("\n Ack.......error..count: %010ld. Bit......error....count: %010ld.",
status.ackerrcnt, status.biterrcnt);
rt_kprintf("\n CRC.......error..count: %010ld. Error.code.[%010ld]: ",
status.crcerrcnt, status.errcode);
switch (status.errcode)
{
case 0:
rt_kprintf("%s.", ErrCode[0]);
break;
case 1:
rt_kprintf("%s.", ErrCode[1]);
break;
case 2:
case 3:
rt_kprintf("%s.", ErrCode[2]);
break;
case 4:
case 5:
case 6:
case 7:
rt_kprintf("%s.", ErrCode[3]);
break;
}
rt_kprintf("\n Total.receive.packages: %010ld. Dropped.receive.packages: %010ld.",
status.rcvpkg, status.dropedrcvpkg);
rt_kprintf("\n Total..send...packages: %010ld. Dropped...send..packages: %010ld.\n",
status.sndpkg + status.dropedsndpkg, status.dropedsndpkg);
}
else
{
rt_kprintf(" Invalid Call %s\n", argv[0]);
rt_kprintf(" Please using %s cannamex .Here canname is driver name and x is candrive number.\n", argv[0]);
}
return 0;
}
MSH_CMD_EXPORT_ALIAS(cmd_canstat, canstat, stat can device status);
#endif

View File

@@ -3,3 +3,7 @@ menuconfig RT_USING_CLK
depends on RT_USING_DM
select RT_USING_ADT_REF
default y
if RT_USING_CLK
osource "$(SOC_DM_CLK_DIR)/Kconfig"
endif

View File

@@ -55,6 +55,10 @@ static struct rt_clk *clk_alloc(struct rt_clk_node *clk_np, const char *dev_id,
clk->fw_node = fw_node;
}
else
{
clk = rt_err_ptr(-RT_ENOMEM);
}
return clk;
}
@@ -76,7 +80,7 @@ static struct rt_clk *clk_create(struct rt_clk_node *clk_np, const char *dev_id,
{
struct rt_clk *clk = clk_alloc(clk_np, dev_id, con_id, fw_node);
if (clk)
if (!rt_is_err(clk))
{
clk_get(clk_np);
@@ -135,15 +139,6 @@ rt_err_t rt_clk_register(struct rt_clk_node *clk_np, struct rt_clk_node *parent_
struct rt_clk *clk = RT_NULL;
if (clk_np)
{
clk = clk_alloc(clk_np, RT_NULL, RT_NULL, RT_NULL);
}
else
{
err = -RT_EINVAL;
}
if (!err && clk_np)
{
clk_np->clk = clk;
@@ -152,6 +147,12 @@ rt_err_t rt_clk_register(struct rt_clk_node *clk_np, struct rt_clk_node *parent_
clk_np->ops = &unused_clk_ops;
}
#if RT_NAME_MAX > 0
rt_strncpy(clk_np->rt_parent.name, RT_CLK_NODE_OBJ_NAME, RT_NAME_MAX);
#else
clk_np->rt_parent.name = RT_CLK_NODE_OBJ_NAME;
#endif
rt_ref_init(&clk_np->ref);
rt_list_init(&clk_np->list);
rt_list_init(&clk_np->children_nodes);
@@ -159,7 +160,16 @@ rt_err_t rt_clk_register(struct rt_clk_node *clk_np, struct rt_clk_node *parent_
if (parent_np)
{
clk_set_parent(clk_np, parent_np);
clk_np->clk = clk_alloc(clk_np, RT_NULL, RT_NULL, RT_NULL);
if (clk_np->clk)
{
clk_set_parent(clk_np, parent_np);
}
else
{
err = -RT_ENOMEM;
}
}
else
{
@@ -265,11 +275,16 @@ static rt_err_t clk_prepare(struct rt_clk *clk, struct rt_clk_node *clk_np)
clk_prepare(clk_np->clk, clk_np->parent);
}
if (clk_np->ops->prepare)
if (clk->prepare_count == 0 && clk_np->ops->prepare)
{
err = clk_np->ops->prepare(clk);
}
if (!err)
{
++clk->prepare_count;
}
return err;
}
@@ -287,10 +302,6 @@ rt_err_t rt_clk_prepare(struct rt_clk *clk)
rt_hw_spin_unlock(&_clk_lock.lock);
}
else
{
err = -RT_EINVAL;
}
return err;
}
@@ -302,10 +313,14 @@ static void clk_unprepare(struct rt_clk *clk, struct rt_clk_node *clk_np)
clk_unprepare(clk_np->clk, clk_np->parent);
}
if (clk_np->ops->unprepare)
if (clk->prepare_count == 1 && clk_np->ops->unprepare)
{
clk_np->ops->unprepare(clk);
}
if (clk->prepare_count)
{
--clk->prepare_count;
}
}
rt_err_t rt_clk_unprepare(struct rt_clk *clk)
@@ -322,10 +337,6 @@ rt_err_t rt_clk_unprepare(struct rt_clk *clk)
rt_hw_spin_unlock(&_clk_lock.lock);
}
else
{
err = -RT_EINVAL;
}
return err;
}
@@ -339,11 +350,16 @@ static rt_err_t clk_enable(struct rt_clk *clk, struct rt_clk_node *clk_np)
clk_enable(clk_np->clk, clk_np->parent);
}
if (clk_np->ops->enable)
if (clk->enable_count == 0 && clk_np->ops->enable)
{
err = clk_np->ops->enable(clk);
}
if (!err)
{
++clk->enable_count;
}
return err;
}
@@ -359,10 +375,6 @@ rt_err_t rt_clk_enable(struct rt_clk *clk)
rt_hw_spin_unlock(&_clk_lock.lock);
}
else
{
err = -RT_EINVAL;
}
return err;
}
@@ -374,10 +386,14 @@ static void clk_disable(struct rt_clk *clk, struct rt_clk_node *clk_np)
clk_disable(clk_np->clk, clk_np->parent);
}
if (clk_np->ops->disable)
if (clk->enable_count == 1 && clk_np->ops->disable)
{
clk_np->ops->disable(clk);
}
if (clk->enable_count)
{
--clk->enable_count;
}
}
void rt_clk_disable(struct rt_clk *clk)
@@ -394,7 +410,7 @@ void rt_clk_disable(struct rt_clk *clk)
rt_err_t rt_clk_prepare_enable(struct rt_clk *clk)
{
rt_err_t err;
rt_err_t err = RT_EOK;
RT_DEBUG_NOT_IN_INTERRUPT;
@@ -412,10 +428,6 @@ rt_err_t rt_clk_prepare_enable(struct rt_clk *clk)
}
}
}
else
{
err = -RT_EINVAL;
}
return err;
}
@@ -453,10 +465,6 @@ rt_err_t rt_clk_array_prepare(struct rt_clk_array *clk_arr)
}
}
}
else
{
err = -RT_EINVAL;
}
return err;
}
@@ -478,10 +486,6 @@ rt_err_t rt_clk_array_unprepare(struct rt_clk_array *clk_arr)
}
}
}
else
{
err = -RT_EINVAL;
}
return err;
}
@@ -508,10 +512,6 @@ rt_err_t rt_clk_array_enable(struct rt_clk_array *clk_arr)
}
}
}
else
{
err = -RT_EINVAL;
}
return err;
}
@@ -529,29 +529,16 @@ void rt_clk_array_disable(struct rt_clk_array *clk_arr)
rt_err_t rt_clk_array_prepare_enable(struct rt_clk_array *clk_arr)
{
rt_err_t err = RT_EOK;
rt_err_t err;
if (clk_arr)
if ((err = rt_clk_array_prepare(clk_arr)))
{
for (int i = 0; i < clk_arr->count; ++i)
{
if ((err = rt_clk_prepare_enable(clk_arr->clks[i])))
{
LOG_E("CLK Array[%d] %s failed error = %s", i,
"prepare_enable", rt_strerror(err));
while (i --> 0)
{
rt_clk_disable_unprepare(clk_arr->clks[i]);
}
break;
}
}
return err;
}
else
if ((err = rt_clk_array_enable(clk_arr)))
{
err = -RT_EINVAL;
rt_clk_array_unprepare(clk_arr);
}
return err;
@@ -559,13 +546,8 @@ rt_err_t rt_clk_array_prepare_enable(struct rt_clk_array *clk_arr)
void rt_clk_array_disable_unprepare(struct rt_clk_array *clk_arr)
{
if (clk_arr)
{
for (int i = 0; i < clk_arr->count; ++i)
{
rt_clk_disable_unprepare(clk_arr->clks[i]);
}
}
rt_clk_array_disable(clk_arr);
rt_clk_array_unprepare(clk_arr);
}
rt_err_t rt_clk_set_rate_range(struct rt_clk *clk, rt_ubase_t min, rt_ubase_t max)
@@ -604,10 +586,6 @@ rt_err_t rt_clk_set_rate_range(struct rt_clk *clk, rt_ubase_t min, rt_ubase_t ma
rt_hw_spin_unlock(&_clk_lock.lock);
}
else
{
err = -RT_EINVAL;
}
return err;
}
@@ -622,10 +600,6 @@ rt_err_t rt_clk_set_min_rate(struct rt_clk *clk, rt_ubase_t rate)
err = rt_clk_set_rate_range(clk, rate, clk_np->max_rate);
}
else
{
err = -RT_EINVAL;
}
return err;
}
@@ -640,10 +614,6 @@ rt_err_t rt_clk_set_max_rate(struct rt_clk *clk, rt_ubase_t rate)
err = rt_clk_set_rate_range(clk, clk_np->min_rate, rate);
}
else
{
err = -RT_EINVAL;
}
return err;
}
@@ -652,7 +622,9 @@ rt_err_t rt_clk_set_rate(struct rt_clk *clk, rt_ubase_t rate)
{
rt_err_t err = RT_EOK;
if (clk && clk->clk_np)
rate = rt_clk_round_rate(clk, rate);
if (clk && clk->clk_np && rate > 0)
{
struct rt_clk_node *clk_np = clk->clk_np;
@@ -690,17 +662,13 @@ rt_err_t rt_clk_set_rate(struct rt_clk *clk, rt_ubase_t rate)
rt_hw_spin_unlock(&_clk_lock.lock);
}
else
{
err = -RT_EINVAL;
}
return err;
}
rt_ubase_t rt_clk_get_rate(struct rt_clk *clk)
{
rt_ubase_t rate = -1UL;
rt_ubase_t rate = 0;
if (clk)
{
@@ -729,10 +697,6 @@ rt_err_t rt_clk_set_phase(struct rt_clk *clk, int degrees)
rt_hw_spin_unlock(&_clk_lock.lock);
}
else
{
err = -RT_EINVAL;
}
return err;
}
@@ -749,38 +713,49 @@ rt_base_t rt_clk_get_phase(struct rt_clk *clk)
rt_hw_spin_unlock(&_clk_lock.lock);
}
else
{
res = -RT_EINVAL;
}
return res;
}
rt_base_t rt_clk_round_rate(struct rt_clk *clk, rt_ubase_t rate)
{
rt_base_t res = RT_EOK;
rt_base_t res = -RT_EINVAL;
if (clk && clk->clk_np && clk->clk_np->ops->round_rate)
if (clk && clk->clk_np)
{
rt_ubase_t best_parent_rate;
struct rt_clk_node *clk_np = clk->clk_np;
rt_hw_spin_lock(&_clk_lock.lock);
if (clk_np->min_rate && clk_np->max_rate)
if (clk_np->ops->round_rate)
{
rate = rt_clamp(rate, clk_np->min_rate, clk_np->max_rate);
rt_ubase_t best_parent_rate;
rt_hw_spin_lock(&_clk_lock.lock);
if (clk_np->min_rate && clk_np->max_rate)
{
rate = rt_clamp(rate, clk_np->min_rate, clk_np->max_rate);
}
res = clk_np->ops->round_rate(clk, rate, &best_parent_rate);
(void)best_parent_rate;
rt_hw_spin_unlock(&_clk_lock.lock);
}
else
{
if (rate < clk_np->min_rate)
{
res = clk_np->min_rate;
}
else if (rate > clk_np->max_rate)
{
res = clk_np->max_rate;
}
else
{
res = rate;
}
}
res = clk->clk_np->ops->round_rate(clk, rate, &best_parent_rate);
(void)best_parent_rate;
rt_hw_spin_unlock(&_clk_lock.lock);
}
else
{
res = -RT_EINVAL;
}
return res;
@@ -798,10 +773,6 @@ rt_err_t rt_clk_set_parent(struct rt_clk *clk, struct rt_clk *clk_parent)
rt_hw_spin_unlock(&_clk_lock.lock);
}
else
{
err = -RT_EINVAL;
}
return err;
}
@@ -887,7 +858,7 @@ void rt_clk_put(struct rt_clk *clk)
}
#ifdef RT_USING_OFW
static struct rt_clk *ofw_get_clk_no_lock(struct rt_ofw_node *np, int index, const char *name)
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;
struct rt_ofw_cell_args clk_args;
@@ -895,10 +866,32 @@ static struct rt_clk *ofw_get_clk_no_lock(struct rt_ofw_node *np, int index, con
if (!rt_ofw_parse_phandle_cells(np, "clocks", "#clock-cells", index, &clk_args))
{
int count;
struct rt_object *obj;
struct rt_clk_node *clk_np = RT_NULL;
struct rt_ofw_node *clk_ofw_np = clk_args.data;
struct rt_clk_node *clk_np = rt_ofw_data(clk_ofw_np);
count = rt_ofw_count_of_clk(clk_ofw_np);
if (!rt_ofw_data(clk_ofw_np))
{
if (locked)
{
rt_hw_spin_unlock(&_clk_lock.lock);
}
rt_platform_ofw_request(clk_ofw_np);
if (locked)
{
rt_hw_spin_lock(&_clk_lock.lock);
}
}
if (rt_ofw_data(clk_ofw_np) && (obj = rt_ofw_parse_object(clk_ofw_np,
RT_CLK_NODE_OBJ_NAME, "#clock-cells")))
{
clk_np = rt_container_of(obj, struct rt_clk_node, rt_parent);
count = rt_ofw_count_of_clk(clk_ofw_np);
}
rt_ofw_node_put(clk_ofw_np);
@@ -912,6 +905,10 @@ static struct rt_clk *ofw_get_clk_no_lock(struct rt_ofw_node *np, int index, con
clk = clk_create(clk_np, np->full_name, name, &clk_args, np);
}
else
{
clk = rt_err_ptr(-RT_ERROR);
}
}
return clk;
@@ -923,7 +920,7 @@ static struct rt_clk *ofw_get_clk(struct rt_ofw_node *np, int index, const char
rt_hw_spin_lock(&_clk_lock.lock);
clk = ofw_get_clk_no_lock(np, index, name);
clk = ofw_get_clk_no_lock(np, index, name, RT_TRUE);
rt_hw_spin_unlock(&_clk_lock.lock);
@@ -935,6 +932,11 @@ struct rt_clk_array *rt_ofw_get_clk_array(struct rt_ofw_node *np)
int count;
struct rt_clk_array *clk_arr = RT_NULL;
if (!np)
{
return rt_err_ptr(-RT_EINVAL);
}
if ((count = rt_ofw_count_phandle_cells(np, "clocks", "#clock-cells")) > 0)
{
clk_arr = rt_calloc(1, sizeof(*clk_arr) + sizeof(clk_arr->clks[0]) * count);
@@ -942,6 +944,7 @@ struct rt_clk_array *rt_ofw_get_clk_array(struct rt_ofw_node *np)
if (clk_arr)
{
int i;
rt_err_t err = RT_EOK;
rt_bool_t has_name = rt_ofw_prop_read_bool(np, "clock-names");
clk_arr->count = count;
@@ -957,10 +960,12 @@ struct rt_clk_array *rt_ofw_get_clk_array(struct rt_ofw_node *np)
rt_ofw_prop_read_string_index(np, "clock-names", i, &name);
}
clk_arr->clks[i] = ofw_get_clk_no_lock(np, i, name);
clk_arr->clks[i] = ofw_get_clk_no_lock(np, i, name, RT_FALSE);
if (!clk_arr->clks[i])
if (rt_is_err(clk_arr->clks[i]))
{
err = rt_ptr_err(clk_arr->clks[i]);
--i;
break;
}
@@ -971,7 +976,7 @@ struct rt_clk_array *rt_ofw_get_clk_array(struct rt_ofw_node *np)
if (i > 0 && i < count)
{
rt_clk_array_put(clk_arr);
clk_arr = RT_NULL;
clk_arr = rt_err_ptr(err);
}
}
}

View File

@@ -360,9 +360,9 @@ rt_err_t rt_bus_remove_device(rt_device_t dev)
}
else if (drv)
{
if (drv->shutdown)
if (drv->remove)
{
err = drv->shutdown(dev);
err = drv->remove(dev);
}
/* device and driver are in the same bus */

View File

@@ -13,6 +13,8 @@
* 2013-07-09 Grissiom add ref_count support
* 2016-04-02 Bernard fix the open_flag initialization issue.
* 2021-03-19 Meco Man remove rt_device_init_all()
* 2024-09-15 milo fix log format issue
* fix reopen with a different oflag issue
*/
#include <rtthread.h>
@@ -29,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
@@ -82,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 */
@@ -163,7 +165,7 @@ void rt_device_destroy(rt_device_t dev)
{
/* parameter check */
RT_ASSERT(dev != RT_NULL);
RT_ASSERT(rt_object_get_type(&dev->parent) == RT_Object_Class_Device);
RT_ASSERT(rt_object_get_type(&dev->parent) == RT_Object_Class_Null);
RT_ASSERT(rt_object_is_systemobject(&dev->parent) == RT_FALSE);
rt_object_detach(&(dev->parent));
@@ -195,8 +197,8 @@ rt_err_t rt_device_init(rt_device_t dev)
result = device_init(dev);
if (result != RT_EOK)
{
LOG_E("To initialize device:%s failed. The error code is %d",
dev->parent.name, result);
LOG_E("To initialize device:%.*s failed. The error code is %d",
RT_NAME_MAX, dev->parent.name, result);
}
else
{
@@ -233,8 +235,8 @@ rt_err_t rt_device_open(rt_device_t dev, rt_uint16_t oflag)
result = device_init(dev);
if (result != RT_EOK)
{
LOG_E("To initialize device:%s failed. The error code is %d",
dev->parent.name, result);
LOG_E("To initialize device:%.*s failed. The error code is %d",
RT_NAME_MAX, dev->parent.name, result);
return result;
}
@@ -252,7 +254,7 @@ rt_err_t rt_device_open(rt_device_t dev, rt_uint16_t oflag)
/* device is not opened or opened by other oflag, call device_open interface */
if (!(dev->open_flag & RT_DEVICE_OFLAG_OPEN) ||
((dev->open_flag & RT_DEVICE_OFLAG_MASK) != (oflag & RT_DEVICE_OFLAG_MASK)))
((dev->open_flag & RT_DEVICE_OFLAG_MASK) != ((oflag & RT_DEVICE_OFLAG_MASK) | RT_DEVICE_OFLAG_OPEN)))
{
if (device_open != RT_NULL)
{

View File

@@ -53,6 +53,137 @@ void rt_dm_secondary_cpu_init(void)
}
#endif /* RT_USING_SMP */
/**
* @brief This function will alloc an id in an IDA object
*
* @param ida is the IDA object
*
* @return the id or -RT_EEMPTY
*/
int rt_dm_ida_alloc(struct rt_dm_ida *ida)
{
int id;
RT_ASSERT(ida != RT_NULL);
rt_spin_lock(&ida->lock);
id = rt_bitmap_next_clear_bit(ida->map, 0, RT_DM_IDA_NUM);
if (id != RT_DM_IDA_NUM)
{
rt_bitmap_set_bit(ida->map, id);
}
rt_spin_unlock(&ida->lock);
if (id != RT_DM_IDA_NUM)
{
return id;
}
return -RT_EEMPTY;
}
/**
* @brief This function will take (force) an id in an IDA object
*
* @param ida is the IDA object
*
* @param id is the id that want to take
*
* @return the result of taking
*/
rt_bool_t rt_dm_ida_take(struct rt_dm_ida *ida, int id)
{
RT_ASSERT(ida != RT_NULL);
RT_ASSERT(id >= 0);
rt_spin_lock(&ida->lock);
if (!rt_bitmap_test_bit(ida->map, id))
{
rt_bitmap_set_bit(ida->map, id);
}
else
{
id = RT_DM_IDA_NUM;
}
rt_spin_unlock(&ida->lock);
return id != RT_DM_IDA_NUM;
}
/**
* @brief This function will release an id in an IDA object
*
* @param ida is the IDA object
*
* @param id is the id of IDA object
*/
void rt_dm_ida_free(struct rt_dm_ida *ida, int id)
{
RT_ASSERT(ida != RT_NULL);
RT_ASSERT(id >= 0);
rt_spin_lock(&ida->lock);
rt_bitmap_clear_bit(ida->map, id);
rt_spin_unlock(&ida->lock);
}
/**
* @brief This function will return the specified master id and device id of device.
*
* @param master_id is the master id (0, 255] of device
*
* @param device_id is the device id [-1, 255] of device, when device_id is -1,
* the function will end when find the first device.
*
* @return the device object or RT_NULL
*/
rt_device_t rt_dm_device_find(int master_id, int device_id)
{
struct rt_device *dev, *ret_dev = RT_NULL;
struct rt_object_information *information = RT_NULL;
if (master_id <= 0 || device_id > 255)
{
return RT_NULL;
}
information = rt_object_get_information(RT_Object_Class_Device);
/* parameter check */
if (!information)
{
return RT_NULL;
}
/* which is invoke in interrupt status */
RT_DEBUG_NOT_IN_INTERRUPT;
/* enter critical */
rt_enter_critical();
/* try to find object */
rt_list_for_each_entry(dev, &information->object_list, parent.list)
{
if (master_id == dev->master_id &&
(device_id == -1 || device_id == dev->device_id))
{
ret_dev = dev;
break;
}
}
/* leave critical */
rt_exit_critical();
return ret_dev;
}
struct prefix_track
{
rt_list_t list;

View File

@@ -78,21 +78,21 @@ static rt_bool_t platform_match(rt_driver_t drv, rt_device_t dev)
{
struct rt_platform_driver *pdrv = rt_container_of(drv, struct rt_platform_driver, parent);
struct rt_platform_device *pdev = rt_container_of(dev, struct rt_platform_device, parent);
#ifdef RT_USING_OFW
struct rt_ofw_node *np = dev->ofw_node;
/* 1、match with ofw node */
if (np)
{
#ifdef RT_USING_OFW
pdev->id = rt_ofw_node_match(np, pdrv->ids);
#else
pdev->id = RT_NULL;
#endif
if (pdev->id)
{
return RT_TRUE;
}
}
#endif
/* 2、match with name */
if (pdev->name && pdrv->name)
@@ -123,7 +123,13 @@ static rt_err_t platform_probe(rt_device_t dev)
if (err && err != -RT_EEMPTY)
{
LOG_E("Attach power domain error = %s in device %s", pdev->name, rt_strerror(err));
LOG_E("Attach power domain error = %s in device %s", rt_strerror(err),
#ifdef RT_USING_OFW
(pdev->name && pdev->name[0]) ? pdev->name : rt_ofw_node_full_name(np)
#else
pdev->name
#endif
);
return err;
}

View File

@@ -17,6 +17,7 @@
#include <drivers/ofw_io.h>
#include <drivers/ofw_fdt.h>
#include <drivers/platform.h>
#include <drivers/core/bus.h>
#include <drivers/core/dm.h>
#include "../ofw/ofw_internal.h"
@@ -161,6 +162,7 @@ static rt_err_t platform_ofw_device_probe_once(struct rt_ofw_node *parent_np)
}
pdev->dev_id = ofw_alias_node_id(np);
np->dev = &pdev->parent;
LOG_D("%s register to bus", np->full_name);
rt_platform_device_register(pdev);
@@ -199,6 +201,53 @@ rt_err_t rt_platform_ofw_device_probe_child(struct rt_ofw_node *np)
return err;
}
rt_err_t rt_platform_ofw_request(struct rt_ofw_node *np)
{
rt_err_t err;
if (np)
{
struct rt_device *dev = np->dev;
if (dev)
{
/* Was create */
if (dev->drv)
{
/* Was probe OK */
err = RT_EOK;
}
else
{
err = rt_bus_reload_driver_device(dev->bus, dev);
}
}
else
{
struct rt_platform_device *pdev = alloc_ofw_platform_device(np);
if (pdev)
{
pdev->dev_id = ofw_alias_node_id(np);
np->dev = &pdev->parent;
LOG_D("%s register to bus", np->full_name);
err = rt_platform_device_register(pdev);
}
else
{
err = -RT_ENOMEM;
}
}
}
else
{
err = -RT_EINVAL;
}
return err;
}
static int platform_ofw_device_probe(void)
{
rt_err_t err = RT_EOK;
@@ -206,6 +255,8 @@ static int platform_ofw_device_probe(void)
if (ofw_node_root)
{
rt_ofw_node_get(ofw_node_root);
err = platform_ofw_device_probe_once(ofw_node_root);
rt_ofw_node_put(ofw_node_root);
@@ -216,11 +267,19 @@ static int platform_ofw_device_probe(void)
rt_ofw_node_put(node);
}
if ((node = rt_ofw_find_node_by_path("/clocks")))
{
platform_ofw_device_probe_once(node);
rt_ofw_node_put(node);
}
rt_ofw_node_get(ofw_node_chosen);
if ((node = rt_ofw_get_child_by_compatible(ofw_node_chosen, "simple-framebuffer")))
{
platform_ofw_device_probe_once(node);
rt_ofw_node_put(node);
}
rt_ofw_node_get(ofw_node_chosen);
}
else
{
@@ -244,7 +303,7 @@ rt_err_t rt_platform_ofw_free(struct rt_platform_device *pdev)
rt_ofw_node_clear_flag(np, RT_OFW_F_PLATFORM);
rt_ofw_node_put(np);
pdev->parent.ofw_node = RT_NULL;
rt_free(pdev);
}
}
else

View File

@@ -0,0 +1,10 @@
menuconfig RT_USING_DMA
bool "Using Direct Memory Access (DMA)"
depends on RT_USING_DM
select RT_USING_ADT
select RT_USING_ADT_BITMAP
default n
if RT_USING_DMA
osource "$(SOC_DM_DMA_DIR)/Kconfig"
endif

View File

@@ -0,0 +1,15 @@
from building import *
group = []
if not GetDepend(['RT_USING_DMA']):
Return('group')
cwd = GetCurrentDir()
CPPPATH = [cwd + '/../include']
src = ['dma.c', 'dma_pool.c']
group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH)
Return('group')

View File

@@ -0,0 +1,589 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-02-25 GuEe-GUI the first version
*/
#include <rthw.h>
#include <rtthread.h>
#include <rtdevice.h>
#define DBG_TAG "rtdm.dma"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
static rt_list_t dmac_nodes = RT_LIST_OBJECT_INIT(dmac_nodes);
static struct rt_spinlock dmac_nodes_lock = {};
rt_err_t rt_dma_controller_register(struct rt_dma_controller *ctrl)
{
const char *dev_name;
char dma_name[RT_NAME_MAX];
if (!ctrl || !ctrl->dev || !ctrl->ops)
{
return -RT_EINVAL;
}
dev_name = rt_dm_dev_get_name(ctrl->dev);
if (rt_bitmap_next_set_bit(ctrl->dir_cap, 0, RT_DMA_DIR_MAX) == RT_DMA_DIR_MAX)
{
LOG_E("%s: Not direction capability", dev_name);
return -RT_EINVAL;
}
rt_snprintf(dma_name, sizeof(dma_name), "%s-dmac", dev_name);
rt_list_init(&ctrl->list);
rt_spin_lock(&dmac_nodes_lock);
rt_list_insert_before(&dmac_nodes, &ctrl->list);
rt_spin_unlock(&dmac_nodes_lock);
rt_list_init(&ctrl->channels_nodes);
rt_mutex_init(&ctrl->mutex, dma_name, RT_IPC_FLAG_PRIO);
if (ctrl->dev->ofw_node)
{
rt_dm_dev_bind_fwdata(ctrl->dev, RT_NULL, ctrl);
}
return RT_EOK;
}
rt_err_t rt_dma_controller_unregister(struct rt_dma_controller *ctrl)
{
if (!ctrl)
{
return -RT_EINVAL;
}
rt_mutex_take(&ctrl->mutex, RT_WAITING_FOREVER);
if (!rt_list_isempty(&ctrl->channels_nodes))
{
rt_mutex_release(&ctrl->mutex);
return -RT_EBUSY;
}
if (ctrl->dev->ofw_node)
{
rt_dm_dev_unbind_fwdata(ctrl->dev, RT_NULL);
}
rt_mutex_release(&ctrl->mutex);
rt_mutex_detach(&ctrl->mutex);
rt_spin_lock(&dmac_nodes_lock);
rt_list_remove(&ctrl->list);
rt_spin_unlock(&dmac_nodes_lock);
return RT_EOK;
}
rt_err_t rt_dma_chan_start(struct rt_dma_chan *chan)
{
rt_err_t err;
struct rt_dma_controller *ctrl;
if (!chan)
{
return -RT_EINVAL;
}
if (chan->prep_err)
{
LOG_D("%s: Not config done", rt_dm_dev_get_name(chan->slave));
return chan->prep_err;
}
ctrl = chan->ctrl;
rt_mutex_take(&ctrl->mutex, RT_WAITING_FOREVER);
err = ctrl->ops->start(chan);
rt_mutex_release(&ctrl->mutex);
return err;
}
rt_err_t rt_dma_chan_stop(struct rt_dma_chan *chan)
{
rt_err_t err;
struct rt_dma_controller *ctrl;
if (!chan)
{
return -RT_EINVAL;
}
if (chan->prep_err)
{
LOG_D("%s: Not prepare done", rt_dm_dev_get_name(chan->slave));
return chan->prep_err;
}
ctrl = chan->ctrl;
rt_mutex_take(&ctrl->mutex, RT_WAITING_FOREVER);
err = ctrl->ops->stop(chan);
rt_mutex_release(&ctrl->mutex);
return err;
}
rt_err_t rt_dma_chan_config(struct rt_dma_chan *chan,
struct rt_dma_slave_config *conf)
{
rt_err_t err;
struct rt_dma_controller *ctrl;
enum rt_dma_transfer_direction dir;
if (!chan || !conf)
{
err = -RT_EINVAL;
goto _end;
}
dir = conf->direction;
if (dir >= RT_DMA_DIR_MAX)
{
err = -RT_EINVAL;
goto _end;
}
if (conf->src_addr_width >= RT_DMA_SLAVE_BUSWIDTH_BYTES_MAX ||
conf->dst_addr_width >= RT_DMA_SLAVE_BUSWIDTH_BYTES_MAX)
{
err = -RT_EINVAL;
goto _end;
}
ctrl = chan->ctrl;
if (!rt_bitmap_test_bit(ctrl->dir_cap, dir))
{
err = -RT_ENOSYS;
goto _end;
}
if (!chan->name && dir != RT_DMA_MEM_TO_MEM)
{
LOG_E("%s: illegal config for uname channels",
rt_dm_dev_get_name(ctrl->dev));
err = -RT_EINVAL;
goto _end;
}
rt_mutex_take(&ctrl->mutex, RT_WAITING_FOREVER);
err = ctrl->ops->config(chan, conf);
rt_mutex_release(&ctrl->mutex);
if (!err)
{
rt_memcpy(&chan->conf, conf, sizeof(*conf));
}
_end:
chan->conf_err = err;
return err;
}
rt_err_t rt_dma_chan_done(struct rt_dma_chan *chan, rt_size_t size)
{
if (!chan)
{
return -RT_EINVAL;
}
if (chan->callback)
{
chan->callback(chan, size);
}
return RT_EOK;
}
static rt_bool_t range_is_illegal(const char *name, const char *desc,
rt_ubase_t addr0, rt_ubase_t addr1)
{
rt_bool_t illegal = addr0 < addr1;
if (illegal)
{
LOG_E("%s: %s %p is out of config %p", name, desc, addr0, addr1);
}
return illegal;
}
rt_err_t rt_dma_prep_memcpy(struct rt_dma_chan *chan,
struct rt_dma_slave_transfer *transfer)
{
rt_err_t err;
rt_size_t len;
rt_ubase_t dma_addr_src, dma_addr_dst;
struct rt_dma_controller *ctrl;
struct rt_dma_slave_config *conf;
if (!chan || !transfer)
{
return -RT_EINVAL;
}
ctrl = chan->ctrl;
conf = &chan->conf;
if (chan->conf_err)
{
LOG_D("%s: Not config done", rt_dm_dev_get_name(chan->slave));
return chan->conf_err;
}
RT_ASSERT(chan->conf.direction == RT_DMA_MEM_TO_MEM);
dma_addr_src = transfer->src_addr;
dma_addr_dst = transfer->dst_addr;
len = transfer->buffer_len;
if (range_is_illegal(rt_dm_dev_get_name(ctrl->dev), "source",
dma_addr_src, conf->src_addr))
{
return -RT_EINVAL;
}
if (range_is_illegal(rt_dm_dev_get_name(ctrl->dev), "dest",
dma_addr_dst, conf->dst_addr))
{
return -RT_EINVAL;
}
if (ctrl->ops->prep_memcpy)
{
rt_mutex_take(&ctrl->mutex, RT_WAITING_FOREVER);
err = ctrl->ops->prep_memcpy(chan, dma_addr_dst, dma_addr_src, len);
rt_mutex_release(&ctrl->mutex);
}
else
{
err = -RT_ENOSYS;
}
if (!err)
{
rt_memcpy(&chan->transfer, transfer, sizeof(*transfer));
}
chan->prep_err = err;
return err;
}
rt_err_t rt_dma_prep_cyclic(struct rt_dma_chan *chan,
struct rt_dma_slave_transfer *transfer)
{
rt_err_t err;
rt_ubase_t dma_buf_addr;
struct rt_dma_controller *ctrl;
struct rt_dma_slave_config *conf;
enum rt_dma_transfer_direction dir;
if (!chan || !transfer)
{
return -RT_EINVAL;
}
ctrl = chan->ctrl;
conf = &chan->conf;
if (chan->conf_err)
{
LOG_D("%s: Not config done", rt_dm_dev_get_name(chan->slave));
return chan->conf_err;
}
dir = chan->conf.direction;
if (dir == RT_DMA_MEM_TO_DEV || dir == RT_DMA_MEM_TO_MEM)
{
dma_buf_addr = transfer->src_addr;
if (range_is_illegal(rt_dm_dev_get_name(ctrl->dev), "source",
dma_buf_addr, conf->src_addr))
{
return -RT_EINVAL;
}
}
else if (dir == RT_DMA_DEV_TO_MEM)
{
dma_buf_addr = transfer->dst_addr;
if (range_is_illegal(rt_dm_dev_get_name(ctrl->dev), "dest",
dma_buf_addr, conf->dst_addr))
{
return -RT_EINVAL;
}
}
else
{
dma_buf_addr = ~0UL;
}
if (ctrl->ops->prep_cyclic)
{
rt_mutex_take(&ctrl->mutex, RT_WAITING_FOREVER);
err = ctrl->ops->prep_cyclic(chan, dma_buf_addr,
transfer->buffer_len, transfer->period_len, dir);
rt_mutex_release(&ctrl->mutex);
}
else
{
err = -RT_ENOSYS;
}
if (!err)
{
rt_memcpy(&chan->transfer, transfer, sizeof(*transfer));
}
chan->prep_err = err;
return err;
}
rt_err_t rt_dma_prep_single(struct rt_dma_chan *chan,
struct rt_dma_slave_transfer *transfer)
{
rt_err_t err;
rt_ubase_t dma_buf_addr;
struct rt_dma_controller *ctrl;
struct rt_dma_slave_config *conf;
enum rt_dma_transfer_direction dir;
if (!chan || !transfer)
{
return -RT_EINVAL;
}
ctrl = chan->ctrl;
conf = &chan->conf;
if (chan->conf_err)
{
LOG_D("%s: Not config done", rt_dm_dev_get_name(chan->slave));
return chan->conf_err;
}
dir = chan->conf.direction;
if (dir == RT_DMA_MEM_TO_DEV || dir == RT_DMA_MEM_TO_MEM)
{
dma_buf_addr = transfer->src_addr;
if (range_is_illegal(rt_dm_dev_get_name(ctrl->dev), "source",
dma_buf_addr, conf->src_addr))
{
return -RT_EINVAL;
}
}
else if (dir == RT_DMA_DEV_TO_MEM)
{
dma_buf_addr = transfer->dst_addr;
if (range_is_illegal(rt_dm_dev_get_name(ctrl->dev), "dest",
dma_buf_addr, conf->dst_addr))
{
return -RT_EINVAL;
}
}
else
{
dma_buf_addr = ~0UL;
}
if (ctrl->ops->prep_single)
{
rt_mutex_take(&ctrl->mutex, RT_WAITING_FOREVER);
err = ctrl->ops->prep_single(chan, dma_buf_addr,
transfer->buffer_len, dir);
rt_mutex_release(&ctrl->mutex);
}
else
{
err = -RT_ENOSYS;
}
if (!err)
{
rt_memcpy(&chan->transfer, transfer, sizeof(*transfer));
}
chan->prep_err = err;
return err;
}
static struct rt_dma_controller *ofw_find_dma_controller(struct rt_device *dev,
const char *name, struct rt_ofw_cell_args *args)
{
struct rt_dma_controller *ctrl = RT_NULL;
#ifdef RT_USING_OFW
int index;
struct rt_ofw_node *np = dev->ofw_node, *ctrl_np;
if (!np)
{
return RT_NULL;
}
index = rt_ofw_prop_index_of_string(np, "dma-names", name);
if (index < 0)
{
return RT_NULL;
}
if (!rt_ofw_parse_phandle_cells(np, "dmas", "#dma-cells", index, args))
{
ctrl_np = args->data;
if (!rt_ofw_data(ctrl_np))
{
rt_platform_ofw_request(ctrl_np);
}
ctrl = rt_ofw_data(ctrl_np);
rt_ofw_node_put(ctrl_np);
}
#endif /* RT_USING_OFW */
return ctrl;
}
struct rt_dma_chan *rt_dma_chan_request(struct rt_device *dev, const char *name)
{
void *fw_data = RT_NULL;
struct rt_dma_chan *chan;
struct rt_ofw_cell_args dma_args;
struct rt_dma_controller *ctrl = RT_NULL;
if (!dev)
{
return rt_err_ptr(-RT_EINVAL);
}
if (name)
{
fw_data = &dma_args;
ctrl = ofw_find_dma_controller(dev, name, &dma_args);
}
else
{
struct rt_dma_controller *ctrl_tmp;
rt_spin_lock(&dmac_nodes_lock);
rt_list_for_each_entry(ctrl_tmp, &dmac_nodes, list)
{
/* Only memory to memory for uname request */
if (rt_bitmap_test_bit(ctrl_tmp->dir_cap, RT_DMA_MEM_TO_MEM))
{
ctrl = ctrl_tmp;
break;
}
}
rt_spin_unlock(&dmac_nodes_lock);
}
if (rt_is_err_or_null(ctrl))
{
return ctrl ? ctrl : rt_err_ptr(-RT_ENOSYS);
}
if (ctrl->ops->request_chan)
{
chan = ctrl->ops->request_chan(ctrl, dev, fw_data);
}
else
{
chan = rt_calloc(1, sizeof(*chan));
if (!chan)
{
chan = rt_err_ptr(-RT_ENOMEM);
}
}
if (rt_is_err(chan))
{
return chan;
}
if (!chan)
{
LOG_E("%s: unset request channels error", rt_dm_dev_get_name(ctrl->dev));
return rt_err_ptr(-RT_ERROR);
}
chan->name = name;
chan->ctrl = ctrl;
chan->slave = dev;
rt_list_init(&chan->list);
chan->conf_err = -RT_ERROR;
chan->prep_err = -RT_ERROR;
rt_mutex_take(&ctrl->mutex, RT_WAITING_FOREVER);
rt_list_insert_before(&ctrl->channels_nodes, &chan->list);
rt_mutex_release(&ctrl->mutex);
return chan;
}
rt_err_t rt_dma_chan_release(struct rt_dma_chan *chan)
{
rt_err_t err = RT_EOK;
if (!chan)
{
return -RT_EINVAL;
}
rt_mutex_take(&chan->ctrl->mutex, RT_WAITING_FOREVER);
rt_list_remove(&chan->list);
rt_mutex_release(&chan->ctrl->mutex);
if (chan->ctrl->ops->release_chan)
{
err = chan->ctrl->ops->release_chan(chan);
}
else
{
rt_free(chan);
}
return err;
}

View File

@@ -0,0 +1,691 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-02-25 GuEe-GUI the first version
*/
#include <rthw.h>
#include <rtthread.h>
#include <rtdevice.h>
#define DBG_TAG "dma.pool"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
#include <mm_aspace.h>
#include <dt-bindings/size.h>
static struct rt_spinlock dma_pools_lock = {};
static rt_list_t dma_pool_nodes = RT_LIST_OBJECT_INIT(dma_pool_nodes);
static struct rt_dma_pool *dma_pool_install(rt_region_t *region);
static void *dma_alloc(struct rt_device *dev, rt_size_t size,
rt_ubase_t *dma_handle, rt_ubase_t flags);
static void dma_free(struct rt_device *dev, rt_size_t size,
void *cpu_addr, rt_ubase_t dma_handle, rt_ubase_t flags);
rt_inline void region_pool_lock(void)
{
rt_hw_spin_lock(&dma_pools_lock.lock);
}
rt_inline void region_pool_unlock(void)
{
rt_hw_spin_unlock(&dma_pools_lock.lock);
}
static rt_err_t dma_map_coherent_sync_out_data(struct rt_device *dev,
void *data, rt_size_t size, rt_ubase_t *dma_handle, rt_ubase_t flags)
{
if (dma_handle)
{
*dma_handle = (rt_ubase_t)rt_kmem_v2p(data);
}
rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, data, size);
return RT_EOK;
}
static rt_err_t dma_map_coherent_sync_in_data(struct rt_device *dev,
void *out_data, rt_size_t size, rt_ubase_t dma_handle, rt_ubase_t flags)
{
rt_hw_cpu_dcache_ops(RT_HW_CACHE_INVALIDATE, out_data, size);
return RT_EOK;
}
static const struct rt_dma_map_ops dma_map_coherent_ops =
{
.sync_out_data = dma_map_coherent_sync_out_data,
.sync_in_data = dma_map_coherent_sync_in_data,
};
static rt_err_t dma_map_nocoherent_sync_out_data(struct rt_device *dev,
void *data, rt_size_t size, rt_ubase_t *dma_handle, rt_ubase_t flags)
{
if (dma_handle)
{
*dma_handle = (rt_ubase_t)rt_kmem_v2p(data);
}
return RT_EOK;
}
static rt_err_t dma_map_nocoherent_sync_in_data(struct rt_device *dev,
void *out_data, rt_size_t size, rt_ubase_t dma_handle, rt_ubase_t flags)
{
return RT_EOK;
}
static const struct rt_dma_map_ops dma_map_nocoherent_ops =
{
.sync_out_data = dma_map_nocoherent_sync_out_data,
.sync_in_data = dma_map_nocoherent_sync_in_data,
};
#ifdef RT_USING_OFW
rt_inline rt_ubase_t ofw_addr_cpu2dma(struct rt_device *dev, rt_ubase_t addr)
{
return (rt_ubase_t)rt_ofw_translate_cpu2dma(dev->ofw_node, addr);
}
rt_inline rt_ubase_t ofw_addr_dma2cpu(struct rt_device *dev, rt_ubase_t addr)
{
return (rt_ubase_t)rt_ofw_translate_dma2cpu(dev->ofw_node, addr);
}
static void *ofw_dma_map_alloc(struct rt_device *dev, rt_size_t size,
rt_ubase_t *dma_handle, rt_ubase_t flags)
{
void *cpu_addr = dma_alloc(dev, size, dma_handle, flags);
if (cpu_addr && dma_handle)
{
*dma_handle = ofw_addr_cpu2dma(dev, *dma_handle);
}
return cpu_addr;
}
static void ofw_dma_map_free(struct rt_device *dev, rt_size_t size,
void *cpu_addr, rt_ubase_t dma_handle, rt_ubase_t flags)
{
dma_handle = ofw_addr_dma2cpu(dev, dma_handle);
dma_free(dev, size, cpu_addr, dma_handle, flags);
}
static rt_err_t ofw_dma_map_sync_out_data(struct rt_device *dev,
void *data, rt_size_t size,
rt_ubase_t *dma_handle, rt_ubase_t flags)
{
rt_err_t err;
if (flags & RT_DMA_F_NOCACHE)
{
err = dma_map_nocoherent_sync_out_data(dev, data, size, dma_handle, flags);
}
else
{
err = dma_map_coherent_sync_out_data(dev, data, size, dma_handle, flags);
}
if (!err && dma_handle)
{
*dma_handle = ofw_addr_cpu2dma(dev, *dma_handle);
}
return err;
}
static rt_err_t ofw_dma_map_sync_in_data(struct rt_device *dev,
void *out_data, rt_size_t size,
rt_ubase_t dma_handle, rt_ubase_t flags)
{
dma_handle = ofw_addr_dma2cpu(dev, dma_handle);
if (flags & RT_DMA_F_NOCACHE)
{
return dma_map_nocoherent_sync_in_data(dev, out_data, size, dma_handle, flags);
}
return dma_map_coherent_sync_in_data(dev, out_data, size, dma_handle, flags);
}
static const struct rt_dma_map_ops ofw_dma_map_ops =
{
.alloc = ofw_dma_map_alloc,
.free = ofw_dma_map_free,
.sync_out_data = ofw_dma_map_sync_out_data,
.sync_in_data = ofw_dma_map_sync_in_data,
};
static const struct rt_dma_map_ops *ofw_device_dma_ops(struct rt_device *dev)
{
rt_err_t err;
int region_nr = 0;
const fdt32_t *cell;
rt_phandle phandle;
rt_region_t region;
struct rt_ofw_prop *prop;
struct rt_dma_pool *dma_pool;
const struct rt_dma_map_ops *ops = RT_NULL;
struct rt_ofw_node *mem_np, *np = dev->ofw_node;
rt_ofw_foreach_prop_u32(np, "memory-region", prop, cell, phandle)
{
rt_uint64_t addr, size;
if (!(mem_np = rt_ofw_find_node_by_phandle(phandle)))
{
if (region_nr == 0)
{
return RT_NULL;
}
break;
}
if ((err = rt_ofw_get_address(mem_np, 0, &addr, &size)))
{
LOG_E("%s: Read '%s' error = %s", rt_ofw_node_full_name(mem_np),
"memory-region", rt_strerror(err));
rt_ofw_node_put(mem_np);
continue;
}
region.start = addr;
region.end = addr + size;
region.name = rt_dm_dev_get_name(dev);
rt_ofw_node_put(mem_np);
if (!(dma_pool = dma_pool_install(&region)))
{
return RT_NULL;
}
if (rt_ofw_prop_read_bool(mem_np, "no-map"))
{
dma_pool->flags |= RT_DMA_F_NOMAP;
}
if (!rt_dma_device_is_coherent(dev))
{
dma_pool->flags |= RT_DMA_F_NOCACHE;
}
dma_pool->dev = dev;
++region_nr;
}
if (region_nr)
{
ops = &ofw_dma_map_ops;
}
return ops;
}
#endif /* RT_USING_OFW */
static const struct rt_dma_map_ops *device_dma_ops(struct rt_device *dev)
{
const struct rt_dma_map_ops *ops = dev->dma_ops;
if (ops)
{
return ops;
}
#ifdef RT_USING_OFW
if (dev->ofw_node && (ops = ofw_device_dma_ops(dev)))
{
return ops;
}
#endif
if (rt_dma_device_is_coherent(dev))
{
ops = &dma_map_coherent_ops;
}
else
{
ops = &dma_map_nocoherent_ops;
}
dev->dma_ops = ops;
return ops;
}
static rt_ubase_t dma_pool_alloc(struct rt_dma_pool *pool, rt_size_t size)
{
rt_size_t bit, next_bit, end_bit, max_bits;
size = RT_DIV_ROUND_UP(size, ARCH_PAGE_SIZE);
max_bits = pool->bits - size;
rt_bitmap_for_each_clear_bit(pool->map, bit, max_bits)
{
end_bit = bit + size;
for (next_bit = bit + 1; next_bit < end_bit; ++next_bit)
{
if (rt_bitmap_test_bit(pool->map, next_bit))
{
bit = next_bit;
goto _next;
}
}
if (next_bit == end_bit)
{
while (next_bit --> bit)
{
rt_bitmap_set_bit(pool->map, next_bit);
}
return pool->start + bit * ARCH_PAGE_SIZE;
}
_next:
}
return RT_NULL;
}
static void dma_pool_free(struct rt_dma_pool *pool, rt_ubase_t offset, rt_size_t size)
{
rt_size_t bit = (offset - pool->start) / ARCH_PAGE_SIZE, end_bit;
size = RT_DIV_ROUND_UP(size, ARCH_PAGE_SIZE);
end_bit = bit + size;
for (; bit < end_bit; ++bit)
{
rt_bitmap_clear_bit(pool->map, bit);
}
}
static void *dma_alloc(struct rt_device *dev, rt_size_t size,
rt_ubase_t *dma_handle, rt_ubase_t flags)
{
void *dma_buffer = RT_NULL;
struct rt_dma_pool *pool;
region_pool_lock();
rt_list_for_each_entry(pool, &dma_pool_nodes, list)
{
if (pool->flags & RT_DMA_F_DEVICE)
{
if (!(flags & RT_DMA_F_DEVICE) || pool->dev != dev)
{
continue;
}
}
else if ((flags & RT_DMA_F_DEVICE))
{
continue;
}
if ((flags & RT_DMA_F_NOMAP) && !((pool->flags & RT_DMA_F_NOMAP)))
{
continue;
}
if ((flags & RT_DMA_F_32BITS) && !((pool->flags & RT_DMA_F_32BITS)))
{
continue;
}
if ((flags & RT_DMA_F_LINEAR) && !((pool->flags & RT_DMA_F_LINEAR)))
{
continue;
}
*dma_handle = dma_pool_alloc(pool, size);
if (*dma_handle && !(flags & RT_DMA_F_NOMAP))
{
if (flags & RT_DMA_F_NOCACHE)
{
dma_buffer = rt_ioremap_nocache((void *)*dma_handle, size);
}
else
{
dma_buffer = rt_ioremap_cached((void *)*dma_handle, size);
}
if (!dma_buffer)
{
dma_pool_free(pool, *dma_handle, size);
continue;
}
break;
}
else if (*dma_handle)
{
dma_buffer = (void *)*dma_handle;
break;
}
}
region_pool_unlock();
return dma_buffer;
}
static void dma_free(struct rt_device *dev, rt_size_t size,
void *cpu_addr, rt_ubase_t dma_handle, rt_ubase_t flags)
{
struct rt_dma_pool *pool;
region_pool_lock();
rt_list_for_each_entry(pool, &dma_pool_nodes, list)
{
if (dma_handle >= pool->region.start &&
dma_handle <= pool->region.end)
{
rt_iounmap(cpu_addr);
dma_pool_free(pool, dma_handle, size);
break;
}
}
region_pool_unlock();
}
void *rt_dma_alloc(struct rt_device *dev, rt_size_t size,
rt_ubase_t *dma_handle, rt_ubase_t flags)
{
void *dma_buffer = RT_NULL;
rt_ubase_t dma_handle_s = 0;
const struct rt_dma_map_ops *ops;
if (!dev || !size)
{
return RT_NULL;
}
ops = device_dma_ops(dev);
if (ops->alloc)
{
dma_buffer = ops->alloc(dev, size, &dma_handle_s, flags);
}
else
{
dma_buffer = dma_alloc(dev, size, &dma_handle_s, flags);
}
if (!dma_buffer)
{
return dma_buffer;
}
if (dma_handle)
{
*dma_handle = dma_handle_s;
}
return dma_buffer;
}
void rt_dma_free(struct rt_device *dev, rt_size_t size,
void *cpu_addr, rt_ubase_t dma_handle, rt_ubase_t flags)
{
const struct rt_dma_map_ops *ops;
if (!dev || !size || !cpu_addr)
{
return;
}
ops = device_dma_ops(dev);
if (ops->free)
{
ops->free(dev, size, cpu_addr, dma_handle, flags);
}
else
{
dma_free(dev, size, cpu_addr, dma_handle, flags);
}
}
rt_err_t rt_dma_sync_out_data(struct rt_device *dev, void *data, rt_size_t size,
rt_ubase_t *dma_handle, rt_ubase_t flags)
{
rt_err_t err;
rt_ubase_t dma_handle_s = 0;
const struct rt_dma_map_ops *ops;
if (!data || !size)
{
return -RT_EINVAL;
}
ops = device_dma_ops(dev);
err = ops->sync_out_data(dev, data, size, &dma_handle_s, flags);
if (dma_handle)
{
*dma_handle = dma_handle_s;
}
return err;
}
rt_err_t rt_dma_sync_in_data(struct rt_device *dev, void *out_data, rt_size_t size,
rt_ubase_t dma_handle, rt_ubase_t flags)
{
rt_err_t err;
const struct rt_dma_map_ops *ops;
if (!out_data || !size)
{
return -RT_EINVAL;
}
ops = device_dma_ops(dev);
err = ops->sync_in_data(dev, out_data, size, dma_handle, flags);
return err;
}
static struct rt_dma_pool *dma_pool_install(rt_region_t *region)
{
rt_err_t err;
struct rt_dma_pool *pool;
if (!(pool = rt_calloc(1, sizeof(*pool))))
{
LOG_E("Install pool[%p, %p] error = %s",
region->start, region->end, rt_strerror(-RT_ENOMEM));
return RT_NULL;
}
rt_memcpy(&pool->region, region, sizeof(*region));
pool->flags |= RT_DMA_F_LINEAR;
if (region->end < 4UL * SIZE_GB)
{
pool->flags |= RT_DMA_F_32BITS;
}
pool->start = RT_ALIGN(pool->region.start, ARCH_PAGE_SIZE);
pool->bits = (pool->region.end - pool->start) / ARCH_PAGE_SIZE;
if (!pool->bits)
{
err = -RT_EINVAL;
goto _fail;
}
pool->map = rt_calloc(RT_BITMAP_LEN(pool->bits), sizeof(*pool->map));
if (!pool->map)
{
err = -RT_ENOMEM;
goto _fail;
}
rt_list_init(&pool->list);
region_pool_lock();
rt_list_insert_before(&dma_pool_nodes, &pool->list);
region_pool_unlock();
return pool;
_fail:
rt_free(pool);
LOG_E("Install pool[%p, %p] error = %s",
region->start, region->end, rt_strerror(err));
return RT_NULL;
}
struct rt_dma_pool *rt_dma_pool_install(rt_region_t *region)
{
struct rt_dma_pool *pool;
if (!region)
{
return RT_NULL;
}
if ((pool = dma_pool_install(region)))
{
region = &pool->region;
LOG_I("%s: Reserved %u.%u MiB at %p",
region->name,
(region->end - region->start) / SIZE_MB,
(region->end - region->start) / SIZE_KB & (SIZE_KB - 1),
region->start);
}
return pool;
}
rt_err_t rt_dma_pool_extract(rt_region_t *region_list, rt_size_t list_len,
rt_size_t cma_size, rt_size_t coherent_pool_size)
{
struct rt_dma_pool *pool;
rt_region_t *region = region_list, *region_high = RT_NULL, cma, coherent_pool;
if (!region_list || !list_len || cma_size < coherent_pool_size)
{
return -RT_EINVAL;
}
for (rt_size_t i = 0; i < list_len; ++i, ++region)
{
if (!region->name)
{
continue;
}
/* Always use low address in 4G */
if (region->end - region->start >= cma_size)
{
if ((rt_ssize_t)((4UL * SIZE_GB) - region->start) < cma_size)
{
region_high = region;
continue;
}
goto _found;
}
}
if (region_high)
{
region = region_high;
LOG_W("No available DMA zone in 4G");
goto _found;
}
return -RT_EEMPTY;
_found:
if (region->end - region->start != cma_size)
{
cma.start = region->start;
cma.end = cma.start + cma_size;
/* Update input region */
region->start += cma_size;
}
else
{
rt_memcpy(&cma, region, sizeof(cma));
}
coherent_pool.name = "coherent-pool";
coherent_pool.start = cma.start;
coherent_pool.end = coherent_pool.start + coherent_pool_size;
cma.name = "cma";
cma.start += coherent_pool_size;
if (!(pool = rt_dma_pool_install(&coherent_pool)))
{
return -RT_ENOMEM;
}
/* Use: CMA > coherent-pool */
if (!(pool = rt_dma_pool_install(&cma)))
{
return -RT_ENOMEM;
}
return RT_EOK;
}
#if defined(RT_USING_CONSOLE) && defined(RT_USING_MSH)
static int list_dma_pool(int argc, char**argv)
{
int count = 0;
rt_region_t *region;
struct rt_dma_pool *pool;
rt_kprintf("%-*.s Region\n", RT_NAME_MAX, "Name");
region_pool_lock();
rt_list_for_each_entry(pool, &dma_pool_nodes, list)
{
region = &pool->region;
rt_kprintf("%-*.s [%p, %p]\n", RT_NAME_MAX, region->name,
region->start, region->end);
++count;
}
rt_kprintf("%d DMA memory found\n", count);
region_pool_unlock();
return 0;
}
MSH_CMD_EXPORT(list_dma_pool, dump all dma memory pool);
#endif /* RT_USING_CONSOLE && RT_USING_MSH */

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
* Copyright (c) 2006-2024 RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
@@ -320,7 +320,14 @@ static rt_err_t rt_hwtimer_control(struct rt_device *dev, int cmd, void *args)
break;
default:
{
result = -RT_ENOSYS;
if (timer->ops->control != RT_NULL)
{
result = timer->ops->control(timer, cmd, args);
}
else
{
result = -RT_ENOSYS;
}
}
break;
}

View File

@@ -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

View File

@@ -3,16 +3,16 @@ from building import *
cwd = GetCurrentDir()
src = Split("""
i2c_core.c
i2c_dev.c
dev_i2c_core.c
dev_i2c_dev.c
""")
if GetDepend('RT_USING_I2C_BITOPS'):
src = src + ['i2c-bit-ops.c']
src = src + ['dev_i2c_bit_ops.c']
if GetDepend('RT_USING_SOFT_I2C'):
src = src + ['soft_i2c.c']
src = src + ['dev_soft_i2c.c']
if GetDepend(['RT_USING_DM']):
src += ['i2c_bus.c', 'i2c_dm.c']
src += ['dev_i2c_bus.c', 'dev_i2c_dm.c']
# The set of source files associated with this SConscript file.
path = [cwd + '/../include']

View File

@@ -0,0 +1,464 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2012-04-25 weety first version
*/
#include <rtdevice.h>
#define DBG_TAG "I2C"
#ifdef RT_I2C_BITOPS_DEBUG
#define DBG_LVL DBG_LOG
#else
#define DBG_LVL DBG_INFO
#endif
#include <rtdbg.h>
#define SET_SDA(ops, val) ops->set_sda(ops->data, val)
#define SET_SCL(ops, val) ops->set_scl(ops->data, val)
#define GET_SDA(ops) ops->get_sda(ops->data)
#define GET_SCL(ops) ops->get_scl(ops->data)
rt_inline void i2c_delay(struct rt_i2c_bit_ops *ops)
{
ops->udelay((ops->delay_us + 1) >> 1);
}
rt_inline void i2c_delay2(struct rt_i2c_bit_ops *ops)
{
ops->udelay(ops->delay_us);
}
#define SDA_L(ops) SET_SDA(ops, 0)
#define SDA_H(ops) SET_SDA(ops, 1)
#define SCL_L(ops) SET_SCL(ops, 0)
/**
* release scl line, and wait scl line to high.
*/
static rt_err_t SCL_H(struct rt_i2c_bit_ops *ops)
{
rt_tick_t start;
SET_SCL(ops, 1);
if (!ops->get_scl)
goto done;
start = rt_tick_get();
while (!GET_SCL(ops))
{
if ((rt_tick_get() - start) > ops->timeout)
return -RT_ETIMEOUT;
i2c_delay(ops);
}
#ifdef RT_I2C_BITOPS_DEBUG
if (rt_tick_get() != start)
{
LOG_D("wait %ld tick for SCL line to go high",
rt_tick_get() - start);
}
#endif
done:
i2c_delay(ops);
return RT_EOK;
}
static void i2c_start(struct rt_i2c_bit_ops *ops)
{
#ifdef RT_I2C_BITOPS_DEBUG
if (ops->get_scl && !GET_SCL(ops))
{
LOG_E("I2C bus error, SCL line low");
}
if (ops->get_sda && !GET_SDA(ops))
{
LOG_E("I2C bus error, SDA line low");
}
#endif
SDA_L(ops);
i2c_delay(ops);
SCL_L(ops);
}
static void i2c_restart(struct rt_i2c_bit_ops *ops)
{
SDA_H(ops);
SCL_H(ops);
i2c_delay(ops);
SDA_L(ops);
i2c_delay(ops);
SCL_L(ops);
}
static void i2c_stop(struct rt_i2c_bit_ops *ops)
{
SDA_L(ops);
i2c_delay(ops);
SCL_H(ops);
i2c_delay(ops);
SDA_H(ops);
i2c_delay2(ops);
}
rt_inline rt_bool_t i2c_waitack(struct rt_i2c_bit_ops *ops)
{
rt_bool_t ack;
SDA_H(ops);
i2c_delay(ops);
if (SCL_H(ops) < 0)
{
LOG_W("wait ack timeout");
return -RT_ETIMEOUT;
}
ack = !GET_SDA(ops); /* ACK : SDA pin is pulled low */
LOG_D("%s", ack ? "ACK" : "NACK");
SCL_L(ops);
return ack;
}
static rt_int32_t i2c_writeb(struct rt_i2c_bus_device *bus, rt_uint8_t data)
{
rt_int32_t i;
rt_uint8_t bit;
struct rt_i2c_bit_ops *ops = (struct rt_i2c_bit_ops *)bus->priv;
for (i = 7; i >= 0; i--)
{
SCL_L(ops);
bit = (data >> i) & 1;
SET_SDA(ops, bit);
i2c_delay(ops);
if (SCL_H(ops) < 0)
{
LOG_D("i2c_writeb: 0x%02x, "
"wait scl pin high timeout at bit %d",
data, i);
return -RT_ETIMEOUT;
}
}
SCL_L(ops);
i2c_delay(ops);
return i2c_waitack(ops);
}
static rt_int32_t i2c_readb(struct rt_i2c_bus_device *bus)
{
rt_uint8_t i;
rt_uint8_t data = 0;
struct rt_i2c_bit_ops *ops = (struct rt_i2c_bit_ops *)bus->priv;
SDA_H(ops);
i2c_delay(ops);
for (i = 0; i < 8; i++)
{
data <<= 1;
if (SCL_H(ops) < 0)
{
LOG_D("i2c_readb: wait scl pin high "
"timeout at bit %d", 7 - i);
return -RT_ETIMEOUT;
}
if (GET_SDA(ops))
data |= 1;
SCL_L(ops);
i2c_delay2(ops);
}
return data;
}
static rt_ssize_t i2c_send_bytes(struct rt_i2c_bus_device *bus,
struct rt_i2c_msg *msg)
{
rt_int32_t ret;
rt_size_t bytes = 0;
const rt_uint8_t *ptr = msg->buf;
rt_int32_t count = msg->len;
rt_uint16_t ignore_nack = msg->flags & RT_I2C_IGNORE_NACK;
while (count > 0)
{
ret = i2c_writeb(bus, *ptr);
if ((ret > 0) || (ignore_nack && (ret == 0)))
{
count --;
ptr ++;
bytes ++;
}
else if (ret == 0)
{
LOG_D("send bytes: NACK.");
return 0;
}
else
{
LOG_E("send bytes: error %d", ret);
return ret;
}
}
return bytes;
}
static rt_err_t i2c_send_ack_or_nack(struct rt_i2c_bus_device *bus, int ack)
{
struct rt_i2c_bit_ops *ops = (struct rt_i2c_bit_ops *)bus->priv;
if (ack)
SET_SDA(ops, 0);
i2c_delay(ops);
if (SCL_H(ops) < 0)
{
LOG_E("ACK or NACK timeout.");
return -RT_ETIMEOUT;
}
SCL_L(ops);
return RT_EOK;
}
static rt_ssize_t i2c_recv_bytes(struct rt_i2c_bus_device *bus,
struct rt_i2c_msg *msg)
{
rt_int32_t val;
rt_int32_t bytes = 0; /* actual bytes */
rt_uint8_t *ptr = msg->buf;
rt_int32_t count = msg->len;
const rt_uint32_t flags = msg->flags;
while (count > 0)
{
val = i2c_readb(bus);
if (val >= 0)
{
*ptr = val;
bytes ++;
}
else
{
break;
}
ptr ++;
count --;
LOG_D("recieve bytes: 0x%02x, %s",
val, (flags & RT_I2C_NO_READ_ACK) ?
"(No ACK/NACK)" : (count ? "ACK" : "NACK"));
if (!(flags & RT_I2C_NO_READ_ACK))
{
val = i2c_send_ack_or_nack(bus, count);
if (val < 0)
return val;
}
}
return bytes;
}
static rt_int32_t i2c_send_address(struct rt_i2c_bus_device *bus,
rt_uint8_t addr,
rt_int32_t retries)
{
struct rt_i2c_bit_ops *ops = (struct rt_i2c_bit_ops *)bus->priv;
rt_int32_t i;
rt_err_t ret = 0;
for (i = 0; i <= retries; i++)
{
ret = i2c_writeb(bus, addr);
if (ret == 1 || i == retries)
break;
LOG_D("send stop condition");
i2c_stop(ops);
i2c_delay2(ops);
LOG_D("send start condition");
i2c_start(ops);
}
return ret;
}
static rt_err_t i2c_bit_send_address(struct rt_i2c_bus_device *bus,
struct rt_i2c_msg *msg)
{
rt_uint16_t flags = msg->flags;
rt_uint16_t ignore_nack = msg->flags & RT_I2C_IGNORE_NACK;
struct rt_i2c_bit_ops *ops = (struct rt_i2c_bit_ops *)bus->priv;
rt_uint8_t addr1, addr2;
rt_int32_t retries;
rt_err_t ret;
retries = ignore_nack ? 0 : bus->retries;
if (flags & RT_I2C_ADDR_10BIT)
{
addr1 = 0xf0 | ((msg->addr >> 7) & 0x06);
addr2 = msg->addr & 0xff;
LOG_D("addr1: %d, addr2: %d", addr1, addr2);
ret = i2c_send_address(bus, addr1, retries);
if ((ret != 1) && !ignore_nack)
{
LOG_W("NACK: sending first addr");
return -RT_EIO;
}
ret = i2c_writeb(bus, addr2);
if ((ret != 1) && !ignore_nack)
{
LOG_W("NACK: sending second addr");
return -RT_EIO;
}
if (flags & RT_I2C_RD)
{
LOG_D("send repeated start condition");
i2c_restart(ops);
addr1 |= 0x01;
ret = i2c_send_address(bus, addr1, retries);
if ((ret != 1) && !ignore_nack)
{
LOG_E("NACK: sending repeated addr");
return -RT_EIO;
}
}
}
else
{
/* 7-bit addr */
addr1 = msg->addr << 1;
if (flags & RT_I2C_RD)
addr1 |= 1;
ret = i2c_send_address(bus, addr1, retries);
if ((ret != 1) && !ignore_nack)
return -RT_EIO;
}
return RT_EOK;
}
static rt_ssize_t i2c_bit_xfer(struct rt_i2c_bus_device *bus,
struct rt_i2c_msg msgs[],
rt_uint32_t num)
{
struct rt_i2c_msg *msg;
struct rt_i2c_bit_ops *ops = (struct rt_i2c_bit_ops *)bus->priv;
rt_int32_t ret;
rt_uint32_t i;
rt_uint16_t ignore_nack;
if((ops->i2c_pin_init_flag == RT_FALSE) && (ops->pin_init != RT_NULL))
{
ops->pin_init();
ops->i2c_pin_init_flag = RT_TRUE;
}
if (num == 0) return 0;
for (i = 0; i < num; i++)
{
msg = &msgs[i];
ignore_nack = msg->flags & RT_I2C_IGNORE_NACK;
if (!(msg->flags & RT_I2C_NO_START))
{
if (i)
{
i2c_restart(ops);
}
else
{
LOG_D("send start condition");
i2c_start(ops);
}
ret = i2c_bit_send_address(bus, msg);
if ((ret != RT_EOK) && !ignore_nack)
{
LOG_D("receive NACK from device addr 0x%02x msg %d",
msgs[i].addr, i);
goto out;
}
}
if (msg->flags & RT_I2C_RD)
{
ret = i2c_recv_bytes(bus, msg);
if (ret >= 1)
{
LOG_D("read %d byte%s", ret, ret == 1 ? "" : "s");
}
if (ret < msg->len)
{
if (ret >= 0)
ret = -RT_EIO;
goto out;
}
}
else
{
ret = i2c_send_bytes(bus, msg);
if (ret >= 1)
{
LOG_D("write %d byte%s", ret, ret == 1 ? "" : "s");
}
if (ret < msg->len)
{
if (ret >= 0)
ret = -RT_ERROR;
goto out;
}
}
}
ret = i;
out:
if (!(msg->flags & RT_I2C_NO_STOP))
{
LOG_D("send stop condition");
i2c_stop(ops);
}
return ret;
}
static const struct rt_i2c_bus_device_ops i2c_bit_bus_ops =
{
i2c_bit_xfer,
RT_NULL,
RT_NULL
};
rt_err_t rt_i2c_bit_add_bus(struct rt_i2c_bus_device *bus,
const char *bus_name)
{
bus->ops = &i2c_bit_bus_ops;
return rt_i2c_bus_device_register(bus, bus_name);
}

View File

@@ -0,0 +1,182 @@
/*
* Copyright (c) 2006-2022, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2022-12-06 GuEe-GUI first version
*/
#include <rtdevice.h>
#define DBG_TAG "dev.i2c.bus"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
static struct rt_bus i2c_bus;
void i2c_bus_scan_clients(struct rt_i2c_bus_device *bus)
{
#ifdef RT_USING_OFW
if (bus->parent.ofw_node)
{
struct rt_ofw_node *np = bus->parent.ofw_node, *child_np, *i2c_client_np;
rt_ofw_foreach_available_child_node(np, child_np)
{
rt_uint32_t client_addr;
struct rt_i2c_client *client;
if (rt_ofw_prop_read_bool(child_np, "compatible"))
{
i2c_client_np = child_np;
}
else
{
/* Maybe in i2c-mux */
i2c_client_np = rt_ofw_get_next_child(child_np, RT_NULL);
if (!rt_ofw_prop_read_bool(i2c_client_np, "compatible"))
{
continue;
}
}
client = rt_calloc(1, sizeof(*client));
if (!client)
{
rt_ofw_node_put(i2c_client_np);
LOG_E("Not memory to create i2c client: %s",
rt_ofw_node_full_name(i2c_client_np));
return;
}
rt_ofw_prop_read_u32(i2c_client_np, "reg", &client_addr);
client->parent.ofw_node = i2c_client_np;
client->name = rt_ofw_node_name(i2c_client_np);
client->bus = bus;
client->client_addr = client_addr;
rt_i2c_device_register(client);
if (i2c_client_np != child_np)
{
rt_ofw_node_put(i2c_client_np);
}
}
}
#endif /* RT_USING_OFW */
}
rt_err_t rt_i2c_driver_register(struct rt_i2c_driver *driver)
{
RT_ASSERT(driver != RT_NULL);
driver->parent.bus = &i2c_bus;
return rt_driver_register(&driver->parent);
}
rt_err_t rt_i2c_device_register(struct rt_i2c_client *client)
{
RT_ASSERT(client != RT_NULL);
return rt_bus_add_device(&i2c_bus, &client->parent);
}
static rt_bool_t i2c_match(rt_driver_t drv, rt_device_t dev)
{
const struct rt_i2c_device_id *id;
struct rt_i2c_driver *driver = rt_container_of(drv, struct rt_i2c_driver, parent);
struct rt_i2c_client *client = rt_container_of(dev, struct rt_i2c_client, parent);
if ((id = driver->ids))
{
for (; id->name[0]; ++id)
{
if (!rt_strcmp(id->name, client->name))
{
client->id = id;
client->ofw_id = RT_NULL;
return RT_TRUE;
}
}
}
#ifdef RT_USING_OFW
client->ofw_id = rt_ofw_node_match(client->parent.ofw_node, driver->ofw_ids);
if (client->ofw_id)
{
client->id = RT_NULL;
return RT_TRUE;
}
#endif
return RT_FALSE;
}
static rt_err_t i2c_probe(rt_device_t dev)
{
rt_err_t err;
struct rt_i2c_driver *driver = rt_container_of(dev->drv, struct rt_i2c_driver, parent);
struct rt_i2c_client *client = rt_container_of(dev, struct rt_i2c_client, parent);
if (!client->bus)
{
return -RT_EINVAL;
}
err = driver->probe(client);
return err;
}
static rt_err_t i2c_remove(rt_device_t dev)
{
struct rt_i2c_driver *driver = rt_container_of(dev->drv, struct rt_i2c_driver, parent);
struct rt_i2c_client *client = rt_container_of(dev, struct rt_i2c_client, parent);
if (driver && driver->remove)
{
driver->remove(client);
}
return RT_EOK;
}
static rt_err_t i2c_shutdown(rt_device_t dev)
{
struct rt_i2c_driver *driver = rt_container_of(dev->drv, struct rt_i2c_driver, parent);
struct rt_i2c_client *client = rt_container_of(dev, struct rt_i2c_client, parent);
if (driver && driver->shutdown)
{
driver->shutdown(client);
}
return RT_EOK;
}
static struct rt_bus i2c_bus =
{
.name = "i2c",
.match = i2c_match,
.probe = i2c_probe,
.remove = i2c_remove,
.shutdown = i2c_shutdown,
};
static int i2c_bus_init(void)
{
rt_bus_register(&i2c_bus);
return 0;
}
INIT_CORE_EXPORT(i2c_bus_init);

View File

@@ -0,0 +1,153 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2012-04-25 weety first version
* 2021-04-20 RiceChen added support for bus control api
*/
#include <rtdevice.h>
#define DBG_TAG "I2C"
#ifdef RT_I2C_DEBUG
#define DBG_LVL DBG_LOG
#else
#define DBG_LVL DBG_INFO
#endif
#include <rtdbg.h>
rt_err_t rt_i2c_bus_device_register(struct rt_i2c_bus_device *bus,
const char *bus_name)
{
rt_err_t res = RT_EOK;
rt_mutex_init(&bus->lock, "i2c_bus_lock", RT_IPC_FLAG_PRIO);
if (bus->timeout == 0) bus->timeout = RT_TICK_PER_SECOND;
res = rt_i2c_bus_device_device_init(bus, bus_name);
LOG_D("I2C bus [%s] registered", bus_name);
#ifdef RT_USING_DM
if (!res)
{
i2c_bus_scan_clients(bus);
}
#endif
return res;
}
struct rt_i2c_bus_device *rt_i2c_bus_device_find(const char *bus_name)
{
struct rt_i2c_bus_device *bus;
rt_device_t dev = rt_device_find(bus_name);
if (dev == RT_NULL || dev->type != RT_Device_Class_I2CBUS)
{
LOG_E("I2C bus %s not exist", bus_name);
return RT_NULL;
}
bus = (struct rt_i2c_bus_device *)dev->user_data;
return bus;
}
rt_ssize_t rt_i2c_transfer(struct rt_i2c_bus_device *bus,
struct rt_i2c_msg msgs[],
rt_uint32_t num)
{
rt_ssize_t ret;
rt_err_t err;
if (bus->ops->master_xfer)
{
#ifdef RT_I2C_DEBUG
for (ret = 0; ret < num; ret++)
{
LOG_D("msgs[%d] %c, addr=0x%02x, len=%d", ret,
(msgs[ret].flags & RT_I2C_RD) ? 'R' : 'W',
msgs[ret].addr, msgs[ret].len);
}
#endif
err = rt_mutex_take(&bus->lock, RT_WAITING_FOREVER);
if (err != RT_EOK)
{
return (rt_ssize_t)err;
}
ret = bus->ops->master_xfer(bus, msgs, num);
err = rt_mutex_release(&bus->lock);
if (err != RT_EOK)
{
return (rt_ssize_t)err;
}
return ret;
}
else
{
LOG_E("I2C bus operation not supported");
return -RT_EINVAL;
}
}
rt_err_t rt_i2c_control(struct rt_i2c_bus_device *bus,
int cmd,
void *args)
{
rt_err_t ret;
if(bus->ops->i2c_bus_control)
{
ret = bus->ops->i2c_bus_control(bus, cmd, args);
return ret;
}
else
{
LOG_E("I2C bus operation not supported");
return -RT_EINVAL;
}
}
rt_ssize_t rt_i2c_master_send(struct rt_i2c_bus_device *bus,
rt_uint16_t addr,
rt_uint16_t flags,
const rt_uint8_t *buf,
rt_uint32_t count)
{
rt_ssize_t ret;
struct rt_i2c_msg msg;
msg.addr = addr;
msg.flags = flags;
msg.len = count;
msg.buf = (rt_uint8_t *)buf;
ret = rt_i2c_transfer(bus, &msg, 1);
return (ret == 1) ? count : ret;
}
rt_ssize_t rt_i2c_master_recv(struct rt_i2c_bus_device *bus,
rt_uint16_t addr,
rt_uint16_t flags,
rt_uint8_t *buf,
rt_uint32_t count)
{
rt_ssize_t ret;
struct rt_i2c_msg msg;
RT_ASSERT(bus != RT_NULL);
msg.addr = addr;
msg.flags = flags | RT_I2C_RD;
msg.len = count;
msg.buf = buf;
ret = rt_i2c_transfer(bus, &msg, 1);
return (ret == 1) ? count : ret;
}

View File

@@ -0,0 +1,137 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2012-04-25 weety first version
* 2014-08-03 bernard fix some compiling warning
* 2021-04-20 RiceChen added support for bus clock control
*/
#include <rtdevice.h>
#define DBG_TAG "I2C"
#ifdef RT_I2C_DEBUG
#define DBG_LVL DBG_LOG
#else
#define DBG_LVL DBG_INFO
#endif
#include <rtdbg.h>
static rt_ssize_t i2c_bus_device_read(rt_device_t dev,
rt_off_t pos,
void *buffer,
rt_size_t count)
{
rt_uint16_t addr;
rt_uint16_t flags;
struct rt_i2c_bus_device *bus = (struct rt_i2c_bus_device *)dev->user_data;
RT_ASSERT(bus != RT_NULL);
RT_ASSERT(buffer != RT_NULL);
LOG_D("I2C bus dev [%s] reading %u bytes.", dev->parent.name, count);
addr = pos & 0xffff;
flags = (pos >> 16) & 0xffff;
return rt_i2c_master_recv(bus, addr, flags, (rt_uint8_t *)buffer, count);
}
static rt_ssize_t i2c_bus_device_write(rt_device_t dev,
rt_off_t pos,
const void *buffer,
rt_size_t count)
{
rt_uint16_t addr;
rt_uint16_t flags;
struct rt_i2c_bus_device *bus = (struct rt_i2c_bus_device *)dev->user_data;
RT_ASSERT(bus != RT_NULL);
RT_ASSERT(buffer != RT_NULL);
LOG_D("I2C bus dev [%s] writing %u bytes.", dev->parent.name, count);
addr = pos & 0xffff;
flags = (pos >> 16) & 0xffff;
return rt_i2c_master_send(bus, addr, flags, (const rt_uint8_t *)buffer, count);
}
static rt_err_t i2c_bus_device_control(rt_device_t dev,
int cmd,
void *args)
{
rt_err_t ret;
struct rt_i2c_priv_data *priv_data;
struct rt_i2c_bus_device *bus = (struct rt_i2c_bus_device *)dev->user_data;
RT_ASSERT(bus != RT_NULL);
switch (cmd)
{
/* set 10-bit addr mode */
case RT_I2C_DEV_CTRL_10BIT:
bus->flags |= RT_I2C_ADDR_10BIT;
break;
case RT_I2C_DEV_CTRL_TIMEOUT:
bus->timeout = *(rt_uint32_t *)args;
break;
case RT_I2C_DEV_CTRL_RW:
priv_data = (struct rt_i2c_priv_data *)args;
ret = rt_i2c_transfer(bus, priv_data->msgs, priv_data->number);
if (ret < 0)
{
return -RT_EIO;
}
break;
default:
return rt_i2c_control(bus, cmd, args);
}
return RT_EOK;
}
#ifdef RT_USING_DEVICE_OPS
const static struct rt_device_ops i2c_ops =
{
RT_NULL,
RT_NULL,
RT_NULL,
i2c_bus_device_read,
i2c_bus_device_write,
i2c_bus_device_control
};
#endif
rt_err_t rt_i2c_bus_device_device_init(struct rt_i2c_bus_device *bus,
const char *name)
{
struct rt_device *device;
RT_ASSERT(bus != RT_NULL);
device = &bus->parent;
device->user_data = bus;
/* set device type */
device->type = RT_Device_Class_I2CBUS;
/* initialize device interface */
#ifdef RT_USING_DEVICE_OPS
device->ops = &i2c_ops;
#else
device->init = RT_NULL;
device->open = RT_NULL;
device->close = RT_NULL;
device->read = i2c_bus_device_read;
device->write = i2c_bus_device_write;
device->control = i2c_bus_device_control;
#endif
/* register to device manager */
rt_device_register(device, name, RT_DEVICE_FLAG_RDWR);
return RT_EOK;
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright (c) 2006-2022, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2022-12-06 GuEe-GUI first version
*/
#include <rtdevice.h>
#define DBG_TAG "dev.i2c.dm"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
#ifdef RT_USING_OFW
static void i2c_parse_timing(struct rt_ofw_node *dev_np, const char *propname,
rt_uint32_t *out_value, rt_uint32_t def_value, rt_bool_t use_defaults)
{
if (rt_ofw_prop_read_u32(dev_np, propname, out_value) && use_defaults)
{
*out_value = def_value;
}
}
rt_err_t i2c_timings_ofw_parse(struct rt_ofw_node *dev_np, struct i2c_timings *timings,
rt_bool_t use_defaults)
{
rt_ubase_t def;
rt_bool_t udef = use_defaults;
struct i2c_timings *t = timings;
i2c_parse_timing(dev_np, "clock-frequency", &t->bus_freq_hz, I2C_MAX_STANDARD_MODE_FREQ, udef);
def = t->bus_freq_hz <= I2C_MAX_STANDARD_MODE_FREQ ? 1000 : t->bus_freq_hz <= I2C_MAX_FAST_MODE_FREQ ? 300 : 120;
i2c_parse_timing(dev_np, "i2c-scl-rising-time-ns", &t->scl_rise_ns, def, udef);
def = t->bus_freq_hz <= I2C_MAX_FAST_MODE_FREQ ? 300 : 120;
i2c_parse_timing(dev_np, "i2c-scl-falling-time-ns", &t->scl_fall_ns, def, udef);
i2c_parse_timing(dev_np, "i2c-scl-internal-delay-ns", &t->scl_int_delay_ns, 0, udef);
i2c_parse_timing(dev_np, "i2c-sda-falling-time-ns", &t->sda_fall_ns, t->scl_fall_ns, udef);
i2c_parse_timing(dev_np, "i2c-sda-hold-time-ns", &t->sda_hold_ns, 0, udef);
i2c_parse_timing(dev_np, "i2c-digital-filter-width-ns", &t->digital_filter_width_ns, 0, udef);
i2c_parse_timing(dev_np, "i2c-analog-filter-cutoff-frequency", &t->analog_filter_cutoff_freq_hz, 0, udef);
return RT_EOK;
}
#endif /* RT_USING_OFW */

View File

@@ -0,0 +1,274 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-07-30 sp-cai first version
*/
#include <rtdevice.h>
#ifdef RT_USING_SOFT_I2C
#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)
#error "Please define at least one RT_USING_SOFT_I2Cx"
/*
This driver can be disabled at:
menuconfig -> RT-Thread Components -> Device Drivers -> Using I2C device drivers
*/
#endif
#define DBG_ENABLE
#define DBG_TAG "I2C_S"
#ifdef RT_I2C_BITOPS_DEBUG
#define DBG_LEVEL DBG_LOG
#endif
#include <rtdbg.h>
/* i2c config class */
struct soft_i2c_config
{
rt_base_t scl_pin;
rt_base_t sda_pin;
const char *bus_name;
rt_uint16_t timing_delay; /* scl and sda line delay */
rt_uint16_t timing_timeout; /* in tick */
};
/* i2c dirver class */
struct rt_soft_i2c
{
struct rt_i2c_bus_device i2c_bus;
struct rt_i2c_bit_ops ops;
};
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,
.sda_pin = RT_SOFT_I2C1_SDA_PIN,
.bus_name = RT_SOFT_I2C1_BUS_NAME,
.timing_delay = RT_SOFT_I2C1_TIMING_DELAY,
.timing_timeout = RT_SOFT_I2C1_TIMING_TIMEOUT,
},
#endif //RT_USING_SOFT_I2C1
#ifdef RT_USING_SOFT_I2C2
{
.scl_pin = RT_SOFT_I2C2_SCL_PIN,
.sda_pin = RT_SOFT_I2C2_SDA_PIN,
.bus_name = RT_SOFT_I2C2_BUS_NAME,
.timing_delay = RT_SOFT_I2C2_TIMING_DELAY,
.timing_timeout = RT_SOFT_I2C2_TIMING_TIMEOUT,
},
#endif //RT_USING_SOFT_I2C2
#ifdef RT_USING_SOFT_I2C3
{
.scl_pin = RT_SOFT_I2C3_SCL_PIN,
.sda_pin = RT_SOFT_I2C3_SDA_PIN,
.bus_name = RT_SOFT_I2C3_BUS_NAME,
.timing_delay = RT_SOFT_I2C3_TIMING_DELAY,
.timing_timeout = RT_SOFT_I2C3_TIMING_TIMEOUT,
},
#endif //RT_USING_SOFT_I2C3
#ifdef RT_USING_SOFT_I2C4
{
.scl_pin = RT_SOFT_I2C4_SCL_PIN,
.sda_pin = RT_SOFT_I2C4_SDA_PIN,
.bus_name = RT_SOFT_I2C4_BUS_NAME,
.timing_delay = RT_SOFT_I2C4_TIMING_DELAY,
.timing_timeout = RT_SOFT_I2C4_TIMING_TIMEOUT,
},
#endif //RT_USING_SOFT_I2C4
#ifdef RT_USING_SOFT_I2C5
{
.scl_pin = RT_SOFT_I2C5_SCL_PIN,
.sda_pin = RT_SOFT_I2C5_SDA_PIN,
.bus_name = RT_SOFT_I2C5_BUS_NAME,
.timing_delay = RT_SOFT_I2C5_TIMING_DELAY,
.timing_timeout = RT_SOFT_I2C5_TIMING_TIMEOUT,
},
#endif //RT_USING_SOFT_I2C5
#ifdef RT_USING_SOFT_I2C6
{
.scl_pin = RT_SOFT_I2C6_SCL_PIN,
.sda_pin = RT_SOFT_I2C6_SDA_PIN,
.bus_name = RT_SOFT_I2C6_BUS_NAME,
.timing_delay = RT_SOFT_I2C6_TIMING_DELAY,
.timing_timeout = RT_SOFT_I2C6_TIMING_TIMEOUT,
},
#endif //RT_USING_SOFT_I2C6
#ifdef RT_USING_SOFT_I2C7
{
.scl_pin = RT_SOFT_I2C7_SCL_PIN,
.sda_pin = RT_SOFT_I2C7_SDA_PIN,
.bus_name = RT_SOFT_I2C7_BUS_NAME,
.timing_delay = RT_SOFT_I2C7_TIMING_DELAY,
.timing_timeout = RT_SOFT_I2C7_TIMING_TIMEOUT,
},
#endif //RT_USING_SOFT_I2C7
#ifdef RT_USING_SOFT_I2C8
{
.scl_pin = RT_SOFT_I2C8_SCL_PIN,
.sda_pin = RT_SOFT_I2C8_SDA_PIN,
.bus_name = RT_SOFT_I2C8_BUS_NAME,
.timing_delay = RT_SOFT_I2C8_TIMING_DELAY,
.timing_timeout = RT_SOFT_I2C8_TIMING_TIMEOUT,
},
#endif //RT_USING_SOFT_I2C8
};
static struct rt_soft_i2c i2c_bus_obj[sizeof(i2c_cfg) / sizeof(i2c_cfg[0])] =
{ 0 };
/**
* This function initializes the i2c pin.
* @param i2c config class.
*/
static void pin_init(const struct soft_i2c_config *cfg)
{
rt_pin_mode(cfg->scl_pin, PIN_MODE_OUTPUT_OD);
rt_pin_mode(cfg->sda_pin, PIN_MODE_OUTPUT_OD);
rt_pin_write(cfg->scl_pin, PIN_HIGH);
rt_pin_write(cfg->sda_pin, PIN_HIGH);
}
/**
* This function sets the sda pin.
* @param i2c config class.
* @param The sda pin state.
*/
static void set_sda(void *cfg, rt_int32_t value)
{
rt_pin_write(((const struct soft_i2c_config*)cfg)->sda_pin, value);
}
/**
* This function sets the scl pin.
* @param i2c config class.
* @param The sda pin state.
*/
static void set_scl(void *cfg, rt_int32_t value)
{
rt_pin_write(((const struct soft_i2c_config*)cfg)->scl_pin, value);
}
/**
* This function gets the sda pin state.
* @param i2c config class.
*/
static rt_int32_t get_sda(void *cfg)
{
return rt_pin_read(((const struct soft_i2c_config*)cfg)->sda_pin);
}
/**
* This function gets the scl pin state.
* @param i2c config class.
*/
static rt_int32_t get_scl(void *cfg)
{
return rt_pin_read(((const struct soft_i2c_config*)cfg)->scl_pin);
}
static const struct rt_i2c_bit_ops soft_i2c_ops =
{
.set_sda = set_sda,
.set_scl = set_scl,
.get_sda = get_sda,
.get_scl = get_scl,
.udelay = rt_hw_us_delay,
};
/**
* if i2c is locked, this function will unlock it
*
* @param i2c config class.
*
* @return RT_EOK indicates successful unlock.
*/
static rt_err_t i2c_bus_unlock(const struct soft_i2c_config *cfg)
{
rt_ubase_t i = 0;
if(PIN_LOW == rt_pin_read(cfg->sda_pin))
{
while(i++ < 9)
{
rt_pin_write(cfg->scl_pin, PIN_HIGH);
rt_hw_us_delay(cfg->timing_delay);
rt_pin_write(cfg->scl_pin, PIN_LOW);
rt_hw_us_delay(cfg->timing_delay);
}
}
if(PIN_LOW == rt_pin_read(cfg->sda_pin))
{
return -RT_ERROR;
}
return RT_EOK;
}
/* I2C initialization function */
int rt_soft_i2c_init(void)
{
int err = RT_EOK;
struct rt_soft_i2c *obj;
int i;
for(i = 0; i < sizeof(i2c_bus_obj) / sizeof(i2c_bus_obj[0]); i++)
{
struct soft_i2c_config *cfg = &i2c_cfg[i];
pin_init(cfg);
obj = &i2c_bus_obj[i];
obj->ops = soft_i2c_ops;
obj->ops.data = cfg;
obj->i2c_bus.priv = &obj->ops;
obj->ops.delay_us = cfg->timing_delay;
obj->ops.timeout = cfg->timing_timeout;
if(rt_i2c_bit_add_bus(&obj->i2c_bus, cfg->bus_name) == RT_EOK)
{
i2c_bus_unlock(cfg);
LOG_D("Software simulation %s init done"
", SCL pin: 0x%02X, SDA pin: 0x%02X"
, cfg->bus_name
, cfg->scl_pin
, cfg->sda_pin
);
}
else
{
err++;
LOG_E("Software simulation %s init fail"
", SCL pin: 0x%02X, SDA pin: 0x%02X"
, cfg->bus_name
, cfg->scl_pin
, cfg->sda_pin
);
}
}
return err;
}
INIT_PREV_EXPORT(rt_soft_i2c_init);
#endif // RT_USING_SOFT_I2C

View File

@@ -0,0 +1,15 @@
from building import *
group = []
if not GetDepend(['RT_USING_DM']):
Return('group')
cwd = GetCurrentDir()
CPPPATH = [cwd + '/../include']
src = ['iio.c',]
group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH)
Return('group')

View File

@@ -0,0 +1,71 @@
/*
* Copyright (c) 2006-2022, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2022-3-08 GuEe-GUI the first version
*/
#include <rtthread.h>
#include <rtdevice.h>
static void *ofw_iio_channel_get_by_index(struct rt_ofw_node *np, int index, int *out_channel)
{
void *iio = RT_NULL;
#ifdef RT_USING_OFW
struct rt_ofw_node *iio_np;
struct rt_ofw_cell_args iio_args;
if (!rt_ofw_parse_phandle_cells(np, "io-channels", "#io-channel-cells", index, &iio_args))
{
iio_np = iio_args.data;
if (!rt_ofw_data(iio_np))
{
rt_platform_ofw_request(iio_np);
}
iio = rt_ofw_data(iio_np);
rt_ofw_node_put(iio_np);
if (out_channel)
{
*out_channel = iio_args.args[0];
}
}
#endif /* RT_USING_OFW */
return iio;
}
void *rt_iio_channel_get_by_index(struct rt_device *dev, int index, int *out_channel)
{
void *iio = RT_NULL;
if (!dev || index < 0)
{
return RT_NULL;
}
if (dev->ofw_node)
{
iio = ofw_iio_channel_get_by_index(dev->ofw_node, index, out_channel);
}
return iio;
}
void *rt_iio_channel_get_by_name(struct rt_device *dev, const char *name, int *out_channel)
{
int index;
if (!dev || !name)
{
return RT_NULL;
}
index = rt_dm_dev_prop_index_of_string(dev, "io-channel-names", name);
return rt_iio_channel_get_by_index(dev, index, out_channel);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
* Copyright (c) 2006-2024 RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
@@ -14,12 +14,65 @@
#define __ADC_H__
#include <rtthread.h>
/**
* @addtogroup Drivers RTTHREAD Driver
* @defgroup ADC ADC
*
* @brief ADC driver api
*
* <b>Example</b>
* @code {.c}
* #define ADC_DEV_NAME "adc1"
* #define ADC_DEV_CHANNEL 5
* #define REFER_VOLTAGE 330
* #define CONVERT_BITS (1 << 12)
*
* static int adc_vol_sample(int argc, char *argv[])
* {
* rt_adc_device_t adc_dev;
* rt_uint32_t value, vol;
*
* rt_err_t ret = RT_EOK;
*
* adc_dev = (rt_adc_device_t)rt_device_find(ADC_DEV_NAME);
* if (adc_dev == RT_NULL)
* {
* rt_kprintf("adc sample run failed! can't find %s device!\n", ADC_DEV_NAME);
* return -RT_ERROR;
* }
*
* ret = rt_adc_enable(adc_dev, ADC_DEV_CHANNEL);
*
* value = rt_adc_read(adc_dev, ADC_DEV_CHANNEL);
* rt_kprintf("the value is :%d \n", value);
*
* vol = value * REFER_VOLTAGE / CONVERT_BITS;
* rt_kprintf("the voltage is :%d.%02d \n", vol / 100, vol % 100);
*
* ret = rt_adc_disable(adc_dev, ADC_DEV_CHANNEL);
*
* return ret;
* }
* MSH_CMD_EXPORT(adc_vol_sample, adc voltage convert sample);
*
* @endcode
*
* @ingroup Drivers
*/
/*!
* @addtogroup ADC
* @{
*/
#define RT_ADC_INTERN_CH_TEMPER (-1)
#define RT_ADC_INTERN_CH_VREF (-2)
#define RT_ADC_INTERN_CH_VBAT (-3)
struct rt_adc_device;
/**
* @brief Configure the adc device
*/
struct rt_adc_ops
{
rt_err_t (*enabled)(struct rt_adc_device *device, rt_int8_t channel, rt_bool_t enabled);
@@ -27,7 +80,9 @@ struct rt_adc_ops
rt_uint8_t (*get_resolution)(struct rt_adc_device *device);
rt_int16_t (*get_vref) (struct rt_adc_device *device);
};
/**
* @brief adc device
*/
struct rt_adc_device
{
struct rt_device parent;
@@ -43,10 +98,53 @@ typedef enum
RT_ADC_CMD_GET_VREF = RT_DEVICE_CTRL_BASE(ADC) + 4, /* get reference voltage */
} rt_adc_cmd_t;
/**
* @brief register the adc device
* @param adc adc device
* @param name device name
* @param ops device ops
* @param user_data device private data
* @return rt_err_t error code
* @ingroup ADC
*/
rt_err_t rt_hw_adc_register(rt_adc_device_t adc,const char *name, const struct rt_adc_ops *ops, const void *user_data);
/**
* @brief read the adc value
* @param dev adc device
* @param channel adc channel
* @return rt_uint32_t adc value
* @ingroup ADC
*/
rt_uint32_t rt_adc_read(rt_adc_device_t dev, rt_int8_t channel);
/**
* @brief enable the adc channel
* @param dev adc device
* @param channel adc channel
* @return rt_err_t error code
* @ingroup ADC
*/
rt_err_t rt_adc_enable(rt_adc_device_t dev, rt_int8_t channel);
/**
* @brief disable the adc channel
* @param dev adc device
* @param channel adc channel
* @return rt_err_t error code
* @ingroup ADC
*/
rt_err_t rt_adc_disable(rt_adc_device_t dev, rt_int8_t channel);
/**
* @brief get the adc resolution
* @param dev adc device
* @param channel adc channel
* @return rt_int16_t adc resolution
* @ingroup ADC
*/
rt_int16_t rt_adc_voltage(rt_adc_device_t dev, rt_int8_t channel);
/*! @}*/
#endif /* __ADC_H__ */

View File

@@ -0,0 +1,397 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-02-25 GuEe-GUI the first version
*/
#ifndef __AHCI_H__
#define __AHCI_H__
#include <rthw.h>
#include <rtthread.h>
#include <drivers/scsi.h>
#include <drivers/misc.h>
struct rt_ahci_ops;
/* Generic Host Control */
#define RT_AHCI_HBA_CAP 0x00 /* Host capability*/
#define RT_AHCI_CAP_NP RT_GENMASK(4, 0) /* Number of Ports */
#define RT_AHCI_CAP_NCS RT_GENMASK(8, 12) /* Number of Command Slots */
#define RT_AHCI_CAP_PSC RT_BIT(13) /* Partial State Capable */
#define RT_AHCI_CAP_SSC RT_BIT(14) /* Slumber capable */
#define RT_AHCI_CAP_PMD RT_BIT(15) /* PIO Multiple DRQ Block */
#define RT_AHCI_CAP_SPM RT_BIT(17) /* Port Multiplier */
#define RT_AHCI_CAP_AHCI RT_BIT(18) /* AHCI only */
#define RT_AHCI_CAP_SNZO RT_BIT(19) /* Non-Zero DMA Offsets */
#define RT_AHCI_CAP_ISS RT_GENMASK(23, 20) /* Interface Speed Support */
#define RT_AHCI_CAP_CLO RT_BIT(24) /* Command List Override support */
#define RT_AHCI_CAP_SAL RT_BIT(25) /* Activity LED */
#define RT_AHCI_CAP_SALP RT_BIT(26) /* Aggressive Link Power Management */
#define RT_AHCI_CAP_SSS RT_BIT(27) /* Staggered Spin-up */
#define RT_AHCI_CAP_SIS RT_BIT(28) /* Interlock Switch */
#define RT_AHCI_CAP_NCQ RT_BIT(30) /* Native Command Queueing */
#define RT_AHCI_CAP_64 RT_BIT(31) /* PCI DAC (64-bit DMA) support */
#define RT_AHCI_HBA_GHC 0x04 /* Global host control */
#define RT_AHCI_GHC_RESET RT_BIT(0) /* Reset controller; self-clear */
#define RT_AHCI_GHC_IRQ_EN RT_BIT(1) /* Global IRQ enable */
#define RT_AHCI_GHC_AHCI_EN RT_BIT(31) /* AHCI enabled */
#define RT_AHCI_HBA_INTS 0x08 /* Interrupt status */
#define RT_AHCI_HBA_PI 0x0c /* Port implemented */
#define RT_AHCI_HBA_VS 0x10 /* Version */
#define RT_AHCI_HBA_CCC_CTL 0x14 /* Command completion coalescing control */
#define RT_AHCI_HBA_CCC_PTS 0x18 /* Command completion coalescing ports */
#define RT_AHCI_HBA_EM_LOC 0x1c /* Enclosure management location */
#define RT_AHCI_HBA_EM_CTL 0x20 /* Enclosure management control */
#define RT_AHCI_HBA_CAP2 0x24 /* Host capabilities extended */
#define RT_AHCI_HBA_BOHC 0x28 /* BIOS/OS handoff control and status */
#define RT_AHCI_HBA_VENDOR 0xa0 /* Vendor specific registers (0xa0 - 0xff) */
#define RT_AHCI_PORT_CLB 0x00 /* Command list base address, 1K-byte aligned */
#define RT_AHCI_PORT_CLBU 0x04 /* Command list base address upper 32 bits */
#define RT_AHCI_PORT_FB 0x08 /* FIS base address, 256-byte aligned */
#define RT_AHCI_PORT_FBU 0x0C /* FIS base address upper 32 bits */
#define RT_AHCI_PORT_INTS 0x10 /* Interrupt status */
#define RT_AHCI_PORT_INTE 0x14 /* Interrupt enable */
#define RT_AHCI_PORT_INTE_D2H_REG_FIS RT_BIT(0) /* D2H Register FIS rx'd */
#define RT_AHCI_PORT_INTE_PIOS_FIS RT_BIT(1) /* PIO Setup FIS rx'd */
#define RT_AHCI_PORT_INTE_DMAS_FIS RT_BIT(2) /* DMA Setup FIS rx'd */
#define RT_AHCI_PORT_INTE_SDB_FIS RT_BIT(3) /* Set Device Bits FIS rx'd */
#define RT_AHCI_PORT_INTE_UNK_FIS RT_BIT(4) /* Unknown FIS rx'd */
#define RT_AHCI_PORT_INTE_SG_DONE RT_BIT(5) /* Descriptor processed */
#define RT_AHCI_PORT_INTE_CONNECT RT_BIT(6) /* Port connect change status */
#define RT_AHCI_PORT_INTE_DMPS RT_BIT(7) /* Mechanical presence status */
#define RT_AHCI_PORT_INTE_PHYRDY RT_BIT(22) /* PhyRdy changed */
#define RT_AHCI_PORT_INTE_BAD_PMP RT_BIT(23) /* Incorrect port multiplier */
#define RT_AHCI_PORT_INTE_OVERFLOW RT_BIT(24) /* Xfer exhausted available S/G */
#define RT_AHCI_PORT_INTE_IF_NONFATAL RT_BIT(26) /* Interface non-fatal error */
#define RT_AHCI_PORT_INTE_IF_ERR RT_BIT(27) /* Interface fatal error */
#define RT_AHCI_PORT_INTE_HBUS_DATA_ERR RT_BIT(28) /* Host bus data error */
#define RT_AHCI_PORT_INTE_HBUS_ERR RT_BIT(29) /* Host bus fatal error */
#define RT_AHCI_PORT_INTE_TF_ERR RT_BIT(30) /* Task file error */
#define RT_AHCI_PORT_INTE_COLD_PRES RT_BIT(31) /* Cold presence detect */
#define RT_AHCI_PORT_CMD 0x18 /* Command and status */
#define RT_AHCI_PORT_CMD_START RT_BIT(0) /* Enable port DMA engine */
#define RT_AHCI_PORT_CMD_SPIN_UP RT_BIT(1) /* Spin up device */
#define RT_AHCI_PORT_CMD_POWER_ON RT_BIT(2) /* Power up device */
#define RT_AHCI_PORT_CMD_CLO RT_BIT(3) /* Command list override */
#define RT_AHCI_PORT_CMD_FIS_RX RT_BIT(4) /* Enable FIS receive DMA engine */
#define RT_AHCI_PORT_CMD_FIS_ON RT_BIT(14) /* FIS DMA engine running */
#define RT_AHCI_PORT_CMD_LIST_ON RT_BIT(15) /* cmd list DMA engine running */
#define RT_AHCI_PORT_CMD_ATAPI RT_BIT(24) /* Device is ATAPI */
#define RT_AHCI_PORT_CMD_ACTIVE RT_BIT(28) /* Active state */
#define RT_AHCI_PORT_TFD 0x20 /* Task file data */
#define RT_AHCI_PORT_TFDATA_ERR RT_BIT(0) /* Indicates an error during the transfer */
#define RT_AHCI_PORT_TFDATA_DRQ RT_BIT(3) /* Indicates a data transfer is requested */
#define RT_AHCI_PORT_TFDATA_BSY RT_BIT(7) /* Indicates the interface is busy */
#define RT_AHCI_PORT_SIG 0x24 /* Signature */
#define RT_AHCI_PORT_SIG_REG_MASK 0xff
#define RT_AHCI_PORT_SIG_SECTOR_NR_SHIFT 0 /* Sector Count Register */
#define RT_AHCI_PORT_SIG_LBA_LOW_SHIFT 8 /* LBA Low Register */
#define RT_AHCI_PORT_SIG_LBA_MID_SHIFT 16 /* LBA Mid Register */
#define RT_AHCI_PORT_SIG_LBA_HIGH_SHIFT 24 /* LBA High Register */
#define RT_AHCI_PORT_SIG_SATA_CDROM 0xeb140101
#define RT_AHCI_PORT_SIG_SATA_DISK 0x00000101
#define RT_AHCI_PORT_SSTS 0x28 /* SATA status (SCR0:SStatus) */
#define RT_AHCI_PORT_SSTS_DET_MASK 0x3
#define RT_AHCI_PORT_SSTS_DET_COMINIT 0x1
#define RT_AHCI_PORT_SSTS_DET_PHYRDY 0x3
#define RT_AHCI_PORT_SCTL 0x2c /* SATA control (SCR2:SControl) */
#define RT_AHCI_PORT_SERR 0x30 /* SATA error (SCR1:SError) */
#define RT_AHCI_PORT_SERR_ERR_I RT_BIT(0) /* Recovered Data Integrity Error */
#define RT_AHCI_PORT_SERR_ERR_M RT_BIT(1) /* Recovered Communications Error */
#define RT_AHCI_PORT_SERR_ERR_T RT_BIT(8) /* Transient Data Integrity Error */
#define RT_AHCI_PORT_SERR_ERR_C RT_BIT(9) /* Persistent Communication or Data Integrity Error */
#define RT_AHCI_PORT_SERR_ERR_P RT_BIT(10) /* Protocol Error */
#define RT_AHCI_PORT_SERR_ERR_E RT_BIT(11) /* Internal Error */
#define RT_AHCI_PORT_SERR_DIAG_N RT_BIT(16) /* PhyRdy Change */
#define RT_AHCI_PORT_SERR_DIAG_I RT_BIT(17) /* Phy Internal Error */
#define RT_AHCI_PORT_SERR_DIAG_W RT_BIT(18) /* Comm Wake */
#define RT_AHCI_PORT_SERR_DIAG_B RT_BIT(19) /* 10B to 8B Decode Error */
#define RT_AHCI_PORT_SERR_DIAG_D RT_BIT(20) /* Disparity Error */
#define RT_AHCI_PORT_SERR_DIAG_C RT_BIT(21) /* CRC Error */
#define RT_AHCI_PORT_SERR_DIAG_H RT_BIT(22) /* Handshake Error */
#define RT_AHCI_PORT_SERR_DIAG_S RT_BIT(23) /* Link Sequence Error */
#define RT_AHCI_PORT_SERR_DIAG_T RT_BIT(24) /* Transport state transition error */
#define RT_AHCI_PORT_SERR_DIAG_F RT_BIT(25) /* Unknown FIS Type */
#define RT_AHCI_PORT_SERR_DIAG_X RT_BIT(26) /* Exchanged */
#define RT_AHCI_PORT_SACT 0x34 /* SATA active (SCR3:SActive) */
#define RT_AHCI_PORT_CI 0x38 /* Command issue */
#define RT_AHCI_PORT_SNTF 0x3c /* SATA notification (SCR4:SNotification) */
#define RT_AHCI_PORT_FBS 0x40 /* FIS-based switch control */
#define RT_AHCI_PORT_VENDOR 0x70 /* Vendor specific (0x70 - 0x7f) */
#define RT_AHCI_MAX_SG 56
#define RT_AHCI_CMD_SLOT_SIZE 32
#define RT_AHCI_MAX_CMD_SLOT 32
#define RT_AHCI_RX_FIS_SIZE 256
#define RT_AHCI_CMD_TBL_HDR 0x80
#define RT_AHCI_CMD_TBL_CDB 0x40
#define RT_AHCI_CMD_TBL_SIZE RT_AHCI_CMD_TBL_HDR + (RT_AHCI_MAX_SG * 16)
#define RT_AHCI_DMA_SIZE (RT_AHCI_CMD_SLOT_SIZE * RT_AHCI_MAX_CMD_SLOT + RT_AHCI_CMD_TBL_SIZE + RT_AHCI_RX_FIS_SIZE)
#define RT_ACHI_PRDT_BYTES_MAX (4 * 1024 * 1024)
#define RT_AHCI_FIS_TYPE_REG_H2D 0x27 /* Register FIS - host to device */
#define RT_AHCI_FIS_TYPE_REG_D2H 0x34 /* Register FIS - device to host */
#define RT_AHCI_FIS_TYPE_DMA_ACT 0x39 /* DMA activate FIS - device to host */
#define RT_AHCI_FIS_TYPE_DMA_SETUP 0x41 /* DMA setup FIS - bidirectional */
#define RT_AHCI_FIS_TYPE_DATA 0x46 /* Data FIS - bidirectional */
#define RT_AHCI_FIS_TYPE_BIST 0x58 /* BIST activate FIS - bidirectional */
#define RT_AHCI_FIS_TYPE_PIO_SETUP 0x5f /* PIO setup FIS - device to host */
#define RT_AHCI_FIS_TYPE_DEV_BITS 0xa1 /* Set device bits FIS - device to host */
#define RT_AHCI_ATA_ID_WORDS 256
#define RT_AHCI_ATA_ID_CONFIG 0
#define RT_AHCI_ATA_ID_CYLS 1
#define RT_AHCI_ATA_ID_HEADS 3
#define RT_AHCI_ATA_ID_SECTORS 6
#define RT_AHCI_ATA_ID_SERNO 10
#define RT_AHCI_ATA_ID_BUF_SIZE 21
#define RT_AHCI_ATA_ID_FW_REV 23
#define RT_AHCI_ATA_ID_PROD 27
#define RT_AHCI_ATA_ID_MAX_MULTSECT 47
#define RT_AHCI_ATA_ID_DWORD_IO 48
#define RT_AHCI_ATA_ID_TRUSTED 48
#define RT_AHCI_ATA_ID_CAPABILITY 49
#define RT_AHCI_ATA_ID_OLD_PIO_MODES 51
#define RT_AHCI_ATA_ID_OLD_DMA_MODES 52
#define RT_AHCI_ATA_ID_FIELD_VALID 53
#define RT_AHCI_ATA_ID_CUR_CYLS 54
#define RT_AHCI_ATA_ID_CUR_HEADS 55
#define RT_AHCI_ATA_ID_CUR_SECTORS 56
#define RT_AHCI_ATA_ID_MULTSECT 59
#define RT_AHCI_ATA_ID_LBA_CAPACITY 60
#define RT_AHCI_ATA_ID_SWDMA_MODES 62
#define RT_AHCI_ATA_ID_MWDMA_MODES 63
#define RT_AHCI_ATA_ID_PIO_MODES 64
#define RT_AHCI_ATA_ID_EIDE_DMA_MIN 65
#define RT_AHCI_ATA_ID_EIDE_DMA_TIME 66
#define RT_AHCI_ATA_ID_EIDE_PIO 67
#define RT_AHCI_ATA_ID_EIDE_PIO_IORDY 68
#define RT_AHCI_ATA_ID_ADDITIONAL_SUPP 69
#define RT_AHCI_ATA_ID_QUEUE_DEPTH 75
#define RT_AHCI_ATA_ID_SATA_CAPABILITY 76
#define RT_AHCI_ATA_ID_SATA_CAPABILITY_2 77
#define RT_AHCI_ATA_ID_FEATURE_SUPP 78
#define RT_AHCI_ATA_ID_MAJOR_VER 80
#define RT_AHCI_ATA_ID_COMMAND_SET_1 82
#define RT_AHCI_ATA_ID_COMMAND_SET_2 83
#define RT_AHCI_ATA_ID_CFSSE 84
#define RT_AHCI_ATA_ID_CFS_ENABLE_1 85
#define RT_AHCI_ATA_ID_CFS_ENABLE_2 86
#define RT_AHCI_ATA_ID_CSF_DEFAULT 87
#define RT_AHCI_ATA_ID_UDMA_MODES 88
#define RT_AHCI_ATA_ID_HW_CONFIG 93
#define RT_AHCI_ATA_ID_SPG 98
#define RT_AHCI_ATA_ID_LBA_CAPACITY_2 100
#define RT_AHCI_ATA_ID_SECTOR_SIZE 106
#define RT_AHCI_ATA_ID_WWN 108
#define RT_AHCI_ATA_ID_LOGICAL_SECTOR_SIZE 117
#define RT_AHCI_ATA_ID_COMMAND_SET_3 119
#define RT_AHCI_ATA_ID_COMMAND_SET_4 120
#define RT_AHCI_ATA_ID_LAST_LUN 126
#define RT_AHCI_ATA_ID_DLF 128
#define RT_AHCI_ATA_ID_CSFO 129
#define RT_AHCI_ATA_ID_CFA_POWER 160
#define RT_AHCI_ATA_ID_CFA_KEY_MGMT 162
#define RT_AHCI_ATA_ID_CFA_MODES 163
#define RT_AHCI_ATA_ID_DATA_SET_MGMT 169
#define RT_AHCI_ATA_ID_SCT_CMD_XPORT 206
#define RT_AHCI_ATA_ID_ROT_SPEED 217
#define RT_AHCI_ATA_ID_PIO4 (1 << 1)
#define RT_AHCI_ATA_ID_SERNO_LEN 20
#define RT_AHCI_ATA_ID_FW_REV_LEN 8
#define RT_AHCI_ATA_ID_PROD_LEN 40
#define RT_AHCI_ATA_ID_WWN_LEN 8
#define RT_AHCI_ATA_CMD_DSM 0x06
#define RT_AHCI_ATA_CMD_DEV_RESET 0x08 /* ATAPI device reset */
#define RT_AHCI_ATA_CMD_PIO_READ 0x20 /* Read sectors with retry */
#define RT_AHCI_ATA_CMD_PIO_READ_EXT 0x24
#define RT_AHCI_ATA_CMD_READ_EXT 0x25
#define RT_AHCI_ATA_CMD_READ_NATIVE_MAX_EXT 0x27
#define RT_AHCI_ATA_CMD_READ_MULTI_EXT 0x29
#define RT_AHCI_ATA_CMD_READ_LOG_EXT 0x2f
#define RT_AHCI_ATA_CMD_PIO_WRITE 0x30 /* Write sectors with retry */
#define RT_AHCI_ATA_CMD_PIO_WRITE_EXT 0x34
#define RT_AHCI_ATA_CMD_WRITE_EXT 0x35
#define RT_AHCI_ATA_CMD_SET_MAX_EXT 0x37
#define RT_AHCI_ATA_CMD_WRITE_MULTI_EXT 0x39
#define RT_AHCI_ATA_CMD_WRITE_FUA_EXT 0x3d
#define RT_AHCI_ATA_CMD_VERIFY 0x40 /* Read verify sectors with retry */
#define RT_AHCI_ATA_CMD_VERIFY_EXT 0x42
#define RT_AHCI_ATA_CMD_FPDMA_READ 0x60
#define RT_AHCI_ATA_CMD_FPDMA_WRITE 0x61
#define RT_AHCI_ATA_CMD_EDD 0x90 /* Execute device diagnostic */
#define RT_AHCI_ATA_CMD_INIT_DEV_PARAMS 0x91 /* Initialize device parameters */
#define RT_AHCI_ATA_CMD_PACKET 0xa0 /* ATAPI packet */
#define RT_AHCI_ATA_CMD_ID_ATAPI 0xa1 /* ATAPI identify device */
#define RT_AHCI_ATA_CMD_CONF_OVERLAY 0xb1
#define RT_AHCI_ATA_CMD_READ_MULTI 0xc4 /* Read multiple */
#define RT_AHCI_ATA_CMD_WRITE_MULTI 0xc5 /* Write multiple */
#define RT_AHCI_ATA_CMD_SET_MULTI 0xc6 /* Set multiple mode */
#define RT_AHCI_ATA_CMD_READ 0xc8 /* Read DMA with retry */
#define RT_AHCI_ATA_CMD_WRITE 0xca /* Write DMA with retry */
#define RT_AHCI_ATA_CMD_WRITE_MULTI_FUA_EXT 0xce
#define RT_AHCI_ATA_CMD_STANDBYNOW1 0xe0 /* Standby immediate */
#define RT_AHCI_ATA_CMD_IDLEIMMEDIATE 0xe1 /* Idle immediate */
#define RT_AHCI_ATA_CMD_STANDBY 0xe2 /* Place in standby power mode */
#define RT_AHCI_ATA_CMD_IDLE 0xe3 /* Place in idle power mode */
#define RT_AHCI_ATA_CMD_PMP_READ 0xe4 /* Read buffer */
#define RT_AHCI_ATA_CMD_CHK_POWER 0xe5 /* Check power mode */
#define RT_AHCI_ATA_CMD_SLEEP 0xe6 /* Sleep */
#define RT_AHCI_ATA_CMD_FLUSH 0xe7
#define RT_AHCI_ATA_CMD_PMP_WRITE 0xe8 /* Write buffer */
#define RT_AHCI_ATA_CMD_FLUSH_EXT 0xea
#define RT_AHCI_ATA_CMD_ID_ATA 0xec /* Identify device */
#define RT_AHCI_ATA_CMD_SET_FEATURES 0xef /* Set features */
#define RT_AHCI_ATA_CMD_SEC_FREEZE_LOCK 0xf5 /* Security freeze */
#define RT_AHCI_ATA_CMD_READ_NATIVE_MAX 0xf8
#define RT_AHCI_ATA_CMD_SET_MAX 0xf9
#define RT_AHCI_ATA_DSM_TRIM 0x01
#define RT_AHCI_ATA_PROT_FLAG_PIO RT_BIT(0)
#define RT_AHCI_ATA_PROT_FLAG_DMA RT_BIT(1)
#define RT_AHCI_ATA_PROT_FLAG_NCQ RT_BIT(2)
#define RT_AHCI_ATA_PROT_FLAG_ATAPI RT_BIT(3)
#define rt_ahci_ata_id_is_ata(id) (((id)[0] & (1 << 15)) == 0)
#define rt_ahci_ata_id_has_lba(id) ((id)[49] & (1 << 9))
#define rt_ahci_ata_id_has_dma(id) ((id)[49] & (1 << 8))
#define rt_ahci_ata_id_has_ncq(id) ((id)[76] & (1 << 8))
#define rt_ahci_ata_id_queue_depth(id) (((id)[75] & 0x1f) + 1)
#define rt_ahci_ata_id_removeable(id) ((id)[0] & (1 << 7))
#define rt_ahci_ata_id_iordy_disable(id) ((id)[49] & (1 << 10))
#define rt_ahci_ata_id_has_iordy(id) ((id)[49] & (1 << 11))
#define rt_ahci_ata_id_u32(id, n) (((rt_uint32_t)(id)[(n) + 1] << 16) | ((rt_uint32_t) (id)[(n)]))
#define rt_ahci_ata_id_u64(id, n) (((rt_uint64_t)(id)[(n) + 3] << 48) | ((rt_uint64_t)(id)[(n) + 2] << 32) | \
((rt_uint64_t)(id)[(n) + 1] << 16) | ((rt_uint64_t)(id)[(n) + 0]) )
rt_inline rt_bool_t rt_ahci_ata_id_has_lba48(const rt_uint16_t *id)
{
if ((id[RT_AHCI_ATA_ID_COMMAND_SET_2] & 0xc000) != 0x4000 ||
!rt_ahci_ata_id_u64(id, RT_AHCI_ATA_ID_LBA_CAPACITY_2))
{
return 0;
}
return !!(id[RT_AHCI_ATA_ID_COMMAND_SET_2] & (1 << 10));
}
rt_inline rt_uint64_t rt_ahci_ata_id_n_sectors(rt_uint16_t *id)
{
if (rt_ahci_ata_id_has_lba(id))
{
if (rt_ahci_ata_id_has_lba48(id))
{
return rt_ahci_ata_id_u64(id, RT_AHCI_ATA_ID_LBA_CAPACITY_2);
}
return rt_ahci_ata_id_u32(id, RT_AHCI_ATA_ID_LBA_CAPACITY);
}
return 0;
}
rt_inline rt_bool_t rt_ahci_ata_id_wcache_enabled(const rt_uint16_t *id)
{
if ((id[RT_AHCI_ATA_ID_CSF_DEFAULT] & 0xc000) != 0x4000)
{
return RT_FALSE;
}
return id[RT_AHCI_ATA_ID_CFS_ENABLE_1] & (1 << 5);
}
rt_inline rt_bool_t rt_ahci_ata_id_has_flush(const rt_uint16_t *id)
{
if ((id[RT_AHCI_ATA_ID_COMMAND_SET_2] & 0xc000) != 0x4000)
{
return RT_FALSE;
}
return id[RT_AHCI_ATA_ID_COMMAND_SET_2] & (1 << 12);
}
rt_inline rt_bool_t rt_ahci_ata_id_has_flush_ext(const rt_uint16_t *id)
{
if ((id[RT_AHCI_ATA_ID_COMMAND_SET_2] & 0xc000) != 0x4000)
{
return RT_FALSE;
}
return id[RT_AHCI_ATA_ID_COMMAND_SET_2] & (1 << 13);
}
struct rt_ahci_cmd_hdr
{
rt_uint32_t opts;
rt_uint32_t status;
rt_uint32_t tbl_addr_lo;
rt_uint32_t tbl_addr_hi;
rt_uint32_t reserved[4];
};
struct rt_ahci_sg
{
rt_uint32_t addr_lo;
rt_uint32_t addr_hi;
rt_uint32_t reserved;
rt_uint32_t flags_size;
};
struct rt_ahci_port
{
void *regs;
void *dma;
rt_ubase_t dma_handle;
struct rt_ahci_cmd_hdr *cmd_slot;
struct rt_ahci_sg *cmd_tbl_sg;
void *cmd_tbl;
rt_ubase_t cmd_tbl_dma;
void *rx_fis;
rt_uint32_t int_enabled;
rt_size_t block_size;
rt_uint16_t *ataid;
rt_bool_t link;
struct rt_completion done;
};
struct rt_ahci_host
{
struct rt_scsi_host parent;
int irq;
void *regs;
rt_size_t ports_nr;
rt_uint32_t ports_map;
struct rt_ahci_port ports[32];
rt_uint32_t cap;
rt_uint32_t max_blocks;
const struct rt_ahci_ops *ops;
};
struct rt_ahci_ops
{
rt_err_t (*host_init)(struct rt_ahci_host *host);
rt_err_t (*port_init)(struct rt_ahci_host *host, struct rt_ahci_port *port);
rt_err_t (*port_link_up)(struct rt_ahci_host *host, struct rt_ahci_port *port);
rt_err_t (*port_dma_init)(struct rt_ahci_host *host, struct rt_ahci_port *port);
rt_err_t (*port_isr)(struct rt_ahci_host *host, struct rt_ahci_port *port, rt_uint32_t isr);
};
rt_err_t rt_ahci_host_register(struct rt_ahci_host *host);
rt_err_t rt_ahci_host_unregister(struct rt_ahci_host *host);
#endif /* __AHCI_H__ */

View File

@@ -0,0 +1,87 @@
/*
* Copyright (c) 2006-2022, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-02-25 GuEe-GUI first version
*/
#ifndef __BLK_H__
#define __BLK_H__
#include <rthw.h>
#include <rtthread.h>
#include <drivers/classes/block.h>
struct rt_dm_ida;
struct rt_blk_device;
struct rt_blk_disk_ops;
struct rt_blk_disk
{
struct rt_device parent;
const struct rt_blk_disk_ops *ops;
#ifdef RT_USING_DM
struct rt_dm_ida *ida;
#endif
rt_uint32_t read_only:1;
rt_uint32_t parallel_io:1;
rt_uint32_t removable:1;
#define RT_BLK_DISK_MAGIC 0xbdaabdaa
rt_uint32_t __magic;
rt_uint32_t partitions;
#define RT_BLK_PARTITION_NONE (-1)
#define RT_BLK_PARTITION_MAX (RT_UINT32_MAX >> 1)
rt_int32_t max_partitions;
rt_list_t part_nodes;
struct rt_spinlock lock;
struct rt_semaphore usr_lock;
};
struct rt_blk_disk_ops
{
rt_ssize_t (*read)(struct rt_blk_disk *disk, rt_off_t sector, void *buffer,
rt_size_t sector_count);
rt_ssize_t (*write)(struct rt_blk_disk *disk, rt_off_t sector, const void *buffer,
rt_size_t sector_count);
rt_err_t (*getgeome)(struct rt_blk_disk *disk, struct rt_device_blk_geometry *geometry);
rt_err_t (*sync)(struct rt_blk_disk *disk);
rt_err_t (*erase)(struct rt_blk_disk *disk);
rt_err_t (*autorefresh)(struct rt_blk_disk *disk, rt_bool_t is_auto);
rt_err_t (*control)(struct rt_blk_disk *disk, struct rt_blk_device *blk, int cmd, void *args);
};
#ifndef __DFS_H__
#include <dfs_fs.h>
struct rt_blk_device
{
struct rt_device parent;
int partno;
struct dfs_partition partition;
rt_list_t list;
struct rt_blk_disk *disk;
rt_size_t sector_start;
rt_size_t sector_count;
};
#else
struct rt_blk_device;
#endif /* __DFS_H__ */
rt_err_t rt_hw_blk_disk_register(struct rt_blk_disk *disk);
rt_err_t rt_hw_blk_disk_unregister(struct rt_blk_disk *disk);
rt_err_t rt_blk_disk_probe_partition(struct rt_blk_disk *disk);
rt_ssize_t rt_blk_disk_get_capacity(struct rt_blk_disk *disk);
rt_ssize_t rt_blk_disk_get_logical_block_size(struct rt_blk_disk *disk);
#endif /* __BLK_H__ */

View File

@@ -16,6 +16,8 @@
#include <ref.h>
#include <drivers/ofw.h>
#define RT_CLK_NODE_OBJ_NAME "CLKNP"
struct rt_clk_ops;
struct rt_reset_control_node;
@@ -37,6 +39,8 @@ struct rt_clk_node
* };
* We assume the 'N' is the max value of element in 'clock-indices' if OFW.
*/
struct rt_object rt_parent;
rt_list_t list;
rt_list_t children_nodes;
@@ -74,6 +78,8 @@ struct rt_clk
const char *con_id;
rt_ubase_t rate;
int prepare_count;
int enable_count;
void *fw_node;
void *priv;

Some files were not shown because too many files have changed in this diff Show More