cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

hid-creative-sb0540.c (5771B)


      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
     14MODULE_AUTHOR("Bastien Nocera <hadess@hadess.net>");
     15MODULE_DESCRIPTION("HID Creative SB0540 receiver");
     16MODULE_LICENSE("GPL");
     17
     18static 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 */
     70static 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
    117struct 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
    123static 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
    136static 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
    149static 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
    185static 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
    209static 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
    220static 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
    254static const struct hid_device_id creative_sb0540_devices[] = {
    255	{ HID_USB_DEVICE(USB_VENDOR_ID_CREATIVELABS, USB_DEVICE_ID_CREATIVE_SB0540) },
    256	{ }
    257};
    258MODULE_DEVICE_TABLE(hid, creative_sb0540_devices);
    259
    260static 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};
    268module_hid_driver(creative_sb0540_driver);