linux/drivers/gpio/gpiolib-cdev.c

// SPDX-License-Identifier: GPL-2.0

#include <linux/anon_inodes.h>
#include <linux/atomic.h>
#include <linux/bitmap.h>
#include <linux/build_bug.h>
#include <linux/cdev.h>
#include <linux/cleanup.h>
#include <linux/compat.h>
#include <linux/compiler.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/file.h>
#include <linux/gpio.h>
#include <linux/gpio/driver.h>
#include <linux/hte.h>
#include <linux/interrupt.h>
#include <linux/irqreturn.h>
#include <linux/kernel.h>
#include <linux/kfifo.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/overflow.h>
#include <linux/pinctrl/consumer.h>
#include <linux/poll.h>
#include <linux/rbtree.h>
#include <linux/seq_file.h>
#include <linux/spinlock.h>
#include <linux/timekeeping.h>
#include <linux/uaccess.h>
#include <linux/workqueue.h>

#include <uapi/linux/gpio.h>

#include "gpiolib.h"
#include "gpiolib-cdev.h"

/*
 * Array sizes must ensure 64-bit alignment and not create holes in the
 * struct packing.
 */
static_assert();
static_assert();

/*
 * Check that uAPI structs are 64-bit aligned for 32/64-bit compatibility
 */
static_assert();
static_assert();
static_assert();
static_assert();
static_assert();
static_assert();
static_assert();
static_assert();

/* Character device interface to GPIO.
 *
 * The GPIO character device, /dev/gpiochipN, provides userspace an
 * interface to gpiolib GPIOs via ioctl()s.
 */

/*
 * GPIO line handle management
 */

#ifdef CONFIG_GPIO_CDEV_V1
/**
 * struct linehandle_state - contains the state of a userspace handle
 * @gdev: the GPIO device the handle pertains to
 * @label: consumer label used to tag descriptors
 * @descs: the GPIO descriptors held by this handle
 * @num_descs: the number of descriptors held in the descs array
 */
struct linehandle_state {};

#define GPIOHANDLE_REQUEST_VALID_FLAGS

#define GPIOHANDLE_REQUEST_DIRECTION_FLAGS

static int linehandle_validate_flags(u32 flags)
{}

static void linehandle_flags_to_desc_flags(u32 lflags, unsigned long *flagsp)
{}

static long linehandle_set_config(struct linehandle_state *lh,
				  void __user *ip)
{}

static long linehandle_ioctl(struct file *file, unsigned int cmd,
			     unsigned long arg)
{}

#ifdef CONFIG_COMPAT
static long linehandle_ioctl_compat(struct file *file, unsigned int cmd,
				    unsigned long arg)
{}
#endif

static void linehandle_free(struct linehandle_state *lh)
{}

static int linehandle_release(struct inode *inode, struct file *file)
{}

static const struct file_operations linehandle_fileops =;

static int linehandle_create(struct gpio_device *gdev, void __user *ip)
{}
#endif /* CONFIG_GPIO_CDEV_V1 */

/**
 * struct line - contains the state of a requested line
 * @node: to store the object in supinfo_tree if supplemental
 * @desc: the GPIO descriptor for this line.
 * @req: the corresponding line request
 * @irq: the interrupt triggered in response to events on this GPIO
 * @edflags: the edge flags, GPIO_V2_LINE_FLAG_EDGE_RISING and/or
 * GPIO_V2_LINE_FLAG_EDGE_FALLING, indicating the edge detection applied
 * @timestamp_ns: cache for the timestamp storing it between hardirq and
 * IRQ thread, used to bring the timestamp close to the actual event
 * @req_seqno: the seqno for the current edge event in the sequence of
 * events for the corresponding line request. This is drawn from the @req.
 * @line_seqno: the seqno for the current edge event in the sequence of
 * events for this line.
 * @work: the worker that implements software debouncing
 * @debounce_period_us: the debounce period in microseconds
 * @sw_debounced: flag indicating if the software debouncer is active
 * @level: the current debounced physical level of the line
 * @hdesc: the Hardware Timestamp Engine (HTE) descriptor
 * @raw_level: the line level at the time of event
 * @total_discard_seq: the running counter of the discarded events
 * @last_seqno: the last sequence number before debounce period expires
 */
struct line {};

/*
 * a rbtree of the struct lines containing supplemental info.
 * Used to populate gpio_v2_line_info with cdev specific fields not contained
 * in the struct gpio_desc.
 * A line is determined to contain supplemental information by
 * line_has_supinfo().
 */
static struct rb_root supinfo_tree =;
/* covers supinfo_tree */
static DEFINE_SPINLOCK(supinfo_lock);

