root/include/linux/fwctl.h
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES
 */
#ifndef __LINUX_FWCTL_H
#define __LINUX_FWCTL_H
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/cleanup.h>
#include <uapi/fwctl/fwctl.h>

struct fwctl_device;
struct fwctl_uctx;

/**
 * struct fwctl_ops - Driver provided operations
 *
 * fwctl_unregister() will wait until all excuting ops are completed before it
 * returns. Drivers should be mindful to not let their ops run for too long as
 * it will block device hot unplug and module unloading.
 */
struct fwctl_ops {
        /**
         * @device_type: The drivers assigned device_type number. This is uABI.
         */
        enum fwctl_device_type device_type;
        /**
         * @uctx_size: The size of the fwctl_uctx struct to allocate. The first
         * bytes of this memory will be a fwctl_uctx. The driver can use the
         * remaining bytes as its private memory.
         */
        size_t uctx_size;
        /**
         * @open_uctx: Called when a file descriptor is opened before the uctx
         * is ever used.
         */
        int (*open_uctx)(struct fwctl_uctx *uctx);
        /**
         * @close_uctx: Called when the uctx is destroyed, usually when the FD
         * is closed.
         */
        void (*close_uctx)(struct fwctl_uctx *uctx);
        /**
         * @info: Implement FWCTL_INFO. Return a kmalloc() memory that is copied
         * to out_device_data. On input length indicates the size of the user
         * buffer on output it indicates the size of the memory. The driver can
         * ignore length on input, the core code will handle everything.
         */
        void *(*info)(struct fwctl_uctx *uctx, size_t *length);
        /**
         * @fw_rpc: Implement FWCTL_RPC. Deliver rpc_in/in_len to the FW and
         * return the response and set out_len. rpc_in can be returned as the
         * response pointer. Otherwise the returned pointer is freed with
         * kvfree().
         */
        void *(*fw_rpc)(struct fwctl_uctx *uctx, enum fwctl_rpc_scope scope,
                        void *rpc_in, size_t in_len, size_t *out_len);
};

/**
 * struct fwctl_device - Per-driver registration struct
 * @dev: The sysfs (class/fwctl/fwctlXX) device
 *
 * Each driver instance will have one of these structs with the driver private
 * data following immediately after. This struct is refcounted, it is freed by
 * calling fwctl_put().
 */
struct fwctl_device {
        struct device dev;
        /* private: */
        struct cdev cdev;

        /* Protect uctx_list */
        struct mutex uctx_list_lock;
        struct list_head uctx_list;
        /*
         * Protect ops, held for write when ops becomes NULL during unregister,
         * held for read whenever ops is loaded or an ops function is running.
         */
        struct rw_semaphore registration_lock;
        const struct fwctl_ops *ops;
};

struct fwctl_device *_fwctl_alloc_device(struct device *parent,
                                         const struct fwctl_ops *ops,
                                         size_t size);
/**
 * fwctl_alloc_device - Allocate a fwctl
 * @parent: Physical device that provides the FW interface
 * @ops: Driver ops to register
 * @drv_struct: 'struct driver_fwctl' that holds the struct fwctl_device
 * @member: Name of the struct fwctl_device in @drv_struct
 *
 * This allocates and initializes the fwctl_device embedded in the drv_struct.
 * Upon success the pointer must be freed via fwctl_put(). Returns a 'drv_struct
 * \*' on success, NULL on error.
 */
#define fwctl_alloc_device(parent, ops, drv_struct, member)               \
        ({                                                                \
                static_assert(__same_type(struct fwctl_device,            \
                                          ((drv_struct *)NULL)->member)); \
                static_assert(offsetof(drv_struct, member) == 0);         \
                (drv_struct *)_fwctl_alloc_device(parent, ops,            \
                                                  sizeof(drv_struct));    \
        })

static inline struct fwctl_device *fwctl_get(struct fwctl_device *fwctl)
{
        get_device(&fwctl->dev);
        return fwctl;
}
static inline void fwctl_put(struct fwctl_device *fwctl)
{
        put_device(&fwctl->dev);
}
DEFINE_FREE(fwctl, struct fwctl_device *, if (_T) fwctl_put(_T));

int fwctl_register(struct fwctl_device *fwctl);
void fwctl_unregister(struct fwctl_device *fwctl);

/**
 * struct fwctl_uctx - Per user FD context
 * @fwctl: fwctl instance that owns the context
 *
 * Every FD opened by userspace will get a unique context allocation. Any driver
 * private data will follow immediately after.
 */
struct fwctl_uctx {
        struct fwctl_device *fwctl;
        /* private: */
        /* Head at fwctl_device::uctx_list */
        struct list_head uctx_list_entry;
};

#endif