root/drivers/net/wireless/intel/iwlwifi/mld/debugfs.h
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/*
 * Copyright (C) 2024-2025 Intel Corporation
 */
#include "iface.h"
#include "sta.h"

#define MLD_DEBUGFS_OPEN_WRAPPER(name, buflen, argtype)                 \
struct dbgfs_##name##_data {                                            \
        argtype *arg;                                                   \
        bool read_done;                                                 \
        ssize_t rlen;                                                   \
        char buf[buflen];                                               \
};                                                                      \
static int _iwl_dbgfs_##name##_open(struct inode *inode,                \
                                    struct file *file)                  \
{                                                                       \
        struct dbgfs_##name##_data *data;                               \
                                                                        \
        if ((file->f_flags & O_ACCMODE) == O_RDWR)                      \
                return -EOPNOTSUPP;                                     \
                                                                        \
        data = kzalloc(sizeof(*data), GFP_KERNEL);                      \
        if (!data)                                                      \
                return -ENOMEM;                                         \
                                                                        \
        data->read_done = false;                                        \
        data->arg = inode->i_private;                                   \
        file->private_data = data;                                      \
                                                                        \
        return 0;                                                       \
}

#define MLD_DEBUGFS_READ_WRAPPER(name)                                  \
static ssize_t _iwl_dbgfs_##name##_read(struct file *file,              \
                                        char __user *user_buf,          \
                                        size_t count, loff_t *ppos)     \
{                                                                       \
        struct dbgfs_##name##_data *data = file->private_data;          \
                                                                        \
        if (!data->read_done) {                                         \
                data->read_done = true;                                 \
                data->rlen = iwl_dbgfs_##name##_read(data->arg,         \
                                                     sizeof(data->buf),\
                                                     data->buf);        \
        }                                                               \
                                                                        \
        if (data->rlen < 0)                                             \
                return data->rlen;                                      \
        return simple_read_from_buffer(user_buf, count, ppos,           \
                                       data->buf, data->rlen);          \
}

static int _iwl_dbgfs_release(struct inode *inode, struct file *file)
{
        kfree(file->private_data);
        return 0;
}

#define _MLD_DEBUGFS_READ_FILE_OPS(name, buflen, argtype)               \
MLD_DEBUGFS_OPEN_WRAPPER(name, buflen, argtype)                         \
MLD_DEBUGFS_READ_WRAPPER(name)                                          \
static const struct file_operations iwl_dbgfs_##name##_ops = {          \
        .read = _iwl_dbgfs_##name##_read,                               \
        .open = _iwl_dbgfs_##name##_open,                               \
        .llseek = generic_file_llseek,                                  \
        .release = _iwl_dbgfs_release,                                  \
}

#define WIPHY_DEBUGFS_WRITE_HANDLER_WRAPPER(name)                       \
static ssize_t iwl_dbgfs_##name##_write_handler(struct wiphy *wiphy,    \
                                       struct file *file, char *buf,    \
                                       size_t count, void *data)        \
{                                                                       \
        struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);         \
        struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);                 \
        return iwl_dbgfs_##name##_write(mld, buf, count, data);         \
}

static inline struct iwl_mld *
iwl_mld_from_link_sta(struct ieee80211_link_sta *link_sta)
{
        struct ieee80211_vif *vif =
                iwl_mld_sta_from_mac80211(link_sta->sta)->vif;
        return iwl_mld_vif_from_mac80211(vif)->mld;
}

static inline struct iwl_mld *
iwl_mld_from_bss_conf(struct ieee80211_bss_conf *link)
{
        return iwl_mld_vif_from_mac80211(link->vif)->mld;
}

static inline struct iwl_mld *iwl_mld_from_vif(struct ieee80211_vif *vif)
{
        return iwl_mld_vif_from_mac80211(vif)->mld;
}

#define WIPHY_DEBUGFS_WRITE_WRAPPER(name, bufsz, objtype)               \
WIPHY_DEBUGFS_WRITE_HANDLER_WRAPPER(name)                               \
static ssize_t __iwl_dbgfs_##name##_write(struct file *file,            \
                                          const char __user *user_buf,  \
                                          size_t count, loff_t *ppos)   \
{                                                                       \
        struct ieee80211_##objtype *arg = file->private_data;           \
        struct iwl_mld *mld = iwl_mld_from_##objtype(arg);              \
        char buf[bufsz] = {};                                           \
                                                                        \
        return wiphy_locked_debugfs_write(mld->wiphy, file,             \
                                buf, sizeof(buf),                       \
                                user_buf, count,                        \
                                iwl_dbgfs_##name##_write_handler,       \
                                arg);                                   \
}

#define WIPHY_DEBUGFS_WRITE_FILE_OPS(name, bufsz, objtype)              \
        WIPHY_DEBUGFS_WRITE_WRAPPER(name, bufsz, objtype)               \
        static const struct file_operations iwl_dbgfs_##name##_ops = {  \
                .write = __iwl_dbgfs_##name##_write,                    \
                .open = simple_open,                                    \
                .llseek = generic_file_llseek,                          \
        }

#define WIPHY_DEBUGFS_READ_HANDLER_WRAPPER_MLD(name)                    \
static ssize_t iwl_dbgfs_##name##_read_handler(struct wiphy *wiphy,     \
                                       struct file *file, char *buf,    \
                                       size_t count, void *data)        \
{                                                                       \
        struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);         \
        struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);                 \
        return iwl_dbgfs_##name##_read(mld, buf, count);                \
}

#define WIPHY_DEBUGFS_WRITE_HANDLER_WRAPPER_MLD(name)                   \
static ssize_t iwl_dbgfs_##name##_write_handler(struct wiphy *wiphy,    \
                                       struct file *file, char *buf,    \
                                       size_t count, void *data)        \
{                                                                       \
        struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);         \
        struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);                 \
        return iwl_dbgfs_##name##_write(mld, buf, count);               \
}

