forked from mirrors/linux
		
	serdev: Add serdev_device_write subroutine
Add serdev_device_write() a blocking call allowing to transfer arbitraty amount of data (potentially exceeding amount that serdev_device_write_buf can process in a single call) To support that, also add serdev_device_write_wakeup(). Drivers wanting to use full extent of serdev_device_write functionality are expected to provide serdev_device_write_wakeup() as a sole handler of .write_wakeup event or call it as a part of driver's custom .write_wakeup code. Because serdev_device_write() subroutine is a superset of serdev_device_write_buf() the patch re-impelements latter is terms of the former. For drivers wanting to just use serdev_device_write_buf() .write_wakeup handler is optional. Cc: cphealy@gmail.com Cc: Guenter Roeck <linux@roeck-us.net> Cc: linux-serial@vger.kernel.org Cc: linux-kernel@vger.kernel.org Reviewed-by: Rob Herring <robh@kernel.org> Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com> Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
		
							parent
							
								
									e1dc9b0805
								
							
						
					
					
						commit
						6fe729c4bd
					
				
					 2 changed files with 46 additions and 7 deletions
				
			
		|  | @ -116,17 +116,41 @@ void serdev_device_close(struct serdev_device *serdev) | ||||||
| } | } | ||||||
| EXPORT_SYMBOL_GPL(serdev_device_close); | EXPORT_SYMBOL_GPL(serdev_device_close); | ||||||
| 
 | 
 | ||||||
| int serdev_device_write_buf(struct serdev_device *serdev, | void serdev_device_write_wakeup(struct serdev_device *serdev) | ||||||
| 			    const unsigned char *buf, size_t count) | { | ||||||
|  | 	complete(&serdev->write_comp); | ||||||
|  | } | ||||||
|  | EXPORT_SYMBOL_GPL(serdev_device_write_wakeup); | ||||||
|  | 
 | ||||||
|  | int serdev_device_write(struct serdev_device *serdev, | ||||||
|  | 			const unsigned char *buf, size_t count, | ||||||
|  | 			unsigned long timeout) | ||||||
| { | { | ||||||
| 	struct serdev_controller *ctrl = serdev->ctrl; | 	struct serdev_controller *ctrl = serdev->ctrl; | ||||||
|  | 	int ret; | ||||||
| 
 | 
 | ||||||
| 	if (!ctrl || !ctrl->ops->write_buf) | 	if (!ctrl || !ctrl->ops->write_buf || | ||||||
|  | 	    (timeout && !serdev->ops->write_wakeup)) | ||||||
| 		return -EINVAL; | 		return -EINVAL; | ||||||
| 
 | 
 | ||||||
| 	return ctrl->ops->write_buf(ctrl, buf, count); | 	mutex_lock(&serdev->write_lock); | ||||||
|  | 	do { | ||||||
|  | 		reinit_completion(&serdev->write_comp); | ||||||
|  | 
 | ||||||
|  | 		ret = ctrl->ops->write_buf(ctrl, buf, count); | ||||||
|  | 		if (ret < 0) | ||||||
|  | 			break; | ||||||
|  | 
 | ||||||
|  | 		buf += ret; | ||||||
|  | 		count -= ret; | ||||||
|  | 
 | ||||||
|  | 	} while (count && | ||||||
|  | 		 (timeout = wait_for_completion_timeout(&serdev->write_comp, | ||||||
|  | 							timeout))); | ||||||
|  | 	mutex_unlock(&serdev->write_lock); | ||||||
|  | 	return ret < 0 ? ret : (count ? -ETIMEDOUT : 0); | ||||||
| } | } | ||||||
| EXPORT_SYMBOL_GPL(serdev_device_write_buf); | EXPORT_SYMBOL_GPL(serdev_device_write); | ||||||
| 
 | 
 | ||||||