/**
 * struct linereq - contains the state of a userspace line request
 * @gdev: the GPIO device the line request pertains to
 * @label: consumer label used to tag GPIO descriptors
 * @num_lines: the number of lines in the lines array
 * @wait: wait queue that handles blocking reads of events
 * @device_unregistered_nb: notifier block for receiving gdev unregister events
 * @event_buffer_size: the number of elements allocated in @events
 * @events: KFIFO for the GPIO events
 * @seqno: the sequence number for edge events generated on all lines in
 * this line request.  Note that this is not used when @num_lines is 1, as
 * the line_seqno is then the same and is cheaper to calculate.
 * @config_mutex: mutex for serializing ioctl() calls to ensure consistency
 * of configuration, particularly multi-step accesses to desc flags and
 * changes to supinfo status.
 * @lines: the lines held by this line request, with @num_lines elements.
 */
struct linereq {};

static void supinfo_insert(struct line *line)
{}

static void supinfo_erase(struct line *line)
{}

static struct line *supinfo_find(struct gpio_desc *desc)
{}

static void supinfo_to_lineinfo(struct gpio_desc *desc,
				struct gpio_v2_line_info *info)
{}

static inline bool line_has_supinfo(struct line *line)
{}

/*
 * Checks line_has_supinfo() before and after the change to avoid unnecessary
 * supinfo_tree access.
 * Called indirectly by linereq_create() or linereq_set_config() so line
 * is already protected from concurrent changes.
 */
static void line_set_debounce_period(struct line *line,
				     unsigned int debounce_period_us)
{}

#define GPIO_V2_LINE_BIAS_FLAGS

#define GPIO_V2_LINE_DIRECTION_FLAGS

#define GPIO_V2_LINE_DRIVE_FLAGS

#define GPIO_V2_LINE_EDGE_FLAGS

#define GPIO_V2_LINE_FLAG_EDGE_BOTH

#define GPIO_V2_LINE_VALID_FLAGS

/* subset of flags relevant for edge detector configuration */
#define GPIO_V2_LINE_EDGE_DETECTOR_FLAGS

static int linereq_unregistered_notify(struct notifier_block *nb,
				       unsigned long action, void *data)
{}

static void linereq_put_event(struct linereq *lr,
			      struct gpio_v2_line_event *le)
{}

static u64 line_event_timestamp(struct line *line)
{}

static u32 line_event_id(int level)
{}

static inline char *make_irq_label(const char *orig)
{}

static inline void free_irq_label(const char *label)
{}

#ifdef CONFIG_HTE

static enum hte_return process_hw_ts_thread(void *p)
{}

static enum hte_return process_hw_ts(struct hte_ts_data *ts, void *p)
{}

static int hte_edge_setup(struct line *line, u64 eflags)
{}

#else

static int hte_edge_setup(struct line *line, u64 eflags)
{
	return 0;
}
#endif /* CONFIG_HTE */

static irqreturn_t edge_irq_thread(int irq, void *p)
{}

static irqreturn_t edge_irq_handler(int irq, void *p)
{}

/*
 * returns the current debounced logical value.
 */
static bool debounced_value(struct line *line)
{}

static irqreturn_t debounce_irq_handler(int irq, void *p)
{}

static void debounce_work_func(struct work_struct *work)
{}

static int debounce_setup(struct line *line, unsigned int debounce_period_us)
{}

static bool gpio_v2_line_config_debounced(struct gpio_v2_line_config *lc,
					  unsigned int line_idx)
{}

static u32 gpio_v2_line_config_debounce_period(struct gpio_v2_line_config *lc,
					       unsigned int line_idx)
{}

static void edge_detector_stop(struct line *line)
{}

static int edge_detector_fifo_init(struct linereq *req)
{}

static int edge_detector_setup(struct line *line,
			       struct gpio_v2_line_config *lc,
			       unsigned int line_idx, u64 edflags)
{}

static int edge_detector_update(struct line *line,
				struct gpio_v2_line_config *lc,
				unsigned int line_idx, u64 edflags)
{}

static u64 gpio_v2_line_config_flags(struct gpio_v2_line_config *lc,
				     unsigned int line_idx)
{}

static int gpio_v2_line_config_output_value(struct gpio_v2_line_config *lc,
					    unsigned int line_idx)
{}

static int gpio_v2_line_flags_validate(u64 flags)
{}

static int gpio_v2_line_config_validate(struct gpio_v2_line_config *lc,
					unsigned int num_lines)
{}

static void gpio_v2_line_config_flags_to_desc_flags(u64 flags,
						    unsigned long *flagsp)
{}

static long linereq_get_values(struct linereq *lr, void __user *ip)
{}

static long linereq_set_values(struct linereq *lr, void __user *ip)
{}

static long linereq_set_config(struct linereq *lr, void __user *ip)
{}

static long linereq_ioctl(struct file *file, unsigned int cmd,
			  unsigned long arg)
{}

#ifdef CONFIG_COMPAT
static long linereq_ioctl_compat(struct file *file, unsigned int cmd,
				 unsigned long arg)
{}
#endif

static __poll_t linereq_poll(struct file *file,
			     struct poll_table_struct *wait)
{}

static ssize_t linereq_read(struct file *file, char __user *buf,
			    size_t count, loff_t *f_ps)
{}

