/* SPDX-License-Identifier: GPL-2.0 */
/*
 * Fieldbus Device Driver Core
 *
 */

#ifndef __FIELDBUS_DEV_H
#define __FIELDBUS_DEV_H

#include <linux/cdev.h>
#include <linux/wait.h>

enum fieldbus_dev_type {
	FIELDBUS_DEV_TYPE_UNKNOWN = 0,
	FIELDBUS_DEV_TYPE_PROFINET,
};

enum fieldbus_dev_offl_mode {
	FIELDBUS_DEV_OFFL_MODE_CLEAR = 0,
	FIELDBUS_DEV_OFFL_MODE_FREEZE,
	FIELDBUS_DEV_OFFL_MODE_SET
};

/**
 * struct fieldbus_dev - Fieldbus device
 * @read_area:		[DRIVER] function to read the process data area of the
 *				 device. same parameters/return values as
 *				 the read function in struct file_operations
 * @write_area:		[DRIVER] function to write to the process data area of
 *				 the device. same parameters/return values as
 *				 the write function in struct file_operations
 * @write_area_sz	[DRIVER] size of the writable process data area
 * @read_area_sz	[DRIVER] size of the readable process data area
 * @card_name		[DRIVER] name of the card, e.g. "ACME Inc. profinet"
 * @fieldbus_type	[DRIVER] fieldbus type of this device, e.g.
 *					FIELDBUS_DEV_TYPE_PROFINET
 * @enable_get		[DRIVER] function which returns true if the card
 *				 is enabled, false otherwise
 * @fieldbus_id_get	[DRIVER] function to retrieve the unique fieldbus id
 *				 by which this device can be identified;
 *				 return value follows the snprintf convention
 * @simple_enable_set	[DRIVER] (optional) function to enable the device
 *				 according to its default settings
 * @parent		[DRIVER] (optional) the device's parent device
 */
struct fieldbus_dev {
	ssize_t (*read_area)(struct fieldbus_dev *fbdev, char __user *buf,
			     size_t size, loff_t *offset);
	ssize_t (*write_area)(struct fieldbus_dev *fbdev,
			      const char __user *buf, size_t size,
			      loff_t *offset);
	size_t write_area_sz, read_area_sz;
	const char *card_name;
	enum fieldbus_dev_type fieldbus_type;
	bool (*enable_get)(struct fieldbus_dev *fbdev);
	int (*fieldbus_id_get)(struct fieldbus_dev *fbdev, char *buf,
			       size_t max_size);
	int (*simple_enable_set)(struct fieldbus_dev *fbdev, bool enable);
	struct device *parent;

	/* private data */
	int id;
	struct cdev cdev;
	struct device *dev;
	int dc_event;
	wait_queue_head_t dc_wq;
	bool online;
};

#if IS_ENABLED(CONFIG_FIELDBUS_DEV)

/**
 * fieldbus_dev_unregister()
 *	- unregister a previously registered fieldbus device
 * @fb:		Device structure previously registered
 **/
void fieldbus_dev_unregister(struct fieldbus_dev *fb);

/**
 * fieldbus_dev_register()
 *	- register a device with the fieldbus device subsystem
 * @fb:		Device structure filled by the device driver
 **/
int __must_check fieldbus_dev_register(struct fieldbus_dev *fb);

/**
 * fieldbus_dev_area_updated()
 *	- notify the subsystem that an external fieldbus controller updated
 *			the process data area
 * @fb:		Device structure
 **/
void fieldbus_dev_area_updated(struct fieldbus_dev *fb);

/**
 * fieldbus_dev_online_changed()
 *	- notify the subsystem that the fieldbus online status changed
 * @fb:		Device structure
 **/
void fieldbus_dev_online_changed(struct fieldbus_dev *fb, bool online);

#else /* IS_ENABLED(CONFIG_FIELDBUS_DEV) */

static inline void fieldbus_dev_unregister(struct fieldbus_dev *fb) {}
static inline int __must_check fieldbus_dev_register(struct fieldbus_dev *fb)
{
	return -ENOTSUPP;
}

static inline void fieldbus_dev_area_updated(struct fieldbus_dev *fb) {}
static inline void fieldbus_dev_online_changed(struct fieldbus_dev *fb,
					       bool online) {}

#endif /* IS_ENABLED(CONFIG_FIELDBUS_DEV) */
#endif /* __FIELDBUS_DEV_H */