xref: /linux/drivers/hid/hid-creative-sb0540.c (revision 58d416351e6df1a41d415958ccdd8eb9c2173fed)
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 <hadess@hadess.net>");
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