static void linereq_free(struct linereq *lr)
{}

static int linereq_release(struct inode *inode, struct file *file)
{}

#ifdef CONFIG_PROC_FS
static void linereq_show_fdinfo(struct seq_file *out, struct file *file)
{}
#endif

static const struct file_operations line_fileops =;

static int linereq_create(struct gpio_device *gdev, void __user *ip)
{}

#ifdef CONFIG_GPIO_CDEV_V1

/*
 * GPIO line event management
 */

/**
 * struct lineevent_state - contains the state of a userspace event
 * @gdev: the GPIO device the event pertains to
 * @label: consumer label used to tag descriptors
 * @desc: the GPIO descriptor held by this event
 * @eflags: the event flags this line was requested with
 * @irq: the interrupt that trigger in response to events on this GPIO
 * @wait: wait queue that handles blocking reads of events
 * @device_unregistered_nb: notifier block for receiving gdev unregister events
 * @events: KFIFO for the GPIO events
 * @timestamp: cache for the timestamp storing it between hardirq
 * and IRQ thread, used to bring the timestamp close to the actual
 * event
 */
struct lineevent_state {};

#define GPIOEVENT_REQUEST_VALID_FLAGS

static __poll_t lineevent_poll(struct file *file,
			       struct poll_table_struct *wait)
{}

static int lineevent_unregistered_notify(struct notifier_block *nb,
					 unsigned long action, void *data)
{}

struct compat_gpioeevent_data {};

static ssize_t lineevent_read(struct file *file, char __user *buf,
			      size_t count, loff_t *f_ps)
{}

static void lineevent_free(struct lineevent_state *le)
{}

static int lineevent_release(struct inode *inode, struct file *file)
{}

static long lineevent_ioctl(struct file *file, unsigned int cmd,
			    unsigned long arg)
{}

#ifdef CONFIG_COMPAT
static long lineevent_ioctl_compat(struct file *file, unsigned int cmd,
				   unsigned long arg)
{}
#endif

static const struct file_operations lineevent_fileops =;

static irqreturn_t lineevent_irq_thread(int irq, void *p)
{}

static irqreturn_t lineevent_irq_handler(int irq, void *p)
{}

static int lineevent_create(struct gpio_device *gdev, void __user *ip)
{}

static void gpio_v2_line_info_to_v1(struct gpio_v2_line_info *info_v2,
				    struct gpioline_info *info_v1)
{}

static void gpio_v2_line_info_changed_to_v1(
		struct gpio_v2_line_info_changed *lic_v2,
		struct gpioline_info_changed *lic_v1)
{}

#endif /* CONFIG_GPIO_CDEV_V1 */

static void gpio_desc_to_lineinfo(struct gpio_desc *desc,
				  struct gpio_v2_line_info *info)
{}

struct gpio_chardev_data {};

static int chipinfo_get(struct gpio_chardev_data *cdev, void __user *ip)
{}

#ifdef CONFIG_GPIO_CDEV_V1
/*
 * returns 0 if the versions match, else the previously selected ABI version
 */
static int lineinfo_ensure_abi_version(struct gpio_chardev_data *cdata,
				       unsigned int version)
{}

static int lineinfo_get_v1(struct gpio_chardev_data *cdev, void __user *ip,
			   bool watch)
{}
#endif

static int lineinfo_get(struct gpio_chardev_data *cdev, void __user *ip,
			bool watch)
{}

static int lineinfo_unwatch(struct gpio_chardev_data *cdev, void __user *ip)
{}

/*
 * gpio_ioctl() - ioctl handler for the GPIO chardev
 */
static long gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{}

#ifdef CONFIG_COMPAT
static long gpio_ioctl_compat(struct file *file, unsigned int cmd,
			      unsigned long arg)
{}
#endif

static int lineinfo_changed_notify(struct notifier_block *nb,
				   unsigned long action, void *data)
{}

static int gpio_device_unregistered_notify(struct notifier_block *nb,
					   unsigned long action, void *data)
{}

static __poll_t lineinfo_watch_poll(struct file *file,
				    struct poll_table_struct *pollt)
{}

static ssize_t lineinfo_watch_read(struct file *file, char __user *buf,
				   size_t count, loff_t *off)
{}

/**
 * gpio_chrdev_open() - open the chardev for ioctl operations
 * @inode: inode for this chardev
 * @file: file struct for storing private data
 * Returns 0 on success
 */
static int gpio_chrdev_open(struct inode *inode, struct file *file)
{}

/**
 * gpio_chrdev_release() - close chardev after ioctl operations
 * @inode: inode for this chardev
 * @file: file struct for storing private data
 * Returns 0 on success
 */
static int gpio_chrdev_release(struct inode *inode, struct file *file)
{}

static const struct file_operations gpio_fileops =;

int gpiolib_cdev_register(struct gpio_device *gdev, dev_t devt)
{}

void gpiolib_cdev_unregister(struct gpio_device *gdev)
{}