xref: /OK3568_Linux_fs/kernel/drivers/hid/hid-creative-sb0540.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * HID driver for the Creative SB0540 receiver
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Copyright (C) 2019 Red Hat Inc. All Rights Reserved
6*4882a593Smuzhiyun  *
7*4882a593Smuzhiyun  */
8*4882a593Smuzhiyun 
9*4882a593Smuzhiyun #include <linux/device.h>
10*4882a593Smuzhiyun #include <linux/hid.h>
11*4882a593Smuzhiyun #include <linux/module.h>
12*4882a593Smuzhiyun #include "hid-ids.h"
13*4882a593Smuzhiyun 
14*4882a593Smuzhiyun MODULE_AUTHOR("Bastien Nocera <hadess@hadess.net>");
15*4882a593Smuzhiyun MODULE_DESCRIPTION("HID Creative SB0540 receiver");
16*4882a593Smuzhiyun MODULE_LICENSE("GPL");
17*4882a593Smuzhiyun 
18*4882a593Smuzhiyun static const unsigned short creative_sb0540_key_table[] = {
19*4882a593Smuzhiyun 	KEY_POWER,
20*4882a593Smuzhiyun 	KEY_RESERVED,		/* text: 24bit */
21*4882a593Smuzhiyun 	KEY_RESERVED,		/* 24bit wheel up */
22*4882a593Smuzhiyun 	KEY_RESERVED,		/* 24bit wheel down */
23*4882a593Smuzhiyun 	KEY_RESERVED,		/* text: CMSS */
24*4882a593Smuzhiyun 	KEY_RESERVED,		/* CMSS wheel Up */
25*4882a593Smuzhiyun 	KEY_RESERVED,		/* CMSS wheel Down */
26*4882a593Smuzhiyun 	KEY_RESERVED,		/* text: EAX */
27*4882a593Smuzhiyun 	KEY_RESERVED,		/* EAX wheel up */
28*4882a593Smuzhiyun 	KEY_RESERVED,		/* EAX wheel down */
29*4882a593Smuzhiyun 	KEY_RESERVED,		/* text: 3D Midi */
30*4882a593Smuzhiyun 	KEY_RESERVED,		/* 3D Midi wheel up */
31*4882a593Smuzhiyun 	KEY_RESERVED,		/* 3D Midi wheel down */
32*4882a593Smuzhiyun 	KEY_MUTE,
33*4882a593Smuzhiyun 	KEY_VOLUMEUP,
34*4882a593Smuzhiyun 	KEY_VOLUMEDOWN,
35*4882a593Smuzhiyun 	KEY_UP,
36*4882a593Smuzhiyun 	KEY_LEFT,
37*4882a593Smuzhiyun 	KEY_RIGHT,
38*4882a593Smuzhiyun 	KEY_REWIND,
39*4882a593Smuzhiyun 	KEY_OK,
40*4882a593Smuzhiyun 	KEY_FASTFORWARD,
41*4882a593Smuzhiyun 	KEY_DOWN,
42*4882a593Smuzhiyun 	KEY_AGAIN,		/* text: Return, symbol: Jump to */
43*4882a593Smuzhiyun 	KEY_PLAY,		/* text: Start */
44*4882a593Smuzhiyun 	KEY_ESC,		/* text: Cancel */
45*4882a593Smuzhiyun 	KEY_RECORD,
46*4882a593Smuzhiyun 	KEY_OPTION,
47*4882a593Smuzhiyun 	KEY_MENU,		/* text: Display */
48*4882a593Smuzhiyun 	KEY_PREVIOUS,
49*4882a593Smuzhiyun 	KEY_PLAYPAUSE,
50*4882a593Smuzhiyun 	KEY_NEXT,
51*4882a593Smuzhiyun 	KEY_SLOW,
52*4882a593Smuzhiyun 	KEY_STOP,
53*4882a593Smuzhiyun 	KEY_NUMERIC_1,
54*4882a593Smuzhiyun 	KEY_NUMERIC_2,
55*4882a593Smuzhiyun 	KEY_NUMERIC_3,
56*4882a593Smuzhiyun 	KEY_NUMERIC_4,
57*4882a593Smuzhiyun 	KEY_NUMERIC_5,
58*4882a593Smuzhiyun 	KEY_NUMERIC_6,
59*4882a593Smuzhiyun 	KEY_NUMERIC_7,
60*4882a593Smuzhiyun 	KEY_NUMERIC_8,
61*4882a593Smuzhiyun 	KEY_NUMERIC_9,
62*4882a593Smuzhiyun 	KEY_NUMERIC_0
63*4882a593Smuzhiyun };
64*4882a593Smuzhiyun 
65*4882a593Smuzhiyun /*
66*4882a593Smuzhiyun  * Codes and keys from lirc's
67*4882a593Smuzhiyun  * remotes/creative/lircd.conf.alsa_usb
68*4882a593Smuzhiyun  * order and size must match creative_sb0540_key_table[] above
69*4882a593Smuzhiyun  */
70*4882a593Smuzhiyun static const unsigned short creative_sb0540_codes[] = {
71*4882a593Smuzhiyun 	0x619E,
72*4882a593Smuzhiyun 	0x916E,
73*4882a593Smuzhiyun 	0x926D,
74*4882a593Smuzhiyun 	0x936C,
75*4882a593Smuzhiyun 	0x718E,
76*4882a593Smuzhiyun 	0x946B,
77*4882a593Smuzhiyun 	0x956A,
78*4882a593Smuzhiyun 	0x8C73,
79*4882a593Smuzhiyun 	0x9669,
80*4882a593Smuzhiyun 	0x9768,
81*4882a593Smuzhiyun 	0x9867,
82*4882a593Smuzhiyun 	0x9966,
83*4882a593Smuzhiyun 	0x9A65,
84*4882a593Smuzhiyun 	0x6E91,
85*4882a593Smuzhiyun 	0x629D,
86*4882a593Smuzhiyun 	0x639C,
87*4882a593Smuzhiyun 	0x7B84,
88*4882a593Smuzhiyun 	0x6B94,
89*4882a593Smuzhiyun 	0x728D,
90*4882a593Smuzhiyun 	0x8778,
91*4882a593Smuzhiyun 	0x817E,
92*4882a593Smuzhiyun 	0x758A,
93*4882a593Smuzhiyun 	0x8D72,
94*4882a593Smuzhiyun 	0x8E71,
95*4882a593Smuzhiyun 	0x8877,
96*4882a593Smuzhiyun 	0x7C83,
97*4882a593Smuzhiyun 	0x738C,
98*4882a593Smuzhiyun 	0x827D,
99*4882a593Smuzhiyun 	0x7689,
100*4882a593Smuzhiyun 	0x7F80,
101*4882a593Smuzhiyun 	0x7986,
102*4882a593Smuzhiyun 	0x7A85,
103*4882a593Smuzhiyun 	0x7D82,
104*4882a593Smuzhiyun 	0x857A,
105*4882a593Smuzhiyun 	0x8B74,
106*4882a593Smuzhiyun 	0x8F70,
107*4882a593Smuzhiyun 	0x906F,
108*4882a593Smuzhiyun 	0x8A75,
109*4882a593Smuzhiyun 	0x847B,
110*4882a593Smuzhiyun 	0x7887,
111*4882a593Smuzhiyun 	0x8976,
112*4882a593Smuzhiyun 	0x837C,
113*4882a593Smuzhiyun 	0x7788,
114*4882a593Smuzhiyun 	0x807F
115*4882a593Smuzhiyun };
116*4882a593Smuzhiyun 
117*4882a593Smuzhiyun struct creative_sb0540 {
118*4882a593Smuzhiyun 	struct input_dev *input_dev;
119*4882a593Smuzhiyun 	struct hid_device *hid;
120*4882a593Smuzhiyun 	unsigned short keymap[ARRAY_SIZE(creative_sb0540_key_table)];
121*4882a593Smuzhiyun };
122*4882a593Smuzhiyun 
reverse(u64 data,int bits)123*4882a593Smuzhiyun static inline u64 reverse(u64 data, int bits)
124*4882a593Smuzhiyun {
125*4882a593Smuzhiyun 	int i;
126*4882a593Smuzhiyun 	u64 c;
127*4882a593Smuzhiyun 
128*4882a593Smuzhiyun 	c = 0;
129*4882a593Smuzhiyun 	for (i = 0; i < bits; i++) {
130*4882a593Smuzhiyun 		c |= (u64) (((data & (((u64) 1) << i)) ? 1 : 0))
131*4882a593Smuzhiyun 			<< (bits - 1 - i);
132*4882a593Smuzhiyun 	}
133*4882a593Smuzhiyun 	return (c);
134*4882a593Smuzhiyun }
135*4882a593Smuzhiyun 
get_key(struct creative_sb0540 * creative_sb0540,u64 keycode)136*4882a593Smuzhiyun static int get_key(struct creative_sb0540 *creative_sb0540, u64 keycode)
137*4882a593Smuzhiyun {
138*4882a593Smuzhiyun 	int i;
139*4882a593Smuzhiyun 
140*4882a593Smuzhiyun 	for (i = 0; i < ARRAY_SIZE(creative_sb0540_codes); i++) {
141*4882a593Smuzhiyun 		if (creative_sb0540_codes[i] == keycode)
142*4882a593Smuzhiyun 			return creative_sb0540->keymap[i];
143*4882a593Smuzhiyun 	}
144*4882a593Smuzhiyun 
145*4882a593Smuzhiyun 	return 0;
146*4882a593Smuzhiyun 
147*4882a593Smuzhiyun }
148*4882a593Smuzhiyun 
creative_sb0540_raw_event(struct hid_device * hid,struct hid_report * report,u8 * data,int len)149*4882a593Smuzhiyun static int creative_sb0540_raw_event(struct hid_device *hid,
150*4882a593Smuzhiyun 	struct hid_report *report, u8 *data, int len)
151*4882a593Smuzhiyun {
152*4882a593Smuzhiyun 	struct creative_sb0540 *creative_sb0540 = hid_get_drvdata(hid);
153*4882a593Smuzhiyun 	u64 code, main_code;
154*4882a593Smuzhiyun 	int key;
155*4882a593Smuzhiyun 
156*4882a593Smuzhiyun 	if (len != 6)
157*4882a593Smuzhiyun 		return 0;
158*4882a593Smuzhiyun 
159*4882a593Smuzhiyun 	/* From daemons/hw_hiddev.c sb0540_rec() in lirc */
160*4882a593Smuzhiyun 	code = reverse(data[5], 8);
161*4882a593Smuzhiyun 	main_code = (code << 8) + ((~code) & 0xff);
162*4882a593Smuzhiyun 
163*4882a593Smuzhiyun 	/*
164*4882a593Smuzhiyun 	 * Flip to get values in the same format as
165*4882a593Smuzhiyun 	 * remotes/creative/lircd.conf.alsa_usb in lirc
166*4882a593Smuzhiyun 	 */
167*4882a593Smuzhiyun 	main_code = ((main_code & 0xff) << 8) +
168*4882a593Smuzhiyun 		((main_code & 0xff00) >> 8);
169*4882a593Smuzhiyun 
170*4882a593Smuzhiyun 	key = get_key(creative_sb0540, main_code);
171*4882a593Smuzhiyun 	if (key == 0 || key == KEY_RESERVED) {
172*4882a593Smuzhiyun 		hid_err(hid, "Could not get a key for main_code %llX\n",
173*4882a593Smuzhiyun 			main_code);
174*4882a593Smuzhiyun 		return 0;
175*4882a593Smuzhiyun 	}
176*4882a593Smuzhiyun 
177*4882a593Smuzhiyun 	input_report_key(creative_sb0540->input_dev, key, 1);
178*4882a593Smuzhiyun 	input_report_key(creative_sb0540->input_dev, key, 0);
179*4882a593Smuzhiyun 	input_sync(creative_sb0540->input_dev);
180*4882a593Smuzhiyun 
181*4882a593Smuzhiyun 	/* let hidraw and hiddev handle the report */
182*4882a593Smuzhiyun 	return 0;
183*4882a593Smuzhiyun }
184*4882a593Smuzhiyun 
creative_sb0540_input_configured(struct hid_device * hid,struct hid_input * hidinput)185*4882a593Smuzhiyun static int creative_sb0540_input_configured(struct hid_device *hid,
186*4882a593Smuzhiyun 		struct hid_input *hidinput)
187*4882a593Smuzhiyun {
188*4882a593Smuzhiyun 	struct input_dev *input_dev = hidinput->input;
189*4882a593Smuzhiyun 	struct creative_sb0540 *creative_sb0540 = hid_get_drvdata(hid);
190*4882a593Smuzhiyun 	int i;
191*4882a593Smuzhiyun 
192*4882a593Smuzhiyun 	creative_sb0540->input_dev = input_dev;
193*4882a593Smuzhiyun 
194*4882a593Smuzhiyun 	input_dev->keycode = creative_sb0540->keymap;
195*4882a593Smuzhiyun 	input_dev->keycodesize = sizeof(unsigned short);
196*4882a593Smuzhiyun 	input_dev->keycodemax = ARRAY_SIZE(creative_sb0540->keymap);
197*4882a593Smuzhiyun 
198*4882a593Smuzhiyun 	input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
199*4882a593Smuzhiyun 
200*4882a593Smuzhiyun 	memcpy(creative_sb0540->keymap, creative_sb0540_key_table,
201*4882a593Smuzhiyun 		sizeof(creative_sb0540->keymap));
202*4882a593Smuzhiyun 	for (i = 0; i < ARRAY_SIZE(creative_sb0540_key_table); i++)
203*4882a593Smuzhiyun 		set_bit(creative_sb0540->keymap[i], input_dev->keybit);
204*4882a593Smuzhiyun 	clear_bit(KEY_RESERVED, input_dev->keybit);
205*4882a593Smuzhiyun 
206*4882a593Smuzhiyun 	return 0;
207*4882a593Smuzhiyun }
208*4882a593Smuzhiyun 
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)209*4882a593Smuzhiyun static int creative_sb0540_input_mapping(struct hid_device *hid,
210*4882a593Smuzhiyun 		struct hid_input *hi, struct hid_field *field,
211*4882a593Smuzhiyun 		struct hid_usage *usage, unsigned long **bit, int *max)
212*4882a593Smuzhiyun {
213*4882a593Smuzhiyun 	/*
214*4882a593Smuzhiyun 	 * We are remapping the keys ourselves, so ignore the hid-input
215*4882a593Smuzhiyun 	 * keymap processing.
216*4882a593Smuzhiyun 	 */
217*4882a593Smuzhiyun 	return -1;
218*4882a593Smuzhiyun }
219*4882a593Smuzhiyun 
creative_sb0540_probe(struct hid_device * hid,const struct hid_device_id * id)220*4882a593Smuzhiyun static int creative_sb0540_probe(struct hid_device *hid,
221*4882a593Smuzhiyun 		const struct hid_device_id *id)
222*4882a593Smuzhiyun {
223*4882a593Smuzhiyun 	int ret;
224*4882a593Smuzhiyun 	struct creative_sb0540 *creative_sb0540;
225*4882a593Smuzhiyun 
226*4882a593Smuzhiyun 	creative_sb0540 = devm_kzalloc(&hid->dev,
227*4882a593Smuzhiyun 		sizeof(struct creative_sb0540), GFP_KERNEL);
228*4882a593Smuzhiyun 
229*4882a593Smuzhiyun 	if (!creative_sb0540)
230*4882a593Smuzhiyun 		return -ENOMEM;
231*4882a593Smuzhiyun 
232*4882a593Smuzhiyun 	creative_sb0540->hid = hid;
233*4882a593Smuzhiyun 
234*4882a593Smuzhiyun 	/* force input as some remotes bypass the input registration */
235*4882a593Smuzhiyun 	hid->quirks |= HID_QUIRK_HIDINPUT_FORCE;
236*4882a593Smuzhiyun 
237*4882a593Smuzhiyun 	hid_set_drvdata(hid, creative_sb0540);
238*4882a593Smuzhiyun 
239*4882a593Smuzhiyun 	ret = hid_parse(hid);
240*4882a593Smuzhiyun 	if (ret) {
241*4882a593Smuzhiyun 		hid_err(hid, "parse failed\n");
242*4882a593Smuzhiyun 		return ret;
243*4882a593Smuzhiyun 	}
244*4882a593Smuzhiyun 
245*4882a593Smuzhiyun 	ret = hid_hw_start(hid, HID_CONNECT_DEFAULT);
246*4882a593Smuzhiyun 	if (ret) {
247*4882a593Smuzhiyun 		hid_err(hid, "hw start failed\n");
248*4882a593Smuzhiyun 		return ret;
249*4882a593Smuzhiyun 	}
250*4882a593Smuzhiyun 
251*4882a593Smuzhiyun 	return ret;
252*4882a593Smuzhiyun }
253*4882a593Smuzhiyun 
254*4882a593Smuzhiyun static const struct hid_device_id creative_sb0540_devices[] = {
255*4882a593Smuzhiyun 	{ HID_USB_DEVICE(USB_VENDOR_ID_CREATIVELABS, USB_DEVICE_ID_CREATIVE_SB0540) },
256*4882a593Smuzhiyun 	{ }
257*4882a593Smuzhiyun };
258*4882a593Smuzhiyun MODULE_DEVICE_TABLE(hid, creative_sb0540_devices);
259*4882a593Smuzhiyun 
260*4882a593Smuzhiyun static struct hid_driver creative_sb0540_driver = {
261*4882a593Smuzhiyun 	.name = "creative-sb0540",
262*4882a593Smuzhiyun 	.id_table = creative_sb0540_devices,
263*4882a593Smuzhiyun 	.raw_event = creative_sb0540_raw_event,
264*4882a593Smuzhiyun 	.input_configured = creative_sb0540_input_configured,
265*4882a593Smuzhiyun 	.probe = creative_sb0540_probe,
266*4882a593Smuzhiyun 	.input_mapping = creative_sb0540_input_mapping,
267*4882a593Smuzhiyun };
268*4882a593Smuzhiyun module_hid_driver(creative_sb0540_driver);
269