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