1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Roccat common functions for device specific drivers |
4 | * |
5 | * Copyright (c) 2011 Stefan Achatz <erazor_de@users.sourceforge.net> |
6 | */ |
7 | |
8 | /* |
9 | */ |
10 | |
11 | #include <linux/hid.h> |
12 | #include <linux/slab.h> |
13 | #include <linux/module.h> |
14 | #include "hid-roccat-common.h" |
15 | |
16 | static inline uint16_t roccat_common2_feature_report(uint8_t report_id) |
17 | { |
18 | return 0x300 | report_id; |
19 | } |
20 | |
21 | int roccat_common2_receive(struct usb_device *usb_dev, uint report_id, |
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(dev: usb_dev, usb_rcvctrlpipe(usb_dev, 0), |
32 | request: HID_REQ_GET_REPORT, |
33 | USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, |
34 | value: roccat_common2_feature_report(report_id), |
35 | index: 0, data: buf, size, USB_CTRL_SET_TIMEOUT); |
36 | |
37 | memcpy(data, buf, size); |
38 | kfree(objp: buf); |
39 | return ((len < 0) ? len : ((len != size) ? -EIO : 0)); |
40 | } |
41 | EXPORT_SYMBOL_GPL(roccat_common2_receive); |
42 | |
43 | int roccat_common2_send(struct usb_device *usb_dev, uint report_id, |
44 | void const *data, uint size) |
45 | { |
46 | char *buf; |
47 | int len; |
48 | |
49 | buf = kmemdup(p: data, size, GFP_KERNEL); |
50 | if (buf == NULL) |
51 | return -ENOMEM; |
52 | |
53 | len = usb_control_msg(dev: usb_dev, usb_sndctrlpipe(usb_dev, 0), |
54 | request: HID_REQ_SET_REPORT, |
55 | USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT, |
56 | value: roccat_common2_feature_report(report_id), |
57 | index: 0, data: buf, size, USB_CTRL_SET_TIMEOUT); |
58 | |
59 | kfree(objp: buf); |
60 | return ((len < 0) ? len : ((len != size) ? -EIO : 0)); |
61 | } |
62 | EXPORT_SYMBOL_GPL(roccat_common2_send); |
63 | |
64 | enum roccat_common2_control_states { |
65 | ROCCAT_COMMON_CONTROL_STATUS_CRITICAL = 0, |
66 | ROCCAT_COMMON_CONTROL_STATUS_OK = 1, |
67 | ROCCAT_COMMON_CONTROL_STATUS_INVALID = 2, |
68 | ROCCAT_COMMON_CONTROL_STATUS_BUSY = 3, |
69 | ROCCAT_COMMON_CONTROL_STATUS_CRITICAL_NEW = 4, |
70 | }; |
71 | |
72 | static int roccat_common2_receive_control_status(struct usb_device *usb_dev) |
73 | { |
74 | int retval; |
75 | struct roccat_common2_control control; |
76 | |
77 | do { |
78 | msleep(msecs: 50); |
79 | retval = roccat_common2_receive(usb_dev, |
80 | ROCCAT_COMMON_COMMAND_CONTROL, |
81 | &control, sizeof(struct roccat_common2_control)); |
82 | |
83 | if (retval) |
84 | return retval; |
85 | |
86 | switch (control.value) { |
87 | case ROCCAT_COMMON_CONTROL_STATUS_OK: |
88 | return 0; |
89 | case ROCCAT_COMMON_CONTROL_STATUS_BUSY: |
90 | msleep(msecs: 500); |
91 | continue; |
92 | case ROCCAT_COMMON_CONTROL_STATUS_INVALID: |
93 | case ROCCAT_COMMON_CONTROL_STATUS_CRITICAL: |
94 | case ROCCAT_COMMON_CONTROL_STATUS_CRITICAL_NEW: |
95 | return -EINVAL; |
96 | default: |
97 | dev_err(&usb_dev->dev, |
98 | "roccat_common2_receive_control_status: " |
99 | "unknown response value 0x%x\n" , |
100 | control.value); |
101 | return -EINVAL; |
102 | } |
103 | |
104 | } while (1); |
105 | } |
106 | |
107 | int roccat_common2_send_with_status(struct usb_device *usb_dev, |
108 | uint command, void const *buf, uint size) |
109 | { |
110 | int retval; |
111 | |
112 | retval = roccat_common2_send(usb_dev, command, buf, size); |
113 | if (retval) |
114 | return retval; |
115 | |
116 | msleep(msecs: 100); |
117 | |
118 | return roccat_common2_receive_control_status(usb_dev); |
119 | } |
120 | EXPORT_SYMBOL_GPL(roccat_common2_send_with_status); |
121 | |
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 | { |
134 | struct device *dev = kobj_to_dev(kobj)->parent->parent; |
135 | struct roccat_common2_device *roccat_dev = hid_get_drvdata(hdev: 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(lock: &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 | { |
157 | struct device *dev = kobj_to_dev(kobj)->parent->parent; |
158 | struct roccat_common2_device *roccat_dev = hid_get_drvdata(hdev: 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(lock: &roccat_dev->lock); |
168 | |
169 | return retval ? retval : real_size; |
170 | } |
171 | EXPORT_SYMBOL_GPL(roccat_common2_sysfs_write); |
172 | |
173 | MODULE_AUTHOR("Stefan Achatz" ); |
174 | MODULE_DESCRIPTION("USB Roccat common driver" ); |
175 | MODULE_LICENSE("GPL v2" ); |
176 | |