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
);