/* SPDX-License-Identifier: GPL-2.0-only */

#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 {
	
	enum fwctl_device_type device_type;
	
	size_t uctx_size;
	
	int (*open_uctx)(struct fwctl_uctx *uctx);
	
	void (*close_uctx)(struct fwctl_uctx *uctx);
	
	void *(*info)(struct fwctl_uctx *uctx, size_t *length);
	
	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 {
	struct device dev;
	
	struct cdev cdev;

	
	struct mutex uctx_list_lock;
	struct list_head uctx_list;
	
	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);

#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 {
	struct fwctl_device *fwctl;
	
	
	struct list_head uctx_list_entry;
};

#endif
