| 
									
										
										
										
											2011-01-30 13:38:23 +01:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Roccat common functions for device specific drivers | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Copyright (c) 2011 Stefan Achatz <erazor_de@users.sourceforge.net> | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * This program is free software; you can redistribute it and/or modify it | 
					
						
							|  |  |  |  * under the terms of the GNU General Public License as published by the Free | 
					
						
							|  |  |  |  * Software Foundation; either version 2 of the License, or (at your option) | 
					
						
							|  |  |  |  * any later version. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-06-01 15:54:17 +02:00
										 |  |  | #include <linux/hid.h>
 | 
					
						
							| 
									
										
										
										
											2011-01-30 13:38:23 +01:00
										 |  |  | #include <linux/slab.h>
 | 
					
						
							| 
									
										
										
										
											2011-07-03 13:39:48 -04:00
										 |  |  | #include <linux/module.h>
 | 
					
						
							| 
									
										
										
										
											2011-01-30 13:38:23 +01:00
										 |  |  | #include "hid-roccat-common.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-20 22:45:04 +02:00
										 |  |  | static inline uint16_t roccat_common2_feature_report(uint8_t report_id) | 
					
						
							| 
									
										
										
										
											2011-06-01 15:54:17 +02:00
										 |  |  | { | 
					
						
							|  |  |  | 	return 0x300 | report_id; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-20 22:45:04 +02:00
										 |  |  | int roccat_common2_receive(struct usb_device *usb_dev, uint report_id, | 
					
						
							| 
									
										
										
										
											2011-01-30 13:38:23 +01:00
										 |  |  | 		void *data, uint size) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	char *buf; | 
					
						
							|  |  |  | 	int len; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	buf = kmalloc(size, GFP_KERNEL); | 
					
						
							|  |  |  | 	if (buf == NULL) | 
					
						
							|  |  |  | 		return -ENOMEM; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	len = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), | 
					
						
							| 
									
										
										
										
											2011-06-01 15:54:17 +02:00
										 |  |  | 			HID_REQ_GET_REPORT, | 
					
						
							| 
									
										
										
										
											2011-01-30 13:38:23 +01:00
										 |  |  | 			USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, | 
					
						
							| 
									
										
										
										
											2012-05-20 22:45:04 +02:00
										 |  |  | 			roccat_common2_feature_report(report_id), | 
					
						
							| 
									
										
										
										
											2011-06-01 15:54:17 +02:00
										 |  |  | 			0, buf, size, USB_CTRL_SET_TIMEOUT); | 
					
						
							| 
									
										
										
										
											2011-01-30 13:38:23 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	memcpy(data, buf, size); | 
					
						
							|  |  |  | 	kfree(buf); | 
					
						
							|  |  |  | 	return ((len < 0) ? len : ((len != size) ? -EIO : 0)); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2012-05-20 22:45:04 +02:00
										 |  |  | EXPORT_SYMBOL_GPL(roccat_common2_receive); | 
					
						
							| 
									
										
										
										
											2011-01-30 13:38:23 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-20 22:45:04 +02:00
										 |  |  | int roccat_common2_send(struct usb_device *usb_dev, uint report_id, | 
					
						
							| 
									
										
										
										
											2011-01-30 13:38:23 +01:00
										 |  |  | 		void const *data, uint size) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	char *buf; | 
					
						
							|  |  |  | 	int len; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-11-17 23:43:40 +01:00
										 |  |  | 	buf = kmemdup(data, size, GFP_KERNEL); | 
					
						
							| 
									
										
										
										
											2011-01-30 13:38:23 +01:00
										 |  |  | 	if (buf == NULL) | 
					
						
							|  |  |  | 		return -ENOMEM; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0), | 
					
						
							| 
									
										
										
										
											2011-06-01 15:54:17 +02:00
										 |  |  | 			HID_REQ_SET_REPORT, | 
					
						
							| 
									
										
										
										
											2011-01-30 13:38:23 +01:00
										 |  |  | 			USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT, | 
					
						
							| 
									
										
										
										
											2012-05-20 22:45:04 +02:00
										 |  |  | 			roccat_common2_feature_report(report_id), | 
					
						
							| 
									
										
										
										
											2011-06-01 15:54:17 +02:00
										 |  |  | 			0, buf, size, USB_CTRL_SET_TIMEOUT); | 
					
						
							| 
									
										
										
										
											2011-01-30 13:38:23 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	kfree(buf); | 
					
						
							|  |  |  | 	return ((len < 0) ? len : ((len != size) ? -EIO : 0)); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2012-05-20 22:45:04 +02:00
										 |  |  | EXPORT_SYMBOL_GPL(roccat_common2_send); | 
					
						
							| 
									
										
										
										
											2011-01-30 13:38:23 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-20 22:45:04 +02:00
										 |  |  | enum roccat_common2_control_states { | 
					
						
							| 
									
										
										
										
											2013-10-28 18:52:03 +01:00
										 |  |  | 	ROCCAT_COMMON_CONTROL_STATUS_CRITICAL = 0, | 
					
						
							| 
									
										
										
										
											2012-05-20 22:44:59 +02:00
										 |  |  | 	ROCCAT_COMMON_CONTROL_STATUS_OK = 1, | 
					
						
							|  |  |  | 	ROCCAT_COMMON_CONTROL_STATUS_INVALID = 2, | 
					
						
							| 
									
										
										
										
											2013-10-28 18:52:03 +01:00
										 |  |  | 	ROCCAT_COMMON_CONTROL_STATUS_BUSY = 3, | 
					
						
							|  |  |  | 	ROCCAT_COMMON_CONTROL_STATUS_CRITICAL_NEW = 4, | 
					
						
							| 
									
										
										
										
											2012-05-20 22:44:59 +02:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-20 22:45:04 +02:00
										 |  |  | static int roccat_common2_receive_control_status(struct usb_device *usb_dev) | 
					
						
							| 
									
										
										
										
											2012-05-20 22:44:59 +02:00
										 |  |  | { | 
					
						
							|  |  |  | 	int retval; | 
					
						
							| 
									
										
										
										
											2012-05-20 22:45:04 +02:00
										 |  |  | 	struct roccat_common2_control control; | 
					
						
							| 
									
										
										
										
											2012-05-20 22:44:59 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	do { | 
					
						
							|  |  |  | 		msleep(50); | 
					
						
							| 
									
										
										
										
											2012-05-20 22:45:04 +02:00
										 |  |  | 		retval = roccat_common2_receive(usb_dev, | 
					
						
							| 
									
										
										
										
											2012-05-20 22:44:59 +02:00
										 |  |  | 				ROCCAT_COMMON_COMMAND_CONTROL, | 
					
						
							| 
									
										
										
										
											2012-05-20 22:45:04 +02:00
										 |  |  | 				&control, sizeof(struct roccat_common2_control)); | 
					
						
							| 
									
										
										
										
											2012-05-20 22:44:59 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		if (retval) | 
					
						
							|  |  |  | 			return retval; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		switch (control.value) { | 
					
						
							|  |  |  | 		case ROCCAT_COMMON_CONTROL_STATUS_OK: | 
					
						
							|  |  |  | 			return 0; | 
					
						
							| 
									
										
										
										
											2013-10-28 18:52:03 +01:00
										 |  |  | 		case ROCCAT_COMMON_CONTROL_STATUS_BUSY: | 
					
						
							| 
									
										
										
										
											2012-05-20 22:44:59 +02:00
										 |  |  | 			msleep(500); | 
					
						
							|  |  |  | 			continue; | 
					
						
							|  |  |  | 		case ROCCAT_COMMON_CONTROL_STATUS_INVALID: | 
					
						
							| 
									
										
										
										
											2013-10-28 18:52:03 +01:00
										 |  |  | 		case ROCCAT_COMMON_CONTROL_STATUS_CRITICAL: | 
					
						
							|  |  |  | 		case ROCCAT_COMMON_CONTROL_STATUS_CRITICAL_NEW: | 
					
						
							| 
									
										
										
										
											2012-05-20 22:44:59 +02:00
										 |  |  | 			return -EINVAL; | 
					
						
							|  |  |  | 		default: | 
					
						
							|  |  |  | 			dev_err(&usb_dev->dev, | 
					
						
							| 
									
										
										
										
											2012-05-20 22:45:04 +02:00
										 |  |  | 					"roccat_common2_receive_control_status: " | 
					
						
							| 
									
										
										
										
											2012-05-20 22:44:59 +02:00
										 |  |  | 					"unknown response value 0x%x\n", | 
					
						
							|  |  |  | 					control.value); | 
					
						
							|  |  |  | 			return -EINVAL; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	} while (1); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-20 22:45:04 +02:00
										 |  |  | int roccat_common2_send_with_status(struct usb_device *usb_dev, | 
					
						
							| 
									
										
										
										
											2012-05-20 22:44:59 +02:00
										 |  |  | 		uint command, void const *buf, uint size) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int retval; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-20 22:45:04 +02:00
										 |  |  | 	retval = roccat_common2_send(usb_dev, command, buf, size); | 
					
						
							| 
									
										
										
										
											2012-05-20 22:44:59 +02:00
										 |  |  | 	if (retval) | 
					
						
							|  |  |  | 		return retval; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	msleep(100); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-20 22:45:04 +02:00
										 |  |  | 	return roccat_common2_receive_control_status(usb_dev); | 
					
						
							| 
									
										
										
										
											2012-05-20 22:44:59 +02:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2012-05-20 22:45:04 +02:00
										 |  |  | EXPORT_SYMBOL_GPL(roccat_common2_send_with_status); | 
					
						
							| 
									
										
										
										
											2012-05-20 22:44:59 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-28 18:52:05 +01:00
										 |  |  | int roccat_common2_device_init_struct(struct usb_device *usb_dev, | 
					
						
							|  |  |  | 		struct roccat_common2_device *dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	mutex_init(&dev->lock); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL_GPL(roccat_common2_device_init_struct); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ssize_t roccat_common2_sysfs_read(struct file *fp, struct kobject *kobj, | 
					
						
							|  |  |  | 		char *buf, loff_t off, size_t count, | 
					
						
							|  |  |  | 		size_t real_size, uint command) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct device *dev = | 
					
						
							|  |  |  | 			container_of(kobj, struct device, kobj)->parent->parent; | 
					
						
							|  |  |  | 	struct roccat_common2_device *roccat_dev = hid_get_drvdata(dev_get_drvdata(dev)); | 
					
						
							|  |  |  | 	struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); | 
					
						
							|  |  |  | 	int retval; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (off >= real_size) | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (off != 0 || count != real_size) | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	mutex_lock(&roccat_dev->lock); | 
					
						
							|  |  |  | 	retval = roccat_common2_receive(usb_dev, command, buf, real_size); | 
					
						
							|  |  |  | 	mutex_unlock(&roccat_dev->lock); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return retval ? retval : real_size; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL_GPL(roccat_common2_sysfs_read); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ssize_t roccat_common2_sysfs_write(struct file *fp, struct kobject *kobj, | 
					
						
							|  |  |  | 		void const *buf, loff_t off, size_t count, | 
					
						
							|  |  |  | 		size_t real_size, uint command) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct device *dev = | 
					
						
							|  |  |  | 			container_of(kobj, struct device, kobj)->parent->parent; | 
					
						
							|  |  |  | 	struct roccat_common2_device *roccat_dev = hid_get_drvdata(dev_get_drvdata(dev)); | 
					
						
							|  |  |  | 	struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); | 
					
						
							|  |  |  | 	int retval; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (off != 0 || count != real_size) | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	mutex_lock(&roccat_dev->lock); | 
					
						
							|  |  |  | 	retval = roccat_common2_send_with_status(usb_dev, command, buf, real_size); | 
					
						
							|  |  |  | 	mutex_unlock(&roccat_dev->lock); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return retval ? retval : real_size; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL_GPL(roccat_common2_sysfs_write); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-01-30 13:38:23 +01:00
										 |  |  | MODULE_AUTHOR("Stefan Achatz"); | 
					
						
							|  |  |  | MODULE_DESCRIPTION("USB Roccat common driver"); | 
					
						
							|  |  |  | MODULE_LICENSE("GPL v2"); |