Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/hid/hid-creative-sb0540.c
26278 views
1
// SPDX-License-Identifier: GPL-2.0
2
/*
3
* HID driver for the Creative SB0540 receiver
4
*
5
* Copyright (C) 2019 Red Hat Inc. All Rights Reserved
6
*
7
*/
8
9
#include <linux/device.h>
10
#include <linux/hid.h>
11
#include <linux/module.h>
12
#include "hid-ids.h"
13
14
MODULE_AUTHOR("Bastien Nocera <[email protected]>");
15
MODULE_DESCRIPTION("HID Creative SB0540 receiver");
16
MODULE_LICENSE("GPL");
17
18
static const unsigned short creative_sb0540_key_table[] = {
19
KEY_POWER,
20
KEY_RESERVED, /* text: 24bit */
21
KEY_RESERVED, /* 24bit wheel up */
22
KEY_RESERVED, /* 24bit wheel down */
23
KEY_RESERVED, /* text: CMSS */
24
KEY_RESERVED, /* CMSS wheel Up */
25
KEY_RESERVED, /* CMSS wheel Down */
26
KEY_RESERVED, /* text: EAX */
27
KEY_RESERVED, /* EAX wheel up */
28
KEY_RESERVED, /* EAX wheel down */
29
KEY_RESERVED, /* text: 3D Midi */
30
KEY_RESERVED, /* 3D Midi wheel up */
31
KEY_RESERVED, /* 3D Midi wheel down */
32
KEY_MUTE,
33
KEY_VOLUMEUP,
34
KEY_VOLUMEDOWN,
35
KEY_UP,
36
KEY_LEFT,
37
KEY_RIGHT,
38
KEY_REWIND,
39
KEY_OK,
40
KEY_FASTFORWARD,
41
KEY_DOWN,
42
KEY_AGAIN, /* text: Return, symbol: Jump to */
43
KEY_PLAY, /* text: Start */
44
KEY_ESC, /* text: Cancel */
45
KEY_RECORD,
46
KEY_OPTION,
47
KEY_MENU, /* text: Display */
48
KEY_PREVIOUS,
49
KEY_PLAYPAUSE,
50
KEY_NEXT,
51
KEY_SLOW,
52
KEY_STOP,
53
KEY_NUMERIC_1,
54
KEY_NUMERIC_2,
55
KEY_NUMERIC_3,
56
KEY_NUMERIC_4,
57
KEY_NUMERIC_5,
58
KEY_NUMERIC_6,
59
KEY_NUMERIC_7,
60
KEY_NUMERIC_8,
61
KEY_NUMERIC_9,
62
KEY_NUMERIC_0
63
};
64
65
/*
66
* Codes and keys from lirc's
67
* remotes/creative/lircd.conf.alsa_usb
68
* order and size must match creative_sb0540_key_table[] above
69
*/
70
static const unsigned short creative_sb0540_codes[] = {
71
0x619E,
72
0x916E,
73
0x926D,
74
0x936C,
75
0x718E,
76
0x946B,
77
0x956A,
78
0x8C73,
79
0x9669,
80
0x9768,
81
0x9867,
82
0x9966,
83
0x9A65,
84
0x6E91,
85
0x629D,
86
0x639C,
87
0x7B84,
88
0x6B94,
89
0x728D,
90
0x8778,
91
0x817E,
92
0x758A,
93
0x8D72,
94
0x8E71,
95
0x8877,
96
0x7C83,
97
0x738C,
98
0x827D,
99
0x7689,
100
0x7F80,
101
0x7986,
102
0x7A85,
103
0x7D82,
104
0x857A,
105
0x8B74,
106
0x8F70,
107
0x906F,
108
0x8A75,
109
0x847B,
110
0x7887,
111
0x8976,
112
0x837C,
113
0x7788,
114
0x807F
115
};
116
117
struct creative_sb0540 {
118
struct input_dev *input_dev;
119
struct hid_device *hid;
120
unsigned short keymap[ARRAY_SIZE(creative_sb0540_key_table)];
121
};
122
123
static inline u64 reverse(u64 data, int bits)
124
{
125
int i;
126
u64 c;
127
128
c = 0;
129
for (i = 0; i < bits; i++) {
130
c |= (u64) (((data & (((u64) 1) << i)) ? 1 : 0))
131
<< (bits - 1 - i);
132
}
133
return (c);
134
}
135
136
static int get_key(struct creative_sb0540 *creative_sb0540, u64 keycode)
137
{
138
int i;
139
140
for (i = 0; i < ARRAY_SIZE(creative_sb0540_codes); i++) {
141
if (creative_sb0540_codes[i] == keycode)
142
return creative_sb0540->keymap[i];
143
}
144
145
return 0;
146
147
}
148
149
static int creative_sb0540_raw_event(struct hid_device *hid,
150
struct hid_report *report, u8 *data, int len)
151
{
152
struct creative_sb0540 *creative_sb0540 = hid_get_drvdata(hid);
153
u64 code, main_code;
154
int key;
155
156
if (len != 6)
157
return 0;
158
159
/* From daemons/hw_hiddev.c sb0540_rec() in lirc */
160
code = reverse(data[5], 8);
161
main_code = (code << 8) + ((~code) & 0xff);
162
163
/*
164
* Flip to get values in the same format as
165
* remotes/creative/lircd.conf.alsa_usb in lirc
166
*/
167
main_code = ((main_code & 0xff) << 8) +
168
((main_code & 0xff00) >> 8);
169
170
key = get_key(creative_sb0540, main_code);
171
if (key == 0 || key == KEY_RESERVED) {
172
hid_err(hid, "Could not get a key for main_code %llX\n",
173
main_code);
174
return 0;
175
}
176
177
input_report_key(creative_sb0540->input_dev, key, 1);
178
input_report_key(creative_sb0540->input_dev, key, 0);
179
input_sync(creative_sb0540->input_dev);
180
181
/* let hidraw and hiddev handle the report */
182
return 0;
183
}
184
185
static int creative_sb0540_input_configured(struct hid_device *hid,
186
struct hid_input *hidinput)
187
{
188
struct input_dev *input_dev = hidinput->input;
189
struct creative_sb0540 *creative_sb0540 = hid_get_drvdata(hid);
190
int i;
191
192
creative_sb0540->input_dev = input_dev;
193
194
input_dev->keycode = creative_sb0540->keymap;
195
input_dev->keycodesize = sizeof(unsigned short);
196
input_dev->keycodemax = ARRAY_SIZE(creative_sb0540->keymap);
197
198
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
199
200
memcpy(creative_sb0540->keymap, creative_sb0540_key_table,
201
sizeof(creative_sb0540->keymap));
202
for (i = 0; i < ARRAY_SIZE(creative_sb0540_key_table); i++)
203
set_bit(creative_sb0540->keymap[i], input_dev->keybit);
204
clear_bit(KEY_RESERVED, input_dev->keybit);
205
206
return 0;
207
}
208
209
static int creative_sb0540_input_mapping(struct hid_device *hid,
210
struct hid_input *hi, struct hid_field *field,
211
struct hid_usage *usage, unsigned long **bit, int *max)
212
{
213
/*
214
* We are remapping the keys ourselves, so ignore the hid-input
215
* keymap processing.
216
*/
217
return -1;
218
}
219
220
static int creative_sb0540_probe(struct hid_device *hid,
221
const struct hid_device_id *id)
222
{
223
int ret;
224
struct creative_sb0540 *creative_sb0540;
225
226
creative_sb0540 = devm_kzalloc(&hid->dev,
227
sizeof(struct creative_sb0540), GFP_KERNEL);
228
229
if (!creative_sb0540)
230
return -ENOMEM;
231
232
creative_sb0540->hid = hid;
233
234
/* force input as some remotes bypass the input registration */
235
hid->quirks |= HID_QUIRK_HIDINPUT_FORCE;
236
237
hid_set_drvdata(hid, creative_sb0540);
238
239
ret = hid_parse(hid);
240
if (ret) {
241
hid_err(hid, "parse failed\n");
242
return ret;
243
}
244
245
ret = hid_hw_start(hid, HID_CONNECT_DEFAULT);
246
if (ret) {
247
hid_err(hid, "hw start failed\n");
248
return ret;
249
}
250
251
return ret;
252
}
253
254
static const struct hid_device_id creative_sb0540_devices[] = {
255
{ HID_USB_DEVICE(USB_VENDOR_ID_CREATIVELABS, USB_DEVICE_ID_CREATIVE_SB0540) },
256
{ }
257
};
258
MODULE_DEVICE_TABLE(hid, creative_sb0540_devices);
259
260
static struct hid_driver creative_sb0540_driver = {
261
.name = "creative-sb0540",
262
.id_table = creative_sb0540_devices,
263
.raw_event = creative_sb0540_raw_event,
264
.input_configured = creative_sb0540_input_configured,
265
.probe = creative_sb0540_probe,
266
.input_mapping = creative_sb0540_input_mapping,
267
};
268
module_hid_driver(creative_sb0540_driver);
269
270