gpiolib: cdev: support GPIO_V2_LINE_SET_CONFIG_IOCTL
Add support for GPIO_V2_LINE_SET_CONFIG_IOCTL, the uAPI v2 line set config ioctl. Signed-off-by: Kent Gibson <warthog618@gmail.com> Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
This commit is contained in:
		
							parent
							
								
									73e0341992
								
							
						
					
					
						commit
						a54756cb24
					
				| @ -16,6 +16,7 @@ | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/kfifo.h> | ||||
| #include <linux/module.h> | ||||
| #include <linux/mutex.h> | ||||
| #include <linux/pinctrl/consumer.h> | ||||
| #include <linux/poll.h> | ||||
| #include <linux/spinlock.h> | ||||
| @ -444,6 +445,8 @@ struct line { | ||||
|  * @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. | ||||
|  * @lines: the lines held by this line request, with @num_lines elements. | ||||
|  */ | ||||
| struct linereq { | ||||
| @ -454,6 +457,7 @@ struct linereq { | ||||
| 	u32 event_buffer_size; | ||||
| 	DECLARE_KFIFO_PTR(events, struct gpio_v2_line_event); | ||||
| 	atomic_t seqno; | ||||
| 	struct mutex config_mutex; | ||||
| 	struct line lines[]; | ||||
| }; | ||||
| 
 | ||||
| @ -574,6 +578,8 @@ static void edge_detector_stop(struct line *line) | ||||
| 		free_irq(line->irq, line); | ||||
| 		line->irq = 0; | ||||
| 	} | ||||
| 
 | ||||
| 	line->eflags = 0; | ||||
| } | ||||
| 
 | ||||
| static int edge_detector_setup(struct line *line, | ||||
| @ -615,6 +621,17 @@ static int edge_detector_setup(struct line *line, | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int edge_detector_update(struct line *line, u64 eflags, | ||||
| 				bool polarity_change) | ||||
| { | ||||
| 	if ((line->eflags == eflags) && !polarity_change) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	edge_detector_stop(line); | ||||
| 
 | ||||
| 	return edge_detector_setup(line, eflags); | ||||
| } | ||||
| 
 | ||||
| static u64 gpio_v2_line_config_flags(struct gpio_v2_line_config *lc, | ||||
| 				     unsigned int line_idx) | ||||
| { | ||||
| @ -799,6 +816,74 @@ static long linereq_get_values(struct linereq *lr, void __user *ip) | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static long linereq_set_config_unlocked(struct linereq *lr, | ||||
| 					struct gpio_v2_line_config *lc) | ||||
| { | ||||
| 	struct gpio_desc *desc; | ||||
| 	unsigned int i; | ||||
| 	u64 flags; | ||||
| 	bool polarity_change; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	for (i = 0; i < lr->num_lines; i++) { | ||||
| 		desc = lr->lines[i].desc; | ||||
| 		flags = gpio_v2_line_config_flags(lc, i); | ||||
| 		polarity_change = | ||||
| 			(!!test_bit(FLAG_ACTIVE_LOW, &desc->flags) != | ||||
| 			 ((flags & GPIO_V2_LINE_FLAG_ACTIVE_LOW) != 0)); | ||||
| 
 | ||||
| 		gpio_v2_line_config_flags_to_desc_flags(flags, &desc->flags); | ||||
| 		/*
 | ||||
| 		 * Lines have to be requested explicitly for input | ||||
| 		 * or output, else the line will be treated "as is". | ||||
| 		 */ | ||||
| 		if (flags & GPIO_V2_LINE_FLAG_OUTPUT) { | ||||
| 			int val = gpio_v2_line_config_output_value(lc, i); | ||||
| 
 | ||||
| 			edge_detector_stop(&lr->lines[i]); | ||||
| 			ret = gpiod_direction_output(desc, val); | ||||
| 			if (ret) | ||||
| 				return ret; | ||||
| 		} else if (flags & GPIO_V2_LINE_FLAG_INPUT) { | ||||
| 			ret = gpiod_direction_input(desc); | ||||
| 			if (ret) | ||||
| 				return ret; | ||||
| 
 | ||||
| 			ret = edge_detector_update(&lr->lines[i], | ||||
| 					flags & GPIO_V2_LINE_EDGE_FLAGS, | ||||
| 					polarity_change); | ||||
| 			if (ret) | ||||
| 				return ret; | ||||
| 		} | ||||
| 
 | ||||
| 		blocking_notifier_call_chain(&desc->gdev->notifier, | ||||
| 					     GPIO_V2_LINE_CHANGED_CONFIG, | ||||
| 					     desc); | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static long linereq_set_config(struct linereq *lr, void __user *ip) | ||||
| { | ||||
| 	struct gpio_v2_line_config lc; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	if (copy_from_user(&lc, ip, sizeof(lc))) | ||||
| 		return -EFAULT; | ||||
| 
 | ||||
| 	ret = gpio_v2_line_config_validate(&lc, lr->num_lines); | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	mutex_lock(&lr->config_mutex); | ||||
| 
 | ||||
| 	ret = linereq_set_config_unlocked(lr, &lc); | ||||
| 
 | ||||
| 	mutex_unlock(&lr->config_mutex); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static long linereq_ioctl(struct file *file, unsigned int cmd, | ||||
| 			  unsigned long arg) | ||||
| { | ||||
| @ -807,6 +892,8 @@ static long linereq_ioctl(struct file *file, unsigned int cmd, | ||||
| 
 | ||||
| 	if (cmd == GPIO_V2_LINE_GET_VALUES_IOCTL) | ||||
| 		return linereq_get_values(lr, ip); | ||||
| 	else if (cmd == GPIO_V2_LINE_SET_CONFIG_IOCTL) | ||||
| 		return linereq_set_config(lr, ip); | ||||
| 
 | ||||
| 	return -EINVAL; | ||||
| } | ||||
| @ -968,6 +1055,7 @@ static int linereq_create(struct gpio_device *gdev, void __user *ip) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	mutex_init(&lr->config_mutex); | ||||
| 	init_waitqueue_head(&lr->wait); | ||||
| 	lr->event_buffer_size = ulr.event_buffer_size; | ||||
| 	if (lr->event_buffer_size == 0) | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user