123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- * HID driver for the Creative SB0540 receiver
- *
- * Copyright (C) 2019 Red Hat Inc. All Rights Reserved
- *
- */
- #include <linux/device.h>
- #include <linux/hid.h>
- #include <linux/module.h>
- #include "hid-ids.h"
- MODULE_AUTHOR("Bastien Nocera <[email protected]>");
- MODULE_DESCRIPTION("HID Creative SB0540 receiver");
- MODULE_LICENSE("GPL");
- static const unsigned short creative_sb0540_key_table[] = {
- KEY_POWER,
- KEY_RESERVED, /* text: 24bit */
- KEY_RESERVED, /* 24bit wheel up */
- KEY_RESERVED, /* 24bit wheel down */
- KEY_RESERVED, /* text: CMSS */
- KEY_RESERVED, /* CMSS wheel Up */
- KEY_RESERVED, /* CMSS wheel Down */
- KEY_RESERVED, /* text: EAX */
- KEY_RESERVED, /* EAX wheel up */
- KEY_RESERVED, /* EAX wheel down */
- KEY_RESERVED, /* text: 3D Midi */
- KEY_RESERVED, /* 3D Midi wheel up */
- KEY_RESERVED, /* 3D Midi wheel down */
- KEY_MUTE,
- KEY_VOLUMEUP,
- KEY_VOLUMEDOWN,
- KEY_UP,
- KEY_LEFT,
- KEY_RIGHT,
- KEY_REWIND,
- KEY_OK,
- KEY_FASTFORWARD,
- KEY_DOWN,
- KEY_AGAIN, /* text: Return, symbol: Jump to */
- KEY_PLAY, /* text: Start */
- KEY_ESC, /* text: Cancel */
- KEY_RECORD,
- KEY_OPTION,
- KEY_MENU, /* text: Display */
- KEY_PREVIOUS,
- KEY_PLAYPAUSE,
- KEY_NEXT,
- KEY_SLOW,
- KEY_STOP,
- KEY_NUMERIC_1,
- KEY_NUMERIC_2,
- KEY_NUMERIC_3,
- KEY_NUMERIC_4,
- KEY_NUMERIC_5,
- KEY_NUMERIC_6,
- KEY_NUMERIC_7,
- KEY_NUMERIC_8,
- KEY_NUMERIC_9,
- KEY_NUMERIC_0
- };
- /*
- * Codes and keys from lirc's
- * remotes/creative/lircd.conf.alsa_usb
- * order and size must match creative_sb0540_key_table[] above
- */
- static const unsigned short creative_sb0540_codes[] = {
- 0x619E,
- 0x916E,
- 0x926D,
- 0x936C,
- 0x718E,
- 0x946B,
- 0x956A,
- 0x8C73,
- 0x9669,
- 0x9768,
- 0x9867,
- 0x9966,
- 0x9A65,
- 0x6E91,
- 0x629D,
- 0x639C,
- 0x7B84,
- 0x6B94,
- 0x728D,
- 0x8778,
- 0x817E,
- 0x758A,
- 0x8D72,
- 0x8E71,
- 0x8877,
- 0x7C83,
- 0x738C,
- 0x827D,
- 0x7689,
- 0x7F80,
- 0x7986,
- 0x7A85,
- 0x7D82,
- 0x857A,
- 0x8B74,
- 0x8F70,
- 0x906F,
- 0x8A75,
- 0x847B,
- 0x7887,
- 0x8976,
- 0x837C,
- 0x7788,
- 0x807F
- };
- struct creative_sb0540 {
- struct input_dev *input_dev;
- struct hid_device *hid;
- unsigned short keymap[ARRAY_SIZE(creative_sb0540_key_table)];
- };
- static inline u64 reverse(u64 data, int bits)
- {
- int i;
- u64 c;
- c = 0;
- for (i = 0; i < bits; i++) {
- c |= (u64) (((data & (((u64) 1) << i)) ? 1 : 0))
- << (bits - 1 - i);
- }
- return (c);
- }
- static int get_key(struct creative_sb0540 *creative_sb0540, u64 keycode)
- {
- int i;
- for (i = 0; i < ARRAY_SIZE(creative_sb0540_codes); i++) {
- if (creative_sb0540_codes[i] == keycode)
- return creative_sb0540->keymap[i];
- }
- return 0;
- }
- static int creative_sb0540_raw_event(struct hid_device *hid,
- struct hid_report *report, u8 *data, int len)
- {
- struct creative_sb0540 *creative_sb0540 = hid_get_drvdata(hid);
- u64 code, main_code;
- int key;
- if (len != 6)
- return 0;
- /* From daemons/hw_hiddev.c sb0540_rec() in lirc */
- code = reverse(data[5], 8);
- main_code = (code << 8) + ((~code) & 0xff);
- /*
- * Flip to get values in the same format as
- * remotes/creative/lircd.conf.alsa_usb in lirc
- */
- main_code = ((main_code & 0xff) << 8) +
- ((main_code & 0xff00) >> 8);
- key = get_key(creative_sb0540, main_code);
- if (key == 0 || key == KEY_RESERVED) {
- hid_err(hid, "Could not get a key for main_code %llX\n",
- main_code);
- return 0;
- }
- input_report_key(creative_sb0540->input_dev, key, 1);
- input_report_key(creative_sb0540->input_dev, key, 0);
- input_sync(creative_sb0540->input_dev);
- /* let hidraw and hiddev handle the report */
- return 0;
- }
- static int creative_sb0540_input_configured(struct hid_device *hid,
- struct hid_input *hidinput)
- {
- struct input_dev *input_dev = hidinput->input;
- struct creative_sb0540 *creative_sb0540 = hid_get_drvdata(hid);
- int i;
- creative_sb0540->input_dev = input_dev;
- input_dev->keycode = creative_sb0540->keymap;
- input_dev->keycodesize = sizeof(unsigned short);
- input_dev->keycodemax = ARRAY_SIZE(creative_sb0540->keymap);
- input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
- memcpy(creative_sb0540->keymap, creative_sb0540_key_table,
- sizeof(creative_sb0540->keymap));
- for (i = 0; i < ARRAY_SIZE(creative_sb0540_key_table); i++)
- set_bit(creative_sb0540->keymap[i], input_dev->keybit);
- clear_bit(KEY_RESERVED, input_dev->keybit);
- return 0;
- }
- static int creative_sb0540_input_mapping(struct hid_device *hid,
- struct hid_input *hi, struct hid_field *field,
- struct hid_usage *usage, unsigned long **bit, int *max)
- {
- /*
- * We are remapping the keys ourselves, so ignore the hid-input
- * keymap processing.
- */
- return -1;
- }
- static int creative_sb0540_probe(struct hid_device *hid,
- const struct hid_device_id *id)
- {
- int ret;
- struct creative_sb0540 *creative_sb0540;
- creative_sb0540 = devm_kzalloc(&hid->dev,
- sizeof(struct creative_sb0540), GFP_KERNEL);
- if (!creative_sb0540)
- return -ENOMEM;
- creative_sb0540->hid = hid;
- /* force input as some remotes bypass the input registration */
- hid->quirks |= HID_QUIRK_HIDINPUT_FORCE;
- hid_set_drvdata(hid, creative_sb0540);
- ret = hid_parse(hid);
- if (ret) {
- hid_err(hid, "parse failed\n");
- return ret;
- }
- ret = hid_hw_start(hid, HID_CONNECT_DEFAULT);
- if (ret) {
- hid_err(hid, "hw start failed\n");
- return ret;
- }
- return ret;
- }
- static const struct hid_device_id creative_sb0540_devices[] = {
- { HID_USB_DEVICE(USB_VENDOR_ID_CREATIVELABS, USB_DEVICE_ID_CREATIVE_SB0540) },
- { }
- };
- MODULE_DEVICE_TABLE(hid, creative_sb0540_devices);
- static struct hid_driver creative_sb0540_driver = {
- .name = "creative-sb0540",
- .id_table = creative_sb0540_devices,
- .raw_event = creative_sb0540_raw_event,
- .input_configured = creative_sb0540_input_configured,
- .probe = creative_sb0540_probe,
- .input_mapping = creative_sb0540_input_mapping,
- };
- module_hid_driver(creative_sb0540_driver);
|