Просмотр исходного кода

[DFS] Support 9PFS

What is 9PFS (https://en.wikipedia.org/wiki/9P_(protocol)):
9P (or the Plan 9 Filesystem Protocol or Styx) is a network protocol developed for the Plan 9 from Bell Labs distributed operating system as the means of connecting the components of a Plan 9 system. Files are key objects in Plan 9. They represent windows, network connections, processes, and almost anything else available in the operating system.

rt-thread could share filesystem in VM mode with 9pfs such as QEMU...

Signed-off-by: GuEe-GUI <2991707448@qq.com>
GuEe-GUI 1 месяц назад
Родитель
Сommit
2ffa4f3d60

+ 6 - 0
components/dfs/Kconfig

@@ -163,6 +163,12 @@ endif
         default y
         default y
 
 
 if RT_USING_DFS_V1
 if RT_USING_DFS_V1
+    config RT_USING_DFS_9PFS
+        bool "Using Plan 9 remote filesystem"
+        select RT_USING_ADT_BITMAP
+        depends on RT_USING_MEMHEAP
+        default n
+
     config RT_USING_DFS_ISO9660
     config RT_USING_DFS_ISO9660
         bool "Using ISO9660 filesystem"
         bool "Using ISO9660 filesystem"
         depends on RT_USING_MEMHEAP
         depends on RT_USING_MEMHEAP

+ 11 - 0
components/dfs/dfs_v1/filesystems/9pfs/SConscript

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

+ 1186 - 0
components/dfs/dfs_v1/filesystems/9pfs/dfs_9pfs.c

@@ -0,0 +1,1186 @@
+/*
+ * 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.9p"
+#define DBG_LVL DBG_INFO
+#include <rtdbg.h>
+
+#include "dfs_9pfs.h"
+#include <drivers/misc.h>
+#include <drivers/byteorder.h>
+#include <posix/string.h>
+
+#define OREAD   0       /* open for read */
+#define OWRITE  1       /* write */
+#define ORDWR   2       /* read and write */
+#define OEXEC   3       /* execute, == read but check execute permission */
+#define OEXCL   4
+#define OTRUNC  16      /* or'ed in (except for exec), truncate file first */
+#define OCEXEC  32      /* or'ed in, close on exec */
+#define ORCLOSE 64      /* or'ed in, remove on close */
+#define OAPPEND 128     /* or'ed in, append */
+
+#define NOTAG   ((rt_uint16_t)~0)
+#define NOFID   ((rt_uint32_t)~0)
+#define TAG     1
+
+static rt_list_t _protocol_nodes = RT_LIST_OBJECT_INIT(_protocol_nodes);
+static struct rt_spinlock _protocol_lock = { 0 };
+
+#ifndef rt_le8_to_cpu
+#define rt_le8_to_cpu
+#endif
+#ifndef rt_cpu_to_le8
+#define rt_cpu_to_le8
+#endif
+
+#define P9_OF_OPS_GET(width, dir) \
+rt_inline rt_uint##width##_t get_##dir##_##value##width##_of(   \
+        struct p9_connection *conn, unsigned idx)               \
+{                                                               \
+    rt_uint##width##_t *vp = (void *)&conn->dir##_buffer[idx];  \
+    return rt_le##width##_to_cpu(*vp);                          \
+}
+
+#define P9_OF_OPS_PUT(width, dir) \
+rt_inline void put_##dir##_##value##width##_of(                 \
+        struct p9_connection *conn, unsigned idx,               \
+        rt_uint##width##_t value)                               \
+{                                                               \
+    rt_uint##width##_t *vp = (void *)&conn->dir##_buffer[idx];  \
+    *vp = rt_cpu_to_le##width(value);                           \
+}
+
+#define P9_OPS_PUT(width) \
+rt_inline rt_off_t put_value##width(struct p9_connection *conn, \
+        rt_off_t off, rt_uint##width##_t value)                 \
+{                                                               \
+    ((rt_uint##width##_t *)(conn->tx_buffer + off))[0] =        \
+            rt_cpu_to_le##width(value);                         \
+    return off + sizeof(value);                                 \
+}
+
+#define P9_OPS_GROUP(width)     \
+    P9_OF_OPS_GET(width, rx)    \
+    P9_OF_OPS_GET(width, tx)    \
+    P9_OF_OPS_PUT(width, rx)    \
+    P9_OF_OPS_PUT(width, tx)    \
+    P9_OPS_PUT(width)
+
+P9_OPS_GROUP(8)
+P9_OPS_GROUP(16)
+P9_OPS_GROUP(32)
+P9_OPS_GROUP(64)
+#undef P9_OF_OPS_GET
+#undef P9_OF_OPS_PUT
+#undef P9_OPS_GET
+#undef P9_OPS_GROUP
+
+rt_inline rt_uint32_t put_header(struct p9_connection *conn,
+        rt_uint8_t hd, rt_uint16_t tag_flags)
+{
+    rt_uint8_t *stack = conn->tx_buffer;
+
+    ((rt_uint32_t *)stack)[0] = rt_cpu_to_le32(0);
+    stack += sizeof(rt_uint32_t);
+
+    stack[0] = hd;
+    stack += sizeof(rt_uint8_t);
+
+    ((rt_uint16_t *)stack)[0] = rt_cpu_to_le16(tag_flags);
+    stack += sizeof(rt_uint16_t);
+
+    return stack - conn->tx_buffer;
+}
+
+rt_inline rt_off_t put_bytes(struct p9_connection *conn, rt_off_t off,
+        rt_uint8_t *bytes, rt_uint32_t count)
+{
+    rt_uint8_t *stack = conn->tx_buffer + off;
+
+    ((rt_uint16_t *)stack)[0] = rt_cpu_to_le16(count);
+    stack += sizeof(rt_uint16_t);
+
+    rt_memcpy(stack, bytes, count);
+    stack += count;
+
+    return stack - conn->tx_buffer;
+}
+
+rt_inline rt_off_t put_string(struct p9_connection *conn, rt_off_t off,
+        char *string)
+{
+    rt_uint32_t len = rt_strlen(string);
+    rt_uint8_t *stack = conn->tx_buffer + off;
+
+    ((rt_uint16_t *)stack)[0] = rt_cpu_to_le16(len);
+    stack += sizeof(rt_uint16_t);
+
+    rt_memcpy(stack, string, len);
+    stack += len;
+
+    return stack - conn->tx_buffer;
+}
+
+struct p9_connection *p9_connection_alloc(struct p9_protocol *p9p,
+        const char *aname, rt_uint32_t buffer_size)
+{
+    struct p9_connection *conn;
+
+    if (!p9p || !aname)
+    {
+        return RT_NULL;
+    }
+
+    /* Buffer size expected to be 1M */
+    buffer_size = rt_max_t(rt_uint32_t, buffer_size, 0x100000);
+
+    if (!(conn = rt_malloc(sizeof(*conn) + buffer_size)))
+    {
+        return RT_NULL;
+    }
+
+    conn->msg_size = buffer_size;
+    conn->fid = P9_ROOT_FID;
+#ifdef RT_USING_SMART
+    conn->uname = "rt-smart";
+#else
+    conn->uname = "rt-thread";
+#endif
+    conn->aname = (char *)aname;
+
+    conn->rx_buffer = (void *)conn + sizeof(*conn);
+    conn->tx_buffer = conn->rx_buffer + buffer_size / 2;
+    rt_mutex_init(&conn->lock, conn->aname, RT_IPC_FLAG_PRIO);
+
+    conn->protocol = p9p;
+
+    return conn;
+}
+
+rt_err_t p9_connection_free(struct p9_connection *conn)
+{
+    rt_free(conn);
+
+    return RT_EOK;
+}
+
+int p9_transaction(struct p9_connection *conn,
+        rt_uint32_t tx_size, rt_uint32_t *out_rx_size)
+{
+    rt_err_t err;
+    rt_uint32_t rx_size;
+
+    RT_ASSERT(conn != RT_NULL);
+    RT_ASSERT(conn->protocol != RT_NULL);
+
+    rx_size = conn->msg_size;
+    if ((err = conn->protocol->transport(conn->protocol,
+            conn->tx_buffer, tx_size, conn->rx_buffer, &rx_size)))
+    {
+        return P9_TRANSPORT_ERROR;
+    }
+
+    if (get_rx_value16_of(conn, P9_MSG_TAG) !=
+        get_tx_value16_of(conn, P9_MSG_TAG))
+    {
+        return P9_UNEXPECTED_TAG;
+    }
+
+    if (get_rx_value8_of(conn, P9_MSG_ID) == P9_MSG_ERR)
+    {
+        rt_uint32_t err_len = rt_min_t(rt_uint32_t, sizeof(conn->error) - 1,
+                get_rx_value16_of(conn, P9_MSG_ERR_STR_LEN));
+
+        rt_strncpy(conn->error, (void *)&conn->rx_buffer[P9_MSG_ERR_STR], err_len);
+
+        return P9_R_ERROR;
+    }
+
+    if ((get_tx_value8_of(conn, P9_MSG_ID) + 1) !=
+        get_rx_value8_of(conn, P9_MSG_ID))
+    {
+        return P9_UNEXPECTED_MSG;
+    }
+
+    if (out_rx_size)
+    {
+        *out_rx_size = rx_size;
+    }
+
+    return 0;
+}
+
+int p9_version(struct p9_connection *conn)
+{
+    int rc;
+    rt_uint32_t size;
+
+    RT_ASSERT(conn != RT_NULL);
+
+    rt_mutex_take(&conn->lock, RT_WAITING_FOREVER);
+
+    /*
+     * size[4] Tversion tag[2] msize[4] version[s]
+     * size[4] Rversion tag[2] msize[4] version[s]
+     */
+    size = put_header(conn, P9_HD_TVERSION, NOTAG);
+    size = put_value32(conn, size, conn->msg_size);
+    size = put_string(conn, size, P9_VERSION);
+    put_tx_value32_of(conn, P9_MSG_SIZE, size);
+
+    if (!(rc = p9_transaction(conn, size, RT_NULL)))
+    {
+        char *version;
+        rt_uint32_t version_len;
+        rt_uint32_t msg_size = get_rx_value32_of(conn, P9_MSG_VER_MSIZE);
+
+        conn->msg_size = rt_min_t(rt_uint32_t, conn->msg_size, msg_size);
+
+        version = (char *)&conn->rx_buffer[P9_MSG_VER_STR];
+        version_len = get_rx_value16_of(conn, P9_MSG_VER_STR_LEN);
+
+        LOG_D("Version: %s", version);
+
+        if (!rt_strncmp(P9_VERSION_UNKNOWN, version, version_len))
+        {
+            rc = P9_UNKNOWN_VERSION;
+        }
+    }
+
+    rt_mutex_release(&conn->lock);
+
+    return rc;
+}
+
+int p9_attach(struct p9_connection *conn)
+{
+    int rc;
+    rt_uint32_t size;
+
+    RT_ASSERT(conn != RT_NULL);
+
+    if (19 + rt_strlen(conn->uname) + rt_strlen(conn->aname) > conn->msg_size)
+    {
+        return P9_MSG_TOO_LONG;
+    }
+
+    rt_mutex_take(&conn->lock, RT_WAITING_FOREVER);
+
+    /*
+     * size[4] Tattach tag[2] fid[4] afid[4] uname[s] aname[s]
+     * size[4] Rattach tag[2] qid[13]
+     */
+    size = put_header(conn, P9_HD_TATTACH, TAG);
+    size = put_value32(conn, size, conn->fid);
+    size = put_value32(conn, size, NOFID);
+    size = put_string(conn, size, conn->uname);
+    size = put_string(conn, size, conn->aname);
+    size = put_value32(conn, size, RT_UINT32_MAX);
+    put_tx_value32_of(conn, P9_MSG_SIZE, size);
+
+    rc = p9_transaction(conn, size, RT_NULL);
+
+    rt_mutex_release(&conn->lock);
+
+    return rc;
+}
+
+static int p9_clunk_lockess(struct p9_connection *conn, rt_uint32_t fid)
+{
+    rt_uint32_t size;
+
+    /*
+     * size[4] Tclunk tag[2] fid[4]
+     * size[4] Rclunk tag[2]
+     */
+    size = put_header(conn, P9_HD_TCLUNK, TAG);
+    size = put_value32(conn, size, fid);
+    put_tx_value32_of(conn, P9_MSG_SIZE, size);
+
+    return p9_transaction(conn, size, RT_NULL);
+}
+
+int p9_clunk(struct p9_connection *conn, rt_uint32_t fid)
+{
+    int rc;
+
+    RT_ASSERT(conn != RT_NULL);
+
+    rt_mutex_take(&conn->lock, RT_WAITING_FOREVER);
+
+    rc = p9_clunk_lockess(conn, fid);
+
+    rt_mutex_release(&conn->lock);
+
+    return rc;
+}
+
+rt_err_t dfs_9pfs_add_tag(struct p9_protocol *p9p)
+{
+    rt_ubase_t level;
+
+    if (!p9p || !p9p->tag || !p9p->transport)
+    {
+        return -RT_EINVAL;
+    }
+
+    rt_list_init(&p9p->list);
+
+    level = rt_spin_lock_irqsave(&_protocol_lock);
+    rt_list_insert_before(&_protocol_nodes, &p9p->list);
+    rt_spin_unlock_irqrestore(&_protocol_lock, level);
+
+    LOG_D("Add tag(%s) on %s", p9p->tag, p9p->name);
+
+    return RT_EOK;
+}
+
+rt_err_t dfs_9pfs_del_tag(struct p9_protocol *p9p)
+{
+    rt_ubase_t level;
+
+    if (!p9p)
+    {
+        return -RT_EINVAL;
+    }
+
+    level = rt_spin_lock_irqsave(&_protocol_lock);
+    rt_list_remove(&p9p->list);
+    rt_spin_unlock_irqrestore(&_protocol_lock, level);
+
+    LOG_D("Delete tag(%s) on %s", p9p->tag, p9p->name);
+
+    return RT_EOK;
+}
+
+static int p9_to_fs_err(int rc)
+{
+    const int p9_err[] =
+    {
+        [0] = RT_EOK,
+        [-P9_ERROR] = -EIO,
+        [-P9_UNKNOWN_VERSION] = -ENOSYS,
+        [-P9_R_ERROR] = -EIO,
+        [-P9_MSG_TOO_LONG] = -ENOSPC,
+        [-P9_UNEXPECTED_MSG] = -EINVAL,
+        [-P9_UNEXPECTED_TAG] = -EINVAL,
+        [-P9_TRANSPORT_ERROR] = -EIO,
+        [-P9_NO_TRANSPORT] = -EINVAL,
+        [-P9_NULL_PATH] = -EINVAL,
+        [-P9_PATH_ELEMENT_TOO_LONG] = -EINVAL,
+        [-P9_READ_UNEXPECTED_DATA] = -EINVAL,
+        [-P9_NO_BUFFER] = -ENOSPC,
+        [-P9_MSG_SIZE_TOO_BIG] = -ENOSPC,
+    };
+
+    return p9_err[-rc];
+}
+
+static int p9_alloc_fid(struct p9_connection *conn)
+{
+    int fid = rt_bitmap_next_clear_bit(conn->fid_map, 0, DFS_FD_MAX);
+
+    if (fid < DFS_FD_MAX)
+    {
+        rt_bitmap_set_bit(conn->fid_map, fid);
+
+        return fid + P9_FILE_FID;
+    }
+
+    return NOFID;
+}
+
+static void p9_free_fid(struct p9_connection *conn, int fid)
+{
+    if (fid >= P9_FILE_FID)
+    {
+        rt_bitmap_clear_bit(conn->fid_map, fid - P9_FILE_FID);
+    }
+}
+
+static int p9_free_fid_clunk(struct p9_connection *conn, int fid)
+{
+    int rc;
+
+    if (!(rc = p9_clunk_lockess(conn, fid)))
+    {
+        p9_free_fid(conn, fid);
+    }
+
+    return rc;
+}
+
+static rt_uint8_t fs_to_p9_flags(rt_uint32_t raw_flags)
+{
+    rt_uint8_t flags = 0;
+
+    switch (raw_flags & 3)
+    {
+    case O_RDONLY: flags = OREAD; break;
+    case O_WRONLY: flags = OWRITE; break;
+    case O_RDWR: flags = ORDWR; break;
+    default: RT_ASSERT(0); break;
+    }
+
+    if (raw_flags & O_TRUNC)
+    {
+        flags |= OTRUNC;
+    }
+
+    if (raw_flags & O_APPEND)
+    {
+        flags |= OAPPEND;
+    }
+
+    if (raw_flags & O_EXCL)
+    {
+        flags |= OEXCL;
+    }
+
+    return flags;
+}
+
+static char *p9_basename(const char *path)
+{
+    const char *basename = strrchr(path, '/');
+
+    return (char *)(basename ? basename + 1 : path);
+}
+
+static int p9_walk_path_raw(struct p9_connection *conn, const char *path,
+        rt_bool_t submode)
+{
+    rt_uint32_t size;
+    const char *split;
+    int rc, count = 0, len, fid = conn->fid, new_fid;
+
+    new_fid = p9_alloc_fid(conn);
+
+    if (new_fid < 0)
+    {
+        return P9_NO_BUFFER;
+    }
+
+    if (submode)
+    {
+        --count;
+    }
+
+    /* Pass '/' */
+    ++path;
+
+    /*
+     * size[4] Twalk tag[2] fid[4] newfid[4] nwname[2] nwname*(wname[s])
+     * size[4] Rwalk tag[2] nwqid[2] nwqid*(wqid[13])
+     */
+    size = put_header(conn, P9_HD_TWALK, TAG);
+    size = put_value32(conn, size, fid);
+    size = put_value32(conn, size, new_fid);
+    size = put_value16(conn, size, 0);  /* Fill later */
+
+    do {
+        split = strchrnul(path, '/');
+
+        len = split - path;
+        size = put_bytes(conn, size, (rt_uint8_t *)path, len);
+
+        path += len + 1;
+        ++count;
+    } while (*split && count < P9_MSG_WALK_MAX_ELMT);
+
+    if (count < P9_MSG_WALK_MAX_ELMT)
+    {
+        put_value16(conn, P9_MSG_WALK_TX_ELMT, count);
+
+        if (!(rc = p9_transaction(conn, size, RT_NULL)))
+        {
+            if (count == get_rx_value16_of(conn, P9_MSG_WALK_RX_ELMT))
+            {
+                rc = new_fid;
+            }
+            else
+            {
+                /* Should be `P9_PARTIAL_WALK` if support longer path. */
+                rc = P9_ERROR;
+                p9_free_fid(conn, new_fid);
+            }
+        }
+    }
+    else
+    {
+        rc = P9_PATH_ELEMENT_TOO_LONG;
+        p9_free_fid(conn, new_fid);
+    }
+
+    return rc;
+}
+
+rt_inline int p9_walk_path(struct p9_connection *conn, const char *path)
+{
+    return p9_walk_path_raw(conn, path, RT_FALSE);
+}
+
+rt_inline int p9_walk_subpath(struct p9_connection *conn, const char *path)
+{
+    return p9_walk_path_raw(conn, path, RT_TRUE);
+}
+
+static int dfs_9pfs_open(struct dfs_file *fd)
+{
+    int rc;
+    rt_uint32_t size;
+    rt_bool_t is_root;
+    struct p9_connection *conn;
+    struct p9_file *p9f = rt_calloc(1, sizeof(*p9f));
+
+    if (!p9f)
+    {
+        return -RT_ENOMEM;
+    }
+    conn = fd->vnode->fs->data;
+
+    p9f->fid = conn->fid;
+    p9f->connection = conn;
+    is_root = !rt_strcmp(fd->vnode->path, "/");
+
+    rt_mutex_take(&conn->lock, RT_WAITING_FOREVER);
+
+    if (!is_root)
+    {
+        if ((rc = p9_walk_path(p9f->connection, fd->vnode->path)) > 0)
+        {
+            p9f->fid = rc;
+        }
+        else if (fd->flags & O_CREAT)
+        {
+            rt_uint32_t parent_fid, perm = 0755;
+
+            if (fd->flags & O_DIRECTORY)
+            {
+                perm |= P9_STAT_MODE_DIR;
+            }
+
+            parent_fid = p9_walk_subpath(conn, fd->vnode->path);
+
+            /*
+             * size[4] Tcreate tag[2] fid[4] name[s] perm[4] mode[1] extension[s]
+             * size[4] Rcreate tag[2] qid[13] iounit[4]
+             */
+            size = put_header(conn, P9_HD_TCREATE, TAG);
+            size = put_value32(conn, size, parent_fid);
+            size = put_string(conn, size, p9_basename(fd->vnode->path));
+            size = put_value32(conn, size, perm);
+            size = put_value8(conn, size, fs_to_p9_flags(fd->flags));
+            size = put_string(conn, size, "");
+            put_tx_value32_of(conn, P9_MSG_SIZE, size);
+
+            if (!(rc = p9_transaction(conn, size, RT_NULL)))
+            {
+                p9f->iounit = get_rx_value32_of(conn, P9_MSG_CREATE_IOUNIT);
+
+                if ((rc = p9_walk_path(p9f->connection, fd->vnode->path)) > 0)
+                {
+                    p9f->fid = rc;
+                }
+            }
+
+            p9_free_fid_clunk(conn, parent_fid);
+        }
+    }
+
+    if (is_root || rc >= 0)
+    {
+        /*
+         * size[4] Tstat tag[2] fid[4]
+         * size[4] Rstat tag[2] stat[n]
+         */
+        size = put_header(conn, P9_HD_TSTAT, TAG);
+        size = put_value32(conn, size, p9f->fid);
+        put_tx_value32_of(conn, P9_MSG_SIZE, size);
+
+        if (!(rc = p9_transaction(conn, size, RT_NULL)))
+        {
+            fd->vnode->size = get_rx_value64_of(conn, P9_MSG_STAT_LEN);
+
+            /*
+             * size[4] Topen tag[2] fid[4] mode[1]
+             * size[4] Ropen tag[2] qid[13] iounit[4]
+             */
+            size = put_header(conn, P9_HD_TOPEN, TAG);
+            size = put_value32(conn, size, p9f->fid);
+            size = put_value8(conn, size, fs_to_p9_flags(fd->flags));
+            put_tx_value32_of(conn, P9_MSG_SIZE, size);
+
+            if (!(rc = p9_transaction(conn, size, RT_NULL)))
+            {
+                p9f->iounit = get_rx_value32_of(conn, P9_MSG_OPEN_IOUNIT);
+            }
+        }
+    }
+
+    rt_mutex_release(&conn->lock);
+
+    if (!is_root && rc)
+    {
+        if (p9f->fid != conn->fid)
+        {
+            p9_free_fid(conn, p9f->fid);
+        }
+
+        rt_free(p9f);
+
+        return p9_to_fs_err(rc);
+    }
+
+    fd->vnode->data = p9f;
+
+    return 0;
+}
+
+static int dfs_9pfs_close(struct dfs_file *fd)
+{
+    int rc = 0;
+    struct p9_file *p9f = fd->vnode->data;
+    struct p9_connection *conn = p9f->connection;
+
+    if (p9f->fid != conn->fid)
+    {
+        rt_mutex_take(&conn->lock, RT_WAITING_FOREVER);
+        rc = p9_free_fid_clunk(conn, p9f->fid);
+        rt_mutex_release(&conn->lock);
+    }
+
+    if (!rc)
+    {
+        rt_free(p9f);
+    }
+
+    return p9_to_fs_err(rc);
+}
+
+static ssize_t dfs_9pfs_read(struct dfs_file *fd, void *buf, size_t count)
+{
+    int rc;
+    size_t tcount = 0;
+    rt_uint32_t max_count, set, size, got;
+    struct p9_file *p9f = fd->vnode->data;
+    struct p9_connection *conn = p9f->connection;
+
+    max_count = conn->msg_size - P9_MSG_READ_DATA;
+
+    rt_mutex_take(&conn->lock, RT_WAITING_FOREVER);
+
+    while ((ssize_t)count > 0)
+    {
+        set = rt_min_t(rt_uint32_t, max_count, count);
+
+        /*
+         * size[4] Tread tag[2] fid[4] offset[8] count[4]
+         * size[4] Rread tag[2] count[4] data[count]
+         */
+        size = put_header(conn, P9_HD_TREAD, TAG);
+        size = put_value32(conn, size, p9f->fid);
+        size = put_value64(conn, size, fd->pos);
+        size = put_value32(conn, size, set);
+        put_tx_value32_of(conn, P9_MSG_SIZE, size);
+
+        if (!(rc = p9_transaction(conn, size, RT_NULL)))
+        {
+            got = get_rx_value32_of(conn, P9_MSG_READ_COUNT);
+
+            if (got == 0)
+            {
+                break;
+            }
+            else if (got <= set)
+            {
+                fd->pos += got;
+
+                rt_memcpy(buf, conn->rx_buffer + P9_MSG_READ_DATA, got);
+            }
+            else
+            {
+                tcount = p9_to_fs_err(P9_READ_UNEXPECTED_DATA);
+                break;
+            }
+
+            count -= got;
+            tcount += got;
+        }
+    }
+
+    rt_mutex_release(&conn->lock);
+
+    return tcount;
+}
+
+static ssize_t dfs_9pfs_write(struct dfs_file *fd, const void *buf, size_t count)
+{
+    int rc;
+    size_t tcount = 0;
+    rt_uint32_t max_count, set, size, got;
+    struct p9_file *p9f = fd->vnode->data;
+    struct p9_connection *conn = p9f->connection;
+
+    max_count = conn->msg_size - P9_MSG_WRITE_DATA;
+
+    rt_mutex_take(&conn->lock, RT_WAITING_FOREVER);
+
+    while ((ssize_t)count > 0)
+    {
+        set = rt_min_t(rt_uint32_t, max_count, count);
+
+        /*
+         * size[4] Twrite tag[2] fid[4] offset[8] count[4] data[count]
+         * size[4] Rwrite tag[2] count[4]
+         */
+        size = put_header(conn, P9_HD_TWRITE, TAG);
+        size = put_value32(conn, size, p9f->fid);
+        size = put_value64(conn, size, fd->pos);
+        size = put_value32(conn, size, set);
+        rt_memcpy(conn->tx_buffer + P9_MSG_WRITE_DATA, buf, set);
+        size += set;
+        put_tx_value32_of(conn, P9_MSG_SIZE, size);
+
+        if (!(rc = p9_transaction(conn, size, RT_NULL)))
+        {
+            got = get_rx_value32_of(conn, P9_MSG_WRITE_COUNT);
+
+            if (got <= set)
+            {
+                fd->pos += set;
+            }
+            else
+            {
+                tcount = p9_to_fs_err(P9_READ_UNEXPECTED_DATA);
+                break;
+            }
+
+            count -= got;
+            tcount += got;
+        }
+    }
+
+    rt_mutex_release(&conn->lock);
+
+    return tcount;
+}
+
+static int dfs_9pfs_flush(struct dfs_file *fd)
+{
+    int rc;
+    rt_uint32_t size;
+    struct p9_file *p9f = fd->vnode->data;
+    struct p9_connection *conn = p9f->connection;
+
+    /*
+     * size[4] Tfsync tag[2] fid[4] datasync[4]
+     * size[4] Rfsync tag[2]
+     */
+    size = put_header(conn, P9_HD_TFSYNC, TAG);
+    size = put_value32(conn, size, p9f->fid);
+    size = put_value32(conn, size, 1);
+    put_tx_value32_of(conn, P9_MSG_SIZE, size);
+
+    rc = p9_transaction(conn, size, RT_NULL);
+
+    return p9_to_fs_err(rc);
+}
+
+static off_t dfs_9pfs_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_9pfs_getdents(struct dfs_file *fd, struct dirent *dirp, uint32_t count)
+{
+    int rc;
+    rt_uint64_t off;
+    rt_uint32_t ret, end, stat_size, size;
+    struct p9_file *p9f = fd->vnode->data;
+    struct p9_connection *conn = p9f->connection;
+
+    count = (count / sizeof(struct dirent));
+
+    if (!count)
+    {
+        return -EINVAL;
+    }
+    end = fd->pos + count;
+    count = 0;
+
+    rt_mutex_take(&conn->lock, RT_WAITING_FOREVER);
+
+    /*
+     * size[4] Tread tag[2] fid[4] offset[8] count[4]
+     * size[4] Rread tag[2] count[4] stat[n]
+     */
+    size = put_header(conn, P9_HD_TREAD, TAG);
+    size = put_value32(conn, size, p9f->fid);
+    size = put_value64(conn, size, 0);
+    size = put_value32(conn, size, fd->vnode->size);
+    put_tx_value32_of(conn, P9_MSG_SIZE, size);
+
+    if (!(rc = p9_transaction(conn, size, RT_NULL)))
+    {
+        ret = get_rx_value32_of(conn, P9_MSG_READ_COUNT);
+        off = P9_MSG_READ_DATA - P9_MSG_STAT_SIZE;
+
+        for (int i = 0; (rt_int32_t)ret > 0; off += stat_size, ret -= stat_size, ++i)
+        {
+            /*
+             * size[2] type[2] dev[4] qid[1+4+8] mode[4] atime[4] mtime[4] length[8]
+             */
+            stat_size = get_rx_value16_of(conn, off + P9_MSG_STAT_SIZE) + sizeof(rt_uint16_t);
+
+            if (i < fd->pos)
+            {
+                continue;
+            }
+
+            if (get_rx_value16_of(conn, off + P9_MSG_STAT_TYPE) & P9_STAT_MODE_DIR)
+            {
+                dirp->d_type = DT_DIR;
+            }
+            else
+            {
+                dirp->d_type = DT_REG;
+            }
+
+            /*
+             * name_size[2] name[name_size]
+             * uid_size[2] uid[uid_size]
+             * gid_size[2] gid[gid_size]
+             * muid_size[2] muid[muid_size]
+             * extension_size[2] extension[extension_size]
+             * n_uid[4]
+             * n_gid[4]
+             * n_muid[4]
+             */
+            dirp->d_namlen = get_rx_value16_of(conn, off + P9_MSG_STAT_NAME_LEN);
+            dirp->d_reclen = (rt_uint16_t)sizeof(struct dirent);
+            rt_strncpy(dirp->d_name,
+                    (void *)conn->rx_buffer + off + P9_MSG_STAT_NAME,
+                    dirp->d_namlen);
+            dirp->d_name[dirp->d_namlen] = '\0';
+
+            if (!rt_strcmp(dirp->d_name, ".") || !rt_strcmp(dirp->d_name, ".."))
+            {
+                ++fd->pos;
+                continue;
+            }
+
+            ++fd->pos;
+            ++dirp;
+            ++count;
+
+            if (fd->pos >= end)
+            {
+                break;
+            }
+        }
+    }
+
+    rt_mutex_release(&conn->lock);
+
+    count *= sizeof(struct dirent);
+
+    return count;
+}
+
+static const struct dfs_file_ops _9pfs_fops =
+{
+    .open       = dfs_9pfs_open,
+    .close      = dfs_9pfs_close,
+    .read       = dfs_9pfs_read,
+    .write      = dfs_9pfs_write,
+    .flush      = dfs_9pfs_flush,
+    .lseek      = dfs_9pfs_lseek,
+    .getdents   = dfs_9pfs_getdents,
+};
+
+static int dfs_9pfs_mount(struct dfs_filesystem *fs,
+        unsigned long rwflag, const void *data)
+{
+    rt_ubase_t level;
+    struct p9_protocol *p9p, *p9p_tmp;
+    struct p9_connection *conn = RT_NULL;
+
+    if (!data)
+    {
+        return (int)-RT_EINVAL;
+    }
+
+    level = rt_spin_lock_irqsave(&_protocol_lock);
+
+    rt_list_for_each_entry(p9p_tmp, &_protocol_nodes, list)
+    {
+        if (!rt_strcmp(p9p_tmp->tag, data))
+        {
+            p9p = p9p_tmp;
+            break;
+        }
+    }
+
+    rt_spin_unlock_irqrestore(&_protocol_lock, level);
+
+    if (!p9p)
+    {
+        return (int)-RT_EINVAL;
+    }
+
+    if (!(conn = p9_connection_alloc(p9p, p9p->tag, 0)))
+    {
+        return (int)-RT_ENOMEM;
+    }
+
+    if (p9_version(conn) || p9_attach(conn))
+    {
+        p9_connection_free(conn);
+        return (int)-RT_EINVAL;
+    }
+
+    fs->data = conn;
+
+    return (int)RT_EOK;
+}
+
+static int dfs_9pfs_unmount(struct dfs_filesystem *fs)
+{
+    struct p9_connection *conn = fs->data;
+
+    p9_clunk(conn, conn->fid);
+
+    return p9_connection_free(conn);
+}
+
+static int dfs_9pfs_statfs(struct dfs_filesystem *fs, struct statfs *buf)
+{
+    int rc;
+    rt_uint32_t size;
+    struct p9_connection *conn = fs->data;
+
+    rt_mutex_take(&conn->lock, RT_WAITING_FOREVER);
+
+    /*
+     * size[4] Tstatfs tag[2] fid[4]
+     * size[4] Rstatfs tag[2]
+     *      f_type[4]
+     *      f_bsize[4]
+     *      f_blocks[8]
+     *      f_bfree[8]
+     *      f_bavail[8]
+     *      f_files[8]
+     *      f_ffree[8]
+     *      fsid_val[8]
+     *      f_namelen[4]
+     */
+    size = put_header(conn, P9_HD_TSTATFS, TAG);
+    size = put_value32(conn, size, conn->fid);
+    put_tx_value32_of(conn, P9_MSG_SIZE, size);
+
+    if (!(rc = p9_transaction(conn, size, RT_NULL)))
+    {
+        buf->f_bsize = get_rx_value32_of(conn, P9_MSG_F_BSIZE);
+        buf->f_blocks = get_rx_value64_of(conn, P9_MSG_F_BLOCKS);
+        buf->f_bfree = get_rx_value64_of(conn, P9_MSG_F_BFREE);
+        buf->f_bavail = get_rx_value64_of(conn, P9_MSG_F_BAVAIL);
+    }
+
+    rt_mutex_release(&conn->lock);
+
+    return p9_to_fs_err(rc);
+}
+
+static int dfs_9pfs_unlink(struct dfs_filesystem *fs, const char *pathname)
+{
+    rt_uint32_t size;
+    int rc = 0, fid;
+    struct p9_connection *conn = fs->data;
+
+    rt_mutex_take(&conn->lock, RT_WAITING_FOREVER);
+
+    if ((rc = p9_walk_path(conn, pathname)) > 0)
+    {
+        fid = rc;
+
+        /*
+         * size[4] Tremove tag[2] fid[4]
+         * size[4] Rremove tag[2]
+         */
+        size = put_header(conn, P9_HD_TREMOVE, TAG);
+        size = put_value32(conn, size, fid);
+        put_tx_value32_of(conn, P9_MSG_SIZE, size);
+
+        rc = p9_transaction(conn, size, RT_NULL);
+        p9_free_fid_clunk(conn, fid);
+    }
+
+    rt_mutex_release(&conn->lock);
+
+    return p9_to_fs_err(rc);
+}
+
+static int dfs_9pfs_stat(struct dfs_filesystem *fs,
+        const char *filename, struct stat *st)
+{
+    int rc = 0, fid;
+    rt_uint32_t size, mode;
+    struct p9_connection *conn = fs->data;
+
+    rt_mutex_take(&conn->lock, RT_WAITING_FOREVER);
+
+    if ((rc = p9_walk_path(conn, filename)) > 0)
+    {
+        fid = rc;
+
+        /*
+         * size[4] Tstat tag[2] fid[4]
+         * size[4] Rstat tag[2] stat[n]
+         */
+        size = put_header(conn, P9_HD_TSTAT, TAG);
+        size = put_value32(conn, size, fid);
+        put_tx_value32_of(conn, P9_MSG_SIZE, size);
+
+        if (!(rc = p9_transaction(conn, size, RT_NULL)))
+        {
+            st->st_dev = 0;
+
+            mode = get_rx_value32_of(conn, P9_MSG_STAT_MODE);
+            st->st_mode = 0;
+            if (mode & P9_STAT_MODE_DIR)
+            {
+                st->st_mode |= S_IFDIR;
+            }
+            else
+            {
+                st->st_mode |= S_IFREG;
+            }
+            if (mode & P9_STAT_MODE_SYMLINK)
+            {
+                st->st_mode |= S_IFLNK;
+            }
+            if (mode & P9_STAT_MODE_SOCKET)
+            {
+                st->st_mode |= S_IFSOCK;
+            }
+            if (mode & P9_STAT_MODE_NAMED_PIPE)
+            {
+                st->st_mode |= S_IFIFO;
+            }
+            if (mode & P9_STAT_MODE_SETUID)
+            {
+                st->st_mode |= S_ISUID;
+            }
+            if (mode & P9_STAT_MODE_SETGID)
+            {
+                st->st_mode |= S_ISGID;
+            }
+            if (mode & P9_STAT_MODE_SETVTX)
+            {
+                st->st_mode |= S_ISVTX;
+            }
+
+            st->st_atime = get_rx_value32_of(conn, P9_MSG_STAT_ATIME);
+            st->st_mtime = get_rx_value32_of(conn, P9_MSG_STAT_MTIME);
+            st->st_size = get_rx_value64_of(conn, P9_MSG_STAT_LEN);
+
+            /*
+             * name_size[2] name[name_size]
+             * uid_size[2] uid[uid_size]
+             * gid_size[2] gid[gid_size]
+             * muid_size[2] muid[muid_size]
+             * extension_size[2] extension[extension_size]
+             * n_uid[4]
+             * n_gid[4]
+             * n_muid[4]
+             */
+            size = P9_MSG_STAT_NAME_LEN;
+            size += sizeof(rt_uint16_t) + get_rx_value16_of(conn, size);
+            size += sizeof(rt_uint16_t) + get_rx_value16_of(conn, size);
+            size += sizeof(rt_uint16_t) + get_rx_value16_of(conn, size);
+            size += sizeof(rt_uint16_t) + get_rx_value16_of(conn, size);
+            st->st_uid = get_rx_value32_of(conn, size) + sizeof(rt_uint32_t);
+            st->st_gid = get_rx_value32_of(conn, size) + sizeof(rt_uint32_t);
+        }
+
+        p9_free_fid_clunk(conn, fid);
+    }
+
+    rt_mutex_release(&conn->lock);
+
+    return p9_to_fs_err(rc);
+}
+
+static int dfs_9pfs_rename(struct dfs_filesystem *fs,
+        const char *oldpath, const char *newpath)
+{
+    int rc = 0, fid;
+    rt_uint32_t size;
+    struct p9_connection *conn = fs->data;
+
+    rt_mutex_take(&conn->lock, RT_WAITING_FOREVER);
+
+    if ((fid = p9_walk_path(conn, oldpath)) > 0)
+    {
+        /*
+         * size[4] Trename tag[2] fid[4] new_fid[4] name[s]
+         * size[4] Rrename tag[2]
+         */
+        size = put_header(conn, P9_HD_TRENAME, TAG);
+        size = put_value32(conn, size, fid);
+        size = put_value32(conn, size, NOFID);
+        size = put_string(conn, size, p9_basename(newpath));
+        put_tx_value32_of(conn, P9_MSG_SIZE, size);
+
+        rc = p9_transaction(conn, size, RT_NULL);
+        p9_free_fid_clunk(conn, fid);
+    }
+
+    rt_mutex_release(&conn->lock);
+
+    return p9_to_fs_err(rc);
+}
+
+static const struct dfs_filesystem_ops _9pfs =
+{
+    .name       = "9p",
+    .flags      = DFS_FS_FLAG_DEFAULT,
+    .fops       = &_9pfs_fops,
+
+    .mount      = dfs_9pfs_mount,
+    .unmount    = dfs_9pfs_unmount,
+
+    .statfs     = dfs_9pfs_statfs,
+
+    .unlink     = dfs_9pfs_unlink,
+    .stat       = dfs_9pfs_stat,
+    .rename     = dfs_9pfs_rename,
+};
+
+int dfs_9pfs_init(void)
+{
+    /* register 9p file system */
+    return dfs_register(&_9pfs);
+}
+INIT_COMPONENT_EXPORT(dfs_9pfs_init);

+ 247 - 0
components/dfs/dfs_v1/filesystems/9pfs/dfs_9pfs.h

@@ -0,0 +1,247 @@
+/*
+ * 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_9PFS_H__
+#define __DFS_9PFS_H__
+
+#include <rtdef.h>
+
+#include <dfs.h>
+#include <dfs_fs.h>
+#include <dfs_file.h>
+#include <bitmap.h>
+
+#define P9_ERROR                    -1
+#define P9_UNKNOWN_VERSION          -2
+#define P9_R_ERROR                  -3
+#define P9_MSG_TOO_LONG             -4
+#define P9_UNEXPECTED_MSG           -5
+#define P9_UNEXPECTED_TAG           -6
+#define P9_TRANSPORT_ERROR          -7
+#define P9_NO_TRANSPORT             -8
+#define P9_NULL_PATH                -9
+#define P9_PATH_ELEMENT_TOO_LONG    -10
+#define P9_READ_UNEXPECTED_DATA     -11
+#define P9_NO_BUFFER                -12
+#define P9_MSG_SIZE_TOO_BIG         -13
+
+#define P9_PARTIAL_WALK             1
+
+#define P9_VERSION                  "9P2000.u"
+#define P9_VERSION_UNKNOWN          "unknown"
+
+#define P9_MSG_F_TYPE               7
+#define P9_MSG_F_BSIZE              11
+#define P9_MSG_F_BLOCKS             15
+#define P9_MSG_F_BFREE              23
+#define P9_MSG_F_BAVAIL             31
+#define P9_MSG_F_FILES              39
+#define P9_MSG_F_FFREE              47
+#define P9_MSG_FSID_VAL             55
+#define P9_MSG_F_NAMELEN            63
+#define P9_MSG_SIZE                 0
+#define P9_MSG_ID                   4
+#define P9_MSG_ERR                  0x6b
+#define P9_MSG_ERR_STR              9
+#define P9_MSG_ERR_STR_LEN          7
+#define P9_MSG_TAG                  5
+#define P9_MSG_VER_MSIZE            7
+#define P9_MSG_VER_STR_LEN          11
+#define P9_MSG_VER_STR              13
+#define P9_MSG_WALK_TX_ELMT         15
+#define P9_MSG_WALK_RX_ELMT         7
+#define P9_MSG_WALK_MAX_ELMT        16
+#define P9_MSG_QID_SIZE             13
+#define P9_MSG_WALK_RX_HDR_SIZE     9
+#define P9_MSG_OPEN_IOUNIT          20
+#define P9_MSG_OPEN_MODE_MASK       0x5f
+#define P9_MSG_CREATE_IOUNIT        20
+#define P9_MSG_READ_COUNT           7
+#define P9_MSG_READ_DATA            11
+#define P9_MSG_WRITE_COUNT          7
+#define P9_MSG_WRITE_DATA           23
+#define P9_MSG_RENAME_DATA          15
+#define P9_MSG_STAT_SIZE            9
+#define P9_MSG_STAT_TYPE            11
+#define P9_MSG_STAT_DEV             13
+#define P9_MSG_STAT_QID_TYPE        17
+#define P9_MSG_STAT_QID_VERSION     18
+#define P9_MSG_STAT_QID_PATH        22
+#define P9_MSG_STAT_MODE            30
+#define P9_MSG_STAT_ATIME           34
+#define P9_MSG_STAT_MTIME           38
+#define P9_MSG_STAT_LEN             42
+#define P9_MSG_STAT_NAME_LEN        50
+#define P9_MSG_STAT_NAME            52
+
+#define P9_HD_TSTATFS               8
+#define P9_HD_RSTATFS               (P9_HD_TSTATFS + 1)
+#define P9_HD_TRENAME               20
+#define P9_HD_RRENAME               (P9_HD_TRENAME + 1)
+#define P9_HD_TFSYNC                50
+#define P9_HD_RFSYNC                (P9_HD_TFSYNC + 1)
+#define P9_HD_TVERSION              100
+#define P9_HD_RVERSION              (P9_HD_TVERSION + 1)
+#define P9_HD_TAUTH                 102
+#define P9_HD_RAUTH                 (P9_HD_TAUTH + 1)
+#define P9_HD_TATTACH               104
+#define P9_HD_RATTACH               (P9_HD_TATTACH + 1)
+#define P9_HD_TERROR                106
+#define P9_HD_RERROR                (P9_HD_TERROR + 1)
+#define P9_HD_TFLUSH                108
+#define P9_HD_RFLUSH                (P9_HD_TFLUSH + 1)
+#define P9_HD_TWALK                 110
+#define P9_HD_RWALK                 (P9_HD_TWALK + 1)
+#define P9_HD_TOPEN                 112
+#define P9_HD_ROPEN                 (P9_HD_TOPEN + 1)
+#define P9_HD_TCREATE               114
+#define P9_HD_RCREATE               (P9_HD_TCREATE + 1)
+#define P9_HD_TREAD                 116
+#define P9_HD_RREAD                 (P9_HD_TREAD + 1)
+#define P9_HD_TWRITE                118
+#define P9_HD_RWRITE                (P9_HD_TWRITE + 1)
+#define P9_HD_TCLUNK                120
+#define P9_HD_RCLUNK                (P9_HD_TCLUNK + 1)
+#define P9_HD_TREMOVE               122
+#define P9_HD_RREMOVE               (P9_HD_TREMOVE + 1)
+#define P9_HD_TSTAT                 124
+#define P9_HD_RSTAT                 (P9_HD_TSTAT + 1)
+
+#define P9_STAT_MODE_DIR            0x80000000
+#define P9_STAT_MODE_APPEND         0x40000000
+#define P9_STAT_MODE_EXCL           0x20000000
+#define P9_STAT_MODE_MOUNT          0x10000000
+#define P9_STAT_MODE_AUTH           0x08000000
+#define P9_STAT_MODE_TMP            0x04000000
+#define P9_STAT_MODE_SYMLINK        0x02000000
+#define P9_STAT_MODE_LINK           0x01000000
+#define P9_STAT_MODE_DEVICE         0x00800000
+#define P9_STAT_MODE_NAMED_PIPE     0x00200000
+#define P9_STAT_MODE_SOCKET         0x00100000
+#define P9_STAT_MODE_SETUID         0x00080000
+#define P9_STAT_MODE_SETGID         0x00040000
+#define P9_STAT_MODE_SETVTX         0x00010000
+
+struct p9_protocol
+{
+    rt_list_t list;
+    char *name;
+
+    const char *tag;
+    rt_err_t (*transport)(struct p9_protocol *p9p,
+            rt_uint8_t *tx_data, rt_uint32_t tx_size,
+            rt_uint8_t *rx_data, rt_uint32_t *ref_rx_size);
+};
+
+struct p9_connection
+{
+    char *uname;            /* User name */
+    char *aname;            /* Tree/mount name/path */
+    rt_uint32_t fid;        /* Represents mount point */
+    rt_uint32_t msg_size;
+
+    char error[200];
+    rt_uint8_t *rx_buffer;
+    rt_uint8_t *tx_buffer;
+    struct rt_mutex lock;
+
+    struct p9_protocol *protocol;
+    RT_BITMAP_DECLARE(fid_map, DFS_FD_MAX);
+};
+
+struct p9_file
+{
+#define P9_ROOT_FID     1
+#define P9_FILE_FID     2
+    rt_uint32_t fid;        /* Identifies the file to P9 server */
+    rt_uint32_t iounit;     /* Maximum read size in bytes */
+
+    struct p9_connection *connection;
+};
+
+/**
+ * @brief Allocate a new 9P connection.
+ *
+ * @param p9p Pointer to the 9P protocol structure.
+ * @param aname Mount name/path.
+ * @param buffer_size Size of the buffer to allocate.
+ * @return Pointer to the allocated p9_connection structure, or RT_NULL on failure.
+ */
+struct p9_connection *p9_connection_alloc(struct p9_protocol *p9p,
+        const char *aname, rt_uint32_t buffer_size);
+
+/**
+ * @brief Free a 9P connection and release its resources.
+ *
+ * @param conn Pointer to the p9_connection to free.
+ * @return RT_EOK on success, error code otherwise.
+ */
+rt_err_t p9_connection_free(struct p9_connection *conn);
+
+/**
+ * @brief Perform a 9P transaction.
+ *
+ * @param conn Pointer to the p9_connection.
+ * @param tx_size Size of the transmit buffer.
+ * @param out_rx_size Pointer to store the received buffer size.
+ * @return 0 on success, negative error code otherwise.
+ */
+int p9_transaction(struct p9_connection *conn,
+        rt_uint32_t tx_size, rt_uint32_t *out_rx_size);
+
+/**
+ * @brief Negotiate 9P protocol version with the server.
+ *
+ * @param conn Pointer to the p9_connection.
+ * @return 0 on success, negative error code otherwise.
+ */
+int p9_version(struct p9_connection *conn);
+
+/**
+ * @brief Attach to the 9P server.
+ *
+ * @param conn Pointer to the p9_connection.
+ * @return 0 on success, negative error code otherwise.
+ */
+int p9_attach(struct p9_connection *conn);
+
+/**
+ * @brief Clunk (close) a file identifier (fid) on the 9P server.
+ *
+ * @param conn Pointer to the p9_connection.
+ * @param fid File identifier to clunk.
+ * @return 0 on success, negative error code otherwise.
+ */
+int p9_clunk(struct p9_connection *conn, rt_uint32_t fid);
+
+/**
+ * @brief Register a 9P protocol tag.
+ *
+ * @param p9p Pointer to the p9_protocol structure.
+ * @return RT_EOK on success, error code otherwise.
+ */
+rt_err_t dfs_9pfs_add_tag(struct p9_protocol *p9p);
+
+/**
+ * @brief Unregister a 9P protocol tag.
+ *
+ * @param p9p Pointer to the p9_protocol structure.
+ * @return RT_EOK on success, error code otherwise.
+ */
+rt_err_t dfs_9pfs_del_tag(struct p9_protocol *p9p);
+
+/**
+ * @brief Initialize the 9P filesystem.
+ *
+ * @return 0 on success, negative error code otherwise.
+ */
+int dfs_9pfs_init(void);
+
+#endif /* __DFS_9PFS_H__ */

+ 1 - 1
components/finsh/msh_file.c

@@ -585,7 +585,7 @@ static int cmd_mount(int argc, char **argv)
 
 
         /* mount a filesystem to the specified directory */
         /* mount a filesystem to the specified directory */
         rt_kprintf("mount device %s(%s) onto %s ... ", device, fstype, path);
         rt_kprintf("mount device %s(%s) onto %s ... ", device, fstype, path);
-        if (rt_strcmp(fstype, "nfs") == 0)
+        if (rt_strcmp(fstype, "nfs") == 0 || rt_strcmp(fstype, "9p") == 0)
         {
         {
             data = argv[1];
             data = argv[1];
             device = 0;
             device = 0;