| void serdev_device_write_flush(struct serdev_device *serdev) | void serdev_device_write_flush(struct serdev_device *serdev) | ||||||
| { | { | ||||||
|  | @ -232,6 +256,8 @@ struct serdev_device *serdev_device_alloc(struct serdev_controller *ctrl) | ||||||
| 	serdev->dev.parent = &ctrl->dev; | 	serdev->dev.parent = &ctrl->dev; | ||||||
| 	serdev->dev.bus = &serdev_bus_type; | 	serdev->dev.bus = &serdev_bus_type; | ||||||
| 	serdev->dev.type = &serdev_device_type; | 	serdev->dev.type = &serdev_device_type; | ||||||
|  | 	init_completion(&serdev->write_comp); | ||||||
|  | 	mutex_init(&serdev->write_lock); | ||||||
| 	return serdev; | 	return serdev; | ||||||
| } | } | ||||||
| EXPORT_SYMBOL_GPL(serdev_device_alloc); | EXPORT_SYMBOL_GPL(serdev_device_alloc); | ||||||
|  |  | ||||||
|  | @ -39,12 +39,16 @@ struct serdev_device_ops { | ||||||
|  * @nr:		Device number on serdev bus. |  * @nr:		Device number on serdev bus. | ||||||
|  * @ctrl:	serdev controller managing this device. |  * @ctrl:	serdev controller managing this device. | ||||||
|  * @ops:	Device operations. |  * @ops:	Device operations. | ||||||
|  |  * @write_comp	Completion used by serdev_device_write() internally | ||||||
|  |  * @write_lock	Lock to serialize access when writing data | ||||||
|  */ |  */ | ||||||
| struct serdev_device { | struct serdev_device { | ||||||
| 	struct device dev; | 	struct device dev; | ||||||
| 	int nr; | 	int nr; | ||||||
| 	struct serdev_controller *ctrl; | 	struct serdev_controller *ctrl; | ||||||
| 	const struct serdev_device_ops *ops; | 	const struct serdev_device_ops *ops; | ||||||
|  | 	struct completion write_comp; | ||||||
|  | 	struct mutex write_lock; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| static inline struct serdev_device *to_serdev_device(struct device *d) | static inline struct serdev_device *to_serdev_device(struct device *d) | ||||||
|  | @ -186,7 +190,8 @@ int serdev_device_open(struct serdev_device *); | ||||||
| void serdev_device_close(struct serdev_device *); | void serdev_device_close(struct serdev_device *); | ||||||
| unsigned int serdev_device_set_baudrate(struct serdev_device *, unsigned int); | unsigned int serdev_device_set_baudrate(struct serdev_device *, unsigned int); | ||||||
| void serdev_device_set_flow_control(struct serdev_device *, bool); | void serdev_device_set_flow_control(struct serdev_device *, bool); | ||||||
| int serdev_device_write_buf(struct serdev_device *, const unsigned char *, size_t); | void serdev_device_write_wakeup(struct serdev_device *); | ||||||
|  | int serdev_device_write(struct serdev_device *, const unsigned char *, size_t, unsigned long); | ||||||
| void serdev_device_write_flush(struct serdev_device *); | void serdev_device_write_flush(struct serdev_device *); | ||||||
| int serdev_device_write_room(struct serdev_device *); | int serdev_device_write_room(struct serdev_device *); | ||||||
| 
 | 
 | ||||||
|  | @ -223,7 +228,8 @@ static inline unsigned int serdev_device_set_baudrate(struct serdev_device *sdev | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
| static inline void serdev_device_set_flow_control(struct serdev_device *sdev, bool enable) {} | static inline void serdev_device_set_flow_control(struct serdev_device *sdev, bool enable) {} | ||||||
| static inline int serdev_device_write_buf(struct serdev_device *sdev, const unsigned char *buf, size_t count) | static inline int serdev_device_write(struct serdev_device *sdev, const unsigned char *buf, | ||||||
|  | 				      size_t count, unsigned long timeout) | ||||||
| { | { | ||||||
| 	return -ENODEV; | 	return -ENODEV; | ||||||
| } | } | ||||||
|  | @ -259,4 +265,11 @@ static inline struct device *serdev_tty_port_register(struct tty_port *port, | ||||||
| static inline void serdev_tty_port_unregister(struct tty_port *port) {} | static inline void serdev_tty_port_unregister(struct tty_port *port) {} | ||||||
| #endif /* CONFIG_SERIAL_DEV_CTRL_TTYPORT */ | #endif /* CONFIG_SERIAL_DEV_CTRL_TTYPORT */ | ||||||
| 
 | 
 | ||||||
|  | static inline int serdev_device_write_buf(struct serdev_device *serdev, | ||||||
|  | 					  const unsigned char *data, | ||||||
|  | 					  size_t count) | ||||||
|  | { | ||||||
|  | 	return serdev_device_write(serdev, data, count, 0); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| #endif /*_LINUX_SERDEV_H */ | #endif /*_LINUX_SERDEV_H */ | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Andrey Smirnov
						Andrey Smirnov