1 // SPDX-License-Identifier: GPL-2.0
3 * HID driver for the Creative SB0540 receiver
5 * Copyright (C) 2019 Red Hat Inc. All Rights Reserved
9 #include <linux/device.h>
10 #include <linux/hid.h>
11 #include <linux/module.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[] = {
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 */
42 KEY_AGAIN, /* text: Return, symbol: Jump to */
43 KEY_PLAY, /* text: Start */
44 KEY_ESC, /* text: Cancel */
47 KEY_MENU, /* text: Display */
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[] = {
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)
129 for (i = 0; i < bits; i++) {
130 c |= (u64) (((data & (((u64) 1) << i)) ? 1 : 0))
136 static int get_key(struct creative_sb0540 *creative_sb0540, u64 keycode)
140 for (i = 0; i < ARRAY_SIZE(creative_sb0540_codes); i++) {
141 if (creative_sb0540_codes[i] == keycode)
142 return creative_sb0540->keymap[i];
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);
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",
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 */
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);
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);
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
220 static int creative_sb0540_probe(struct hid_device *hid,
221 const struct hid_device_id *id)
224 struct creative_sb0540 *creative_sb0540;
226 creative_sb0540 = devm_kzalloc(&hid->dev,
227 sizeof(struct creative_sb0540), GFP_KERNEL);
229 if (!creative_sb0540)
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);
241 hid_err(hid, "parse failed\n");
245 ret = hid_hw_start(hid, HID_CONNECT_DEFAULT);
247 hid_err(hid, "hw start failed\n");
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);