forked from mirrors/linux
		
	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
					
				
					 1 changed files with 88 additions and 0 deletions
				
			
		|  | @ -16,6 +16,7 @@ | ||||||
| #include <linux/kernel.h> | #include <linux/kernel.h> | ||||||
| #include <linux/kfifo.h> | #include <linux/kfifo.h> | ||||||
| #include <linux/module.h> | #include <linux/module.h> | ||||||
|  | #include <linux/mutex.h> | ||||||
| #include <linux/pinctrl/consumer.h> | #include <linux/pinctrl/consumer.h> | ||||||
| #include <linux/poll.h> | #include <linux/poll.h> | ||||||
| #include <linux/spinlock.h> | #include <linux/spinlock.h> | ||||||
|  | @ -444,6 +445,8 @@ struct line { | ||||||
|  * @seqno: the sequence number for edge events generated on all lines in |  * @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 |  * 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. |  * 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. |  * @lines: the lines held by this line request, with @num_lines elements. | ||||||
|  */ |  */ | ||||||
| struct linereq { | struct linereq { | ||||||
|  | @ -454,6 +457,7 @@ struct linereq { | ||||||
| 	u32 event_buffer_size; | 	u32 event_buffer_size; | ||||||
| 	DECLARE_KFIFO_PTR(events, struct gpio_v2_line_event); | 	DECLARE_KFIFO_PTR(events, struct gpio_v2_line_event); | ||||||
| 	atomic_t seqno; | 	atomic_t seqno; | ||||||
|  | 	struct mutex config_mutex; | ||||||
| 	struct line lines[]; | 	struct line lines[]; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | @ -574,6 +578,8 @@ static void edge_detector_stop(struct line *line) | ||||||
| 		free_irq(line->irq, line); | 		free_irq(line->irq, line); | ||||||
| 		line->irq = 0; | 		line->irq = 0; | ||||||
| 	} | 	} | ||||||
|  | 
 | ||||||
|  | 	line->eflags = 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static int edge_detector_setup(struct line *line, | static int edge_detector_setup(struct line *line, | ||||||
|  | @ -615,6 +621,17 @@ static int edge_detector_setup(struct line *line, | ||||||
| 	return 0; | 	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, | static u64 gpio_v2_line_config_flags(struct gpio_v2_line_config *lc, | ||||||
| 				     unsigned int line_idx) | 				     unsigned int line_idx) | ||||||
| { | { | ||||||
|  | @ -799,6 +816,74 @@ static long linereq_get_values(struct linereq *lr, void __user *ip) | ||||||
| 	return 0; | 	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, | static long linereq_ioctl(struct file *file, unsigned int cmd, | ||||||
| 			  unsigned long arg) | 			  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) | 	if (cmd == GPIO_V2_LINE_GET_VALUES_IOCTL) | ||||||
| 		return linereq_get_values(lr, ip); | 		return linereq_get_values(lr, ip); | ||||||
|  | 	else if (cmd == GPIO_V2_LINE_SET_CONFIG_IOCTL) | ||||||
|  | 		return linereq_set_config(lr, ip); | ||||||
| 
 | 
 | ||||||
| 	return -EINVAL; | 	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); | 	init_waitqueue_head(&lr->wait); | ||||||
| 	lr->event_buffer_size = ulr.event_buffer_size; | 	lr->event_buffer_size = ulr.event_buffer_size; | ||||||
| 	if (lr->event_buffer_size == 0) | 	if (lr->event_buffer_size == 0) | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Kent Gibson
						Kent Gibson