Merge tag 'regmap-fix-v5.11-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux/fpc-iii.git] / drivers / hid / hid-creative-sb0540.c
blobb4c8e7a5d3e0251803e674034b975c323d4af210
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * HID driver for the Creative SB0540 receiver
5 * Copyright (C) 2019 Red Hat Inc. All Rights Reserved
7 */
9 #include <linux/device.h>
10 #include <linux/hid.h>
11 #include <linux/module.h>
12 #include "hid-ids.h"
14 MODULE_AUTHOR("Bastien Nocera <hadess@hadess.net>");
15 MODULE_DESCRIPTION("HID Creative SB0540 receiver");
16 MODULE_LICENSE("GPL");
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
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
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
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)];
123 static inline u64 reverse(u64 data, int bits)
125 int i;
126 u64 c;
128 c = 0;
129 for (i = 0; i < bits; i++) {
130 c |= (u64) (((data & (((u64) 1) << i)) ? 1 : 0))
131 << (bits - 1 - i);
133 return (c);
136 static int get_key(struct creative_sb0540 *creative_sb0540, u64 keycode)
138 int i;
140 for (i = 0; i < ARRAY_SIZE(creative_sb0540_codes); i++) {
141 if (creative_sb0540_codes[i] == keycode)
142 return creative_sb0540->keymap[i];
145 return 0;
149 static int creative_sb0540_raw_event(struct hid_device *hid,
150 struct hid_report *report, u8 *data, int len)
152 struct creative_sb0540 *creative_sb0540 = hid_get_drvdata(hid);
153 u64 code, main_code;
154 int key;
156 if (len != 6)
157 return 0;
159 /* From daemons/hw_hiddev.c sb0540_rec() in lirc */
160 code = reverse(data[5], 8);
161 main_code = (code << 8) + ((~code) & 0xff);
164 * Flip to get values in the same format as
165 * remotes/creative/lircd.conf.alsa_usb in lirc
167 main_code = ((main_code & 0xff) << 8) +
168 ((main_code & 0xff00) >> 8);
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;
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);
181 /* let hidraw and hiddev handle the report */
182 return 0;
185 static int creative_sb0540_input_configured(struct hid_device *hid,
186 struct hid_input *hidinput)
188 struct input_dev *input_dev = hidinput->input;
189 struct creative_sb0540 *creative_sb0540 = hid_get_drvdata(hid);
190 int i;
192 creative_sb0540->input_dev = input_dev;
194 input_dev->keycode = creative_sb0540->keymap;
195 input_dev->keycodesize = sizeof(unsigned short);
196 input_dev->keycodemax = ARRAY_SIZE(creative_sb0540->keymap);
198 input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
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);
206 return 0;
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)
214 * We are remapping the keys ourselves, so ignore the hid-input
215 * keymap processing.
217 return -1;
220 static int creative_sb0540_probe(struct hid_device *hid,
221 const struct hid_device_id *id)
223 int ret;
224 struct creative_sb0540 *creative_sb0540;
226 creative_sb0540 = devm_kzalloc(&hid->dev,
227 sizeof(struct creative_sb0540), GFP_KERNEL);
229 if (!creative_sb0540)
230 return -ENOMEM;
232 creative_sb0540->hid = hid;
234 /* force input as some remotes bypass the input registration */
235 hid->quirks |= HID_QUIRK_HIDINPUT_FORCE;
237 hid_set_drvdata(hid, creative_sb0540);
239 ret = hid_parse(hid);
240 if (ret) {
241 hid_err(hid, "parse failed\n");
242 return ret;
245 ret = hid_hw_start(hid, HID_CONNECT_DEFAULT);
246 if (ret) {
247 hid_err(hid, "hw start failed\n");
248 return ret;
251 return ret;
254 static const struct hid_device_id creative_sb0540_devices[] = {
255 { HID_USB_DEVICE(USB_VENDOR_ID_CREATIVELABS, USB_DEVICE_ID_CREATIVE_SB0540) },
258 MODULE_DEVICE_TABLE(hid, creative_sb0540_devices);
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,
268 module_hid_driver(creative_sb0540_driver);