#define WIPHY_DEBUGFS_WRITE_WRAPPER_MLD(name)                           \
WIPHY_DEBUGFS_WRITE_HANDLER_WRAPPER_MLD(name)                           \
static ssize_t __iwl_dbgfs_##name##_write(struct file *file,            \
                                          const char __user *user_buf,  \
                                          size_t count, loff_t *ppos)   \
{                                                                       \
        struct dbgfs_##name##_data *data = file->private_data;          \
        struct iwl_mld *mld = data->arg;                                \
                                                                        \
        return wiphy_locked_debugfs_write(mld->wiphy, file,             \
                                data->buf, sizeof(data->buf),           \
                                user_buf, count,                        \
                                iwl_dbgfs_##name##_write_handler,       \
                                NULL);                                  \
}

#define WIPHY_DEBUGFS_READ_WRAPPER_MLD(name)                            \
WIPHY_DEBUGFS_READ_HANDLER_WRAPPER_MLD(name)                            \
static ssize_t __iwl_dbgfs_##name##_read(struct file *file,             \
                                        char __user *user_buf,          \
                                        size_t count, loff_t *ppos)     \
{                                                                       \
        struct dbgfs_##name##_data *data = file->private_data;          \
        struct iwl_mld *mld = data->arg;                                \
                                                                        \
        if (!data->read_done) {                                         \
                data->read_done = true;                                 \
                data->rlen = wiphy_locked_debugfs_read(mld->wiphy,      \
                                file, data->buf, sizeof(data->buf),     \
                                user_buf, count, ppos,                  \
                                iwl_dbgfs_##name##_read_handler, NULL); \
                return data->rlen;                                      \
        }                                                               \
                                                                        \
        if (data->rlen < 0)                                             \
                return data->rlen;                                      \
        return simple_read_from_buffer(user_buf, count, ppos,           \
                                       data->buf, data->rlen);          \
}

#define WIPHY_DEBUGFS_READ_FILE_OPS_MLD(name, bufsz)                    \
        MLD_DEBUGFS_OPEN_WRAPPER(name, bufsz, struct iwl_mld)           \
        WIPHY_DEBUGFS_READ_WRAPPER_MLD(name)                            \
        static const struct file_operations iwl_dbgfs_##name##_ops = {  \
                .read = __iwl_dbgfs_##name##_read,                      \
                .open = _iwl_dbgfs_##name##_open,                       \
                .llseek = generic_file_llseek,                          \
                .release = _iwl_dbgfs_release,                          \
        }

#define WIPHY_DEBUGFS_WRITE_FILE_OPS_MLD(name, bufsz)                   \
        MLD_DEBUGFS_OPEN_WRAPPER(name, bufsz, struct iwl_mld)           \
        WIPHY_DEBUGFS_WRITE_WRAPPER_MLD(name)                           \
        static const struct file_operations iwl_dbgfs_##name##_ops = {  \
                .write = __iwl_dbgfs_##name##_write,                    \
                .open = _iwl_dbgfs_##name##_open,                       \
                .llseek = generic_file_llseek,                          \
                .release = _iwl_dbgfs_release,                          \
        }

#define WIPHY_DEBUGFS_READ_WRITE_FILE_OPS_MLD(name, bufsz)              \
        MLD_DEBUGFS_OPEN_WRAPPER(name, bufsz, struct iwl_mld)           \
        WIPHY_DEBUGFS_WRITE_WRAPPER_MLD(name)                           \
        WIPHY_DEBUGFS_READ_WRAPPER_MLD(name)                            \
        static const struct file_operations iwl_dbgfs_##name##_ops = {  \
                .write = __iwl_dbgfs_##name##_write,                    \
                .read = __iwl_dbgfs_##name##_read,                      \
                .open = _iwl_dbgfs_##name##_open,                       \
                .llseek = generic_file_llseek,                          \
                .release = _iwl_dbgfs_release,                          \
        }

#define WIPHY_DEBUGFS_WRITE_WRAPPER_IEEE80211(name, bufsz, objtype)     \
WIPHY_DEBUGFS_WRITE_HANDLER_WRAPPER(name)                               \
static ssize_t _iwl_dbgfs_##name##_write(struct file *file,             \
                                          const char __user *user_buf,  \
                                          size_t count, loff_t *ppos)   \
{                                                                       \
        struct dbgfs_##name##_data *data = file->private_data;          \
        struct ieee80211_##objtype *arg = data->arg;                    \
        struct iwl_mld *mld = iwl_mld_from_##objtype(arg);              \
        char buf[bufsz] = {};                                           \
                                                                        \
        return wiphy_locked_debugfs_write(mld->wiphy, file,             \
                                buf, sizeof(buf),                       \
                                user_buf, count,                        \
                                iwl_dbgfs_##name##_write_handler,       \
                                arg);                                   \
}

#define IEEE80211_WIPHY_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz, objtype) \
        MLD_DEBUGFS_OPEN_WRAPPER(name, bufsz, struct ieee80211_##objtype) \
        WIPHY_DEBUGFS_WRITE_WRAPPER_IEEE80211(name, bufsz, objtype)       \
        MLD_DEBUGFS_READ_WRAPPER(name)                                    \
        static const struct file_operations iwl_dbgfs_##name##_ops = {    \
                .write = _iwl_dbgfs_##name##_write,                       \
                .read = _iwl_dbgfs_##name##_read,                         \
                .open = _iwl_dbgfs_##name##_open,                         \
                .llseek = generic_file_llseek,                            \
                .release = _iwl_dbgfs_release,                            \
        }