Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/hid/hid-cmedia.c
26278 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
/*
3
* HID driver for CMedia CM6533 audio jack controls
4
* and HS100B mute buttons
5
*
6
* Copyright (C) 2015 Ben Chen <[email protected]>
7
* Copyright (C) 2021 Thomas Weißschuh <[email protected]>
8
*/
9
10
#include <linux/device.h>
11
#include <linux/hid.h>
12
#include <linux/module.h>
13
#include "hid-ids.h"
14
15
MODULE_AUTHOR("Ben Chen");
16
MODULE_AUTHOR("Thomas Weißschuh");
17
MODULE_DESCRIPTION("CM6533 HID jack controls and HS100B mute button");
18
MODULE_LICENSE("GPL");
19
20
#define CM6533_JD_TYPE_COUNT 1
21
#define CM6533_JD_RAWEV_LEN 16
22
#define CM6533_JD_SFX_OFFSET 8
23
24
#define HS100B_RDESC_ORIG_SIZE 60
25
26
/* Fixed report descriptor of HS-100B audio chip
27
* Bit 4 is an abolute Microphone mute usage instead of being unassigned.
28
*/
29
static const __u8 hs100b_rdesc_fixed[] = {
30
0x05, 0x0C, /* Usage Page (Consumer), */
31
0x09, 0x01, /* Usage (Consumer Control), */
32
0xA1, 0x01, /* Collection (Application), */
33
0x15, 0x00, /* Logical Minimum (0), */
34
0x25, 0x01, /* Logical Maximum (1), */
35
0x09, 0xE9, /* Usage (Volume Inc), */
36
0x09, 0xEA, /* Usage (Volume Dec), */
37
0x75, 0x01, /* Report Size (1), */
38
0x95, 0x02, /* Report Count (2), */
39
0x81, 0x02, /* Input (Variable), */
40
0x09, 0xE2, /* Usage (Mute), */
41
0x95, 0x01, /* Report Count (1), */
42
0x81, 0x06, /* Input (Variable, Relative), */
43
0x05, 0x0B, /* Usage Page (Telephony), */
44
0x09, 0x2F, /* Usage (2Fh), */
45
0x81, 0x02, /* Input (Variable), */
46
0x09, 0x20, /* Usage (20h), */
47
0x81, 0x06, /* Input (Variable, Relative), */
48
0x05, 0x0C, /* Usage Page (Consumer), */
49
0x09, 0x00, /* Usage (00h), */
50
0x95, 0x03, /* Report Count (3), */
51
0x81, 0x02, /* Input (Variable), */
52
0x26, 0xFF, 0x00, /* Logical Maximum (255), */
53
0x09, 0x00, /* Usage (00h), */
54
0x75, 0x08, /* Report Size (8), */
55
0x95, 0x03, /* Report Count (3), */
56
0x81, 0x02, /* Input (Variable), */
57
0x09, 0x00, /* Usage (00h), */
58
0x95, 0x04, /* Report Count (4), */
59
0x91, 0x02, /* Output (Variable), */
60
0xC0 /* End Collection */
61
};
62
63
/*
64
*
65
*CM6533 audio jack HID raw events:
66
*
67
*Plug in:
68
*01000600 002083xx 080008c0 10000000
69
*about 3 seconds later...
70
*01000a00 002083xx 08000380 10000000
71
*01000600 002083xx 08000380 10000000
72
*
73
*Plug out:
74
*01000400 002083xx 080008c0 x0000000
75
*/
76
77
static const u8 ji_sfx[] = { 0x08, 0x00, 0x08, 0xc0 };
78
static const u8 ji_in[] = { 0x01, 0x00, 0x06, 0x00 };
79
static const u8 ji_out[] = { 0x01, 0x00, 0x04, 0x00 };
80
81
static int jack_switch_types[CM6533_JD_TYPE_COUNT] = {
82
SW_HEADPHONE_INSERT,
83
};
84
85
struct cmhid {
86
struct input_dev *input_dev;
87
struct hid_device *hid;
88
unsigned short switch_map[CM6533_JD_TYPE_COUNT];
89
};
90
91
static void hp_ev(struct hid_device *hid, struct cmhid *cm, int value)
92
{
93
input_report_switch(cm->input_dev, SW_HEADPHONE_INSERT, value);
94
input_sync(cm->input_dev);
95
}
96
97
static int cmhid_raw_event(struct hid_device *hid, struct hid_report *report,
98
u8 *data, int len)
99
{
100
struct cmhid *cm = hid_get_drvdata(hid);
101
102
if (len != CM6533_JD_RAWEV_LEN)
103
goto out;
104
if (memcmp(data+CM6533_JD_SFX_OFFSET, ji_sfx, sizeof(ji_sfx)))
105
goto out;
106
107
if (!memcmp(data, ji_out, sizeof(ji_out))) {
108
hp_ev(hid, cm, 0);
109
goto out;
110
}
111
if (!memcmp(data, ji_in, sizeof(ji_in))) {
112
hp_ev(hid, cm, 1);
113
goto out;
114
}
115
116
out:
117
return 0;
118
}
119
120
static int cmhid_input_configured(struct hid_device *hid,
121
struct hid_input *hidinput)
122
{
123
struct input_dev *input_dev = hidinput->input;
124
struct cmhid *cm = hid_get_drvdata(hid);
125
int i;
126
127
cm->input_dev = input_dev;
128
memcpy(cm->switch_map, jack_switch_types, sizeof(cm->switch_map));
129
input_dev->evbit[0] = BIT(EV_SW);
130
for (i = 0; i < CM6533_JD_TYPE_COUNT; i++)
131
input_set_capability(cm->input_dev,
132
EV_SW, jack_switch_types[i]);
133
return 0;
134
}
135
136
static int cmhid_input_mapping(struct hid_device *hid,
137
struct hid_input *hi, struct hid_field *field,
138
struct hid_usage *usage, unsigned long **bit, int *max)
139
{
140
return -1;
141
}
142
143
static int cmhid_probe(struct hid_device *hid, const struct hid_device_id *id)
144
{
145
int ret;
146
struct cmhid *cm;
147
148
cm = kzalloc(sizeof(struct cmhid), GFP_KERNEL);
149
if (!cm) {
150
ret = -ENOMEM;
151
goto allocfail;
152
}
153
154
cm->hid = hid;
155
156
hid->quirks |= HID_QUIRK_HIDINPUT_FORCE;
157
hid_set_drvdata(hid, cm);
158
159
ret = hid_parse(hid);
160
if (ret) {
161
hid_err(hid, "parse failed\n");
162
goto fail;
163
}
164
165
ret = hid_hw_start(hid, HID_CONNECT_DEFAULT | HID_CONNECT_HIDDEV_FORCE);
166
if (ret) {
167
hid_err(hid, "hw start failed\n");
168
goto fail;
169
}
170
171
return 0;
172
fail:
173
kfree(cm);
174
allocfail:
175
return ret;
176
}
177
178
static void cmhid_remove(struct hid_device *hid)
179
{
180
struct cmhid *cm = hid_get_drvdata(hid);
181
182
hid_hw_stop(hid);
183
kfree(cm);
184
}
185
186
static const struct hid_device_id cmhid_devices[] = {
187
{ HID_USB_DEVICE(USB_VENDOR_ID_CMEDIA, USB_DEVICE_ID_CM6533) },
188
{ }
189
};
190
MODULE_DEVICE_TABLE(hid, cmhid_devices);
191
192
static struct hid_driver cmhid_driver = {
193
.name = "cm6533_jd",
194
.id_table = cmhid_devices,
195
.raw_event = cmhid_raw_event,
196
.input_configured = cmhid_input_configured,
197
.probe = cmhid_probe,
198
.remove = cmhid_remove,
199
.input_mapping = cmhid_input_mapping,
200
};
201
202
static const __u8 *cmhid_hs100b_report_fixup(struct hid_device *hid, __u8 *rdesc,
203
unsigned int *rsize)
204
{
205
if (*rsize == HS100B_RDESC_ORIG_SIZE) {
206
hid_info(hid, "Fixing CMedia HS-100B report descriptor\n");
207
*rsize = sizeof(hs100b_rdesc_fixed);
208
return hs100b_rdesc_fixed;
209
}
210
return rdesc;
211
}
212
213
static const struct hid_device_id cmhid_hs100b_devices[] = {
214
{ HID_USB_DEVICE(USB_VENDOR_ID_CMEDIA, USB_DEVICE_ID_CMEDIA_HS100B) },
215
{ }
216
};
217
MODULE_DEVICE_TABLE(hid, cmhid_hs100b_devices);
218
219
static struct hid_driver cmhid_hs100b_driver = {
220
.name = "cmedia_hs100b",
221
.id_table = cmhid_hs100b_devices,
222
.report_fixup = cmhid_hs100b_report_fixup,
223
};
224
225
static int cmedia_init(void)
226
{
227
int ret;
228
229
ret = hid_register_driver(&cmhid_driver);
230
if (ret)
231
return ret;
232
233
ret = hid_register_driver(&cmhid_hs100b_driver);
234
if (ret)
235
hid_unregister_driver(&cmhid_driver);
236
237
return ret;
238
}
239
module_init(cmedia_init);
240
241
static void cmedia_exit(void)
242
{
243
hid_unregister_driver(&cmhid_driver);
244
hid_unregister_driver(&cmhid_hs100b_driver);
245
}
246
module_exit(cmedia_exit);
247
248