Commit | Line | Data |
---|---|---|
5772f636 SA |
1 | /* |
2 | * Roccat common functions for device specific drivers | |
3 | * | |
4 | * Copyright (c) 2011 Stefan Achatz <erazor_de@users.sourceforge.net> | |
5 | */ | |
6 | ||
7 | /* | |
8 | * This program is free software; you can redistribute it and/or modify it | |
9 | * under the terms of the GNU General Public License as published by the Free | |
10 | * Software Foundation; either version 2 of the License, or (at your option) | |
11 | * any later version. | |
12 | */ | |
13 | ||
1edd5b42 | 14 | #include <linux/hid.h> |
5772f636 | 15 | #include <linux/slab.h> |
8f86a2c3 | 16 | #include <linux/module.h> |
5772f636 SA |
17 | #include "hid-roccat-common.h" |
18 | ||
7392d73b | 19 | static inline uint16_t roccat_common2_feature_report(uint8_t report_id) |
1edd5b42 SA |
20 | { |
21 | return 0x300 | report_id; | |
22 | } | |
23 | ||
7392d73b | 24 | int roccat_common2_receive(struct usb_device *usb_dev, uint report_id, |
5772f636 SA |
25 | void *data, uint size) |
26 | { | |
27 | char *buf; | |
28 | int len; | |
29 | ||
30 | buf = kmalloc(size, GFP_KERNEL); | |
31 | if (buf == NULL) | |
32 | return -ENOMEM; | |
33 | ||
34 | len = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), | |
1edd5b42 | 35 | HID_REQ_GET_REPORT, |
5772f636 | 36 | USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, |
7392d73b | 37 | roccat_common2_feature_report(report_id), |
1edd5b42 | 38 | 0, buf, size, USB_CTRL_SET_TIMEOUT); |
5772f636 SA |
39 | |
40 | memcpy(data, buf, size); | |
41 | kfree(buf); | |
42 | return ((len < 0) ? len : ((len != size) ? -EIO : 0)); | |
43 | } | |
7392d73b | 44 | EXPORT_SYMBOL_GPL(roccat_common2_receive); |
5772f636 | 45 | |
7392d73b | 46 | int roccat_common2_send(struct usb_device *usb_dev, uint report_id, |
5772f636 SA |
47 | void const *data, uint size) |
48 | { | |
49 | char *buf; | |
50 | int len; | |
51 | ||
4c33a885 | 52 | buf = kmemdup(data, size, GFP_KERNEL); |
5772f636 SA |
53 | if (buf == NULL) |
54 | return -ENOMEM; | |
55 | ||
5772f636 | 56 | len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0), |
1edd5b42 | 57 | HID_REQ_SET_REPORT, |
5772f636 | 58 | USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT, |
7392d73b | 59 | roccat_common2_feature_report(report_id), |
1edd5b42 | 60 | 0, buf, size, USB_CTRL_SET_TIMEOUT); |
5772f636 SA |
61 | |
62 | kfree(buf); | |
63 | return ((len < 0) ? len : ((len != size) ? -EIO : 0)); | |
64 | } | |
7392d73b | 65 | EXPORT_SYMBOL_GPL(roccat_common2_send); |
5772f636 | 66 | |
7392d73b | 67 | enum roccat_common2_control_states { |
14fc4290 | 68 | ROCCAT_COMMON_CONTROL_STATUS_CRITICAL = 0, |
4728f2dc SA |
69 | ROCCAT_COMMON_CONTROL_STATUS_OK = 1, |
70 | ROCCAT_COMMON_CONTROL_STATUS_INVALID = 2, | |
14fc4290 SA |
71 | ROCCAT_COMMON_CONTROL_STATUS_BUSY = 3, |
72 | ROCCAT_COMMON_CONTROL_STATUS_CRITICAL_NEW = 4, | |
4728f2dc SA |
73 | }; |
74 | ||
7392d73b | 75 | static int roccat_common2_receive_control_status(struct usb_device *usb_dev) |
4728f2dc SA |
76 | { |
77 | int retval; | |
7392d73b | 78 | struct roccat_common2_control control; |
4728f2dc SA |
79 | |
80 | do { | |
81 | msleep(50); | |
7392d73b | 82 | retval = roccat_common2_receive(usb_dev, |
4728f2dc | 83 | ROCCAT_COMMON_COMMAND_CONTROL, |
7392d73b | 84 | &control, sizeof(struct roccat_common2_control)); |
4728f2dc SA |
85 | |
86 | if (retval) | |
87 | return retval; | |
88 | ||
89 | switch (control.value) { | |
90 | case ROCCAT_COMMON_CONTROL_STATUS_OK: | |
91 | return 0; | |
14fc4290 | 92 | case ROCCAT_COMMON_CONTROL_STATUS_BUSY: |
4728f2dc SA |
93 | msleep(500); |
94 | continue; | |
95 | case ROCCAT_COMMON_CONTROL_STATUS_INVALID: | |
14fc4290 SA |
96 | case ROCCAT_COMMON_CONTROL_STATUS_CRITICAL: |
97 | case ROCCAT_COMMON_CONTROL_STATUS_CRITICAL_NEW: | |
4728f2dc SA |
98 | return -EINVAL; |
99 | default: | |
100 | dev_err(&usb_dev->dev, | |
7392d73b | 101 | "roccat_common2_receive_control_status: " |
4728f2dc SA |
102 | "unknown response value 0x%x\n", |
103 | control.value); | |
104 | return -EINVAL; | |
105 | } | |
106 | ||
107 | } while (1); | |
108 | } | |
109 | ||
7392d73b | 110 | int roccat_common2_send_with_status(struct usb_device *usb_dev, |
4728f2dc SA |
111 | uint command, void const *buf, uint size) |
112 | { | |
113 | int retval; | |
114 | ||
7392d73b | 115 | retval = roccat_common2_send(usb_dev, command, buf, size); |
4728f2dc SA |
116 | if (retval) |
117 | return retval; | |
118 | ||
119 | msleep(100); | |
120 | ||
7392d73b | 121 | return roccat_common2_receive_control_status(usb_dev); |
4728f2dc | 122 | } |
7392d73b | 123 | EXPORT_SYMBOL_GPL(roccat_common2_send_with_status); |
4728f2dc | 124 | |
71304f5a SA |
125 | int roccat_common2_device_init_struct(struct usb_device *usb_dev, |
126 | struct roccat_common2_device *dev) | |
127 | { | |
128 | mutex_init(&dev->lock); | |
129 | return 0; | |
130 | } | |
131 | EXPORT_SYMBOL_GPL(roccat_common2_device_init_struct); | |
132 | ||
133 | ssize_t roccat_common2_sysfs_read(struct file *fp, struct kobject *kobj, | |
134 | char *buf, loff_t off, size_t count, | |
135 | size_t real_size, uint command) | |
136 | { | |
137 | struct device *dev = | |
138 | container_of(kobj, struct device, kobj)->parent->parent; | |
139 | struct roccat_common2_device *roccat_dev = hid_get_drvdata(dev_get_drvdata(dev)); | |
140 | struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); | |
141 | int retval; | |
142 | ||
143 | if (off >= real_size) | |
144 | return 0; | |
145 | ||
146 | if (off != 0 || count != real_size) | |
147 | return -EINVAL; | |
148 | ||
149 | mutex_lock(&roccat_dev->lock); | |
150 | retval = roccat_common2_receive(usb_dev, command, buf, real_size); | |
151 | mutex_unlock(&roccat_dev->lock); | |
152 | ||
153 | return retval ? retval : real_size; | |
154 | } | |
155 | EXPORT_SYMBOL_GPL(roccat_common2_sysfs_read); | |
156 | ||
157 | ssize_t roccat_common2_sysfs_write(struct file *fp, struct kobject *kobj, | |
158 | void const *buf, loff_t off, size_t count, | |
159 | size_t real_size, uint command) | |
160 | { | |
161 | struct device *dev = | |
162 | container_of(kobj, struct device, kobj)->parent->parent; | |
163 | struct roccat_common2_device *roccat_dev = hid_get_drvdata(dev_get_drvdata(dev)); | |
164 | struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); | |
165 | int retval; | |
166 | ||
167 | if (off != 0 || count != real_size) | |
168 | return -EINVAL; | |
169 | ||
170 | mutex_lock(&roccat_dev->lock); | |
171 | retval = roccat_common2_send_with_status(usb_dev, command, buf, real_size); | |
172 | mutex_unlock(&roccat_dev->lock); | |
173 | ||
174 | return retval ? retval : real_size; | |
175 | } | |
176 | EXPORT_SYMBOL_GPL(roccat_common2_sysfs_write); | |
177 | ||
5772f636 SA |
178 | MODULE_AUTHOR("Stefan Achatz"); |
179 | MODULE_DESCRIPTION("USB Roccat common driver"); | |
180 | MODULE_LICENSE("GPL v2"); |