brintos

brintos / linux-shallow public Read only

0
0
Text · 5.6 KiB · b4c8e7a Raw
269 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * HID driver for the Creative SB0540 receiver4 *5 * Copyright (C) 2019 Red Hat Inc. All Rights Reserved6 *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_063};64 65/*66 * Codes and keys from lirc's67 * remotes/creative/lircd.conf.alsa_usb68 * order and size must match creative_sb0540_key_table[] above69 */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	0x807F115};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 as165	 * remotes/creative/lircd.conf.alsa_usb in lirc166	 */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-input215	 * 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);269