brintos

brintos / linux-shallow public Read only

0
0
Text · 14.8 KiB · f3908a9 Raw
566 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 *  HID driver for some samsung "special" devices4 *5 *  Copyright (c) 1999 Andreas Gal6 *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>7 *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc8 *  Copyright (c) 2006-2007 Jiri Kosina9 *  Copyright (c) 2008 Jiri Slaby10 *  Copyright (c) 2010 Don Prince <dhprince.devel@yahoo.co.uk>11 *12 *  This driver supports several HID devices:13 *14 *  [0419:0001] Samsung IrDA remote controller (reports as Cypress USB Mouse).15 *	various hid report fixups for different variants.16 *17 *  [0419:0600] Creative Desktop Wireless 6000 keyboard/mouse combo18 *	several key mappings used from the consumer usage page19 *	deviate from the USB HUT 1.12 standard.20 */21 22/*23 */24 25#include <linux/device.h>26#include <linux/usb.h>27#include <linux/hid.h>28#include <linux/module.h>29 30#include "hid-ids.h"31 32/*33 * There are several variants for 0419:0001:34 *35 * 1. 184 byte report descriptor36 * Vendor specific report #4 has a size of 48 bit,37 * and therefore is not accepted when inspecting the descriptors.38 * As a workaround we reinterpret the report as:39 *   Variable type, count 6, size 8 bit, log. maximum 25540 * The burden to reconstruct the data is moved into user space.41 *42 * 2. 203 byte report descriptor43 * Report #4 has an array field with logical range 0..18 instead of 1..15.44 *45 * 3. 135 byte report descriptor46 * Report #4 has an array field with logical range 0..17 instead of 1..14.47 *48 * 4. 171 byte report descriptor49 * Report #3 has an array field with logical range 0..1 instead of 1..3.50 */51static inline void samsung_irda_dev_trace(struct hid_device *hdev,52		unsigned int rsize)53{54	hid_info(hdev, "fixing up Samsung IrDA %d byte report descriptor\n",55		 rsize);56}57 58static __u8 *samsung_irda_report_fixup(struct hid_device *hdev, __u8 *rdesc,59		unsigned int *rsize)60{61	if (*rsize == 184 && !memcmp(&rdesc[175], "\x25\x40\x75\x30\x95\x01", 6) &&62			rdesc[182] == 0x40) {63		samsung_irda_dev_trace(hdev, 184);64		rdesc[176] = 0xff;65		rdesc[178] = 0x08;66		rdesc[180] = 0x06;67		rdesc[182] = 0x42;68	} else if (*rsize == 203 && !memcmp(&rdesc[192], "\x15\x00\x25\x12", 4)) {69		samsung_irda_dev_trace(hdev, 203);70		rdesc[193] = 0x01;71		rdesc[195] = 0x0f;72	} else if (*rsize == 135 && !memcmp(&rdesc[124], "\x15\x00\x25\x11", 4)) {73		samsung_irda_dev_trace(hdev, 135);74		rdesc[125] = 0x01;75		rdesc[127] = 0x0e;76	} else if (*rsize == 171 && !memcmp(&rdesc[160], "\x15\x00\x25\x01", 4)) {77		samsung_irda_dev_trace(hdev, 171);78		rdesc[161] = 0x01;79		rdesc[163] = 0x03;80	}81	return rdesc;82}83 84#define samsung_kbd_mouse_map_key_clear(c) \85	hid_map_usage_clear(hi, usage, bit, max, EV_KEY, (c))86 87static int samsung_kbd_mouse_input_mapping(struct hid_device *hdev,88	struct hid_input *hi, struct hid_field *field, struct hid_usage *usage,89	unsigned long **bit, int *max)90{91	struct usb_interface *intf = to_usb_interface(hdev->dev.parent);92	unsigned short ifnum = intf->cur_altsetting->desc.bInterfaceNumber;93 94	if (ifnum != 1 || HID_UP_CONSUMER != (usage->hid & HID_USAGE_PAGE))95		return 0;96 97	dbg_hid("samsung wireless keyboard/mouse input mapping event [0x%x]\n",98		usage->hid & HID_USAGE);99 100	switch (usage->hid & HID_USAGE) {101	/* report 2 */102	case 0x183:103		samsung_kbd_mouse_map_key_clear(KEY_MEDIA);104		break;105	case 0x195:106		samsung_kbd_mouse_map_key_clear(KEY_EMAIL);107		break;108	case 0x196:109		samsung_kbd_mouse_map_key_clear(KEY_CALC);110		break;111	case 0x197:112		samsung_kbd_mouse_map_key_clear(KEY_COMPUTER);113		break;114	case 0x22b:115		samsung_kbd_mouse_map_key_clear(KEY_SEARCH);116		break;117	case 0x22c:118		samsung_kbd_mouse_map_key_clear(KEY_WWW);119		break;120	case 0x22d:121		samsung_kbd_mouse_map_key_clear(KEY_BACK);122		break;123	case 0x22e:124		samsung_kbd_mouse_map_key_clear(KEY_FORWARD);125		break;126	case 0x22f:127		samsung_kbd_mouse_map_key_clear(KEY_FAVORITES);128		break;129	case 0x230:130		samsung_kbd_mouse_map_key_clear(KEY_REFRESH);131		break;132	case 0x231:133		samsung_kbd_mouse_map_key_clear(KEY_STOP);134		break;135	default:136		return 0;137	}138 139	return 1;140}141 142static int samsung_kbd_input_mapping(struct hid_device *hdev,143	struct hid_input *hi, struct hid_field *field, struct hid_usage *usage,144	unsigned long **bit, int *max)145{146	if (!(HID_UP_CONSUMER == (usage->hid & HID_USAGE_PAGE) ||147			HID_UP_KEYBOARD == (usage->hid & HID_USAGE_PAGE)))148		return 0;149 150	dbg_hid("samsung wireless keyboard input mapping event [0x%x]\n",151		usage->hid & HID_USAGE);152 153	if (HID_UP_KEYBOARD == (usage->hid & HID_USAGE_PAGE)) {154		set_bit(EV_REP, hi->input->evbit);155		switch (usage->hid & HID_USAGE) {156		case 0x32:157			samsung_kbd_mouse_map_key_clear(KEY_BACKSLASH);158			break;159		case 0x64:160			samsung_kbd_mouse_map_key_clear(KEY_102ND);161			break;162		/* Only for BR keyboard */163		case 0x87:164			samsung_kbd_mouse_map_key_clear(KEY_RO);165			break;166		default:167			return 0;168		}169	}170 171	if (HID_UP_CONSUMER == (usage->hid & HID_USAGE_PAGE)) {172		switch (usage->hid & HID_USAGE) {173		/* report 2 */174		/* MENU */175		case 0x040:176			samsung_kbd_mouse_map_key_clear(KEY_MENU);177			break;178		case 0x18a:179			samsung_kbd_mouse_map_key_clear(KEY_MAIL);180			break;181		case 0x196:182			samsung_kbd_mouse_map_key_clear(KEY_WWW);183			break;184		case 0x19e:185			samsung_kbd_mouse_map_key_clear(KEY_SCREENLOCK);186			break;187		case 0x221:188			samsung_kbd_mouse_map_key_clear(KEY_SEARCH);189			break;190		case 0x223:191			samsung_kbd_mouse_map_key_clear(KEY_HOMEPAGE);192			break;193		/* Smtart Voice Key */194		case 0x300:195			samsung_kbd_mouse_map_key_clear(BTN_TRIGGER_HAPPY13);196			break;197		/* RECENTAPPS */198		case 0x301:199			samsung_kbd_mouse_map_key_clear(BTN_TRIGGER_HAPPY1);200			break;201		/* APPLICATION */202		case 0x302:203			samsung_kbd_mouse_map_key_clear(BTN_TRIGGER_HAPPY2);204			break;205		/* Voice search */206		case 0x305:207			samsung_kbd_mouse_map_key_clear(BTN_TRIGGER_HAPPY4);208			break;209		/* QPANEL on/off */210		case 0x306:211			samsung_kbd_mouse_map_key_clear(BTN_TRIGGER_HAPPY5);212			break;213		/* SIP on/off */214		case 0x307:215			samsung_kbd_mouse_map_key_clear(BTN_TRIGGER_HAPPY3);216			break;217		/* LANG */218		case 0x308:219			samsung_kbd_mouse_map_key_clear(KEY_LANGUAGE);220			break;221		case 0x30a:222			samsung_kbd_mouse_map_key_clear(KEY_BRIGHTNESSDOWN);223			break;224		case 0x30b:225			samsung_kbd_mouse_map_key_clear(KEY_BRIGHTNESSUP);226			break;227		default:228			return 0;229		}230	}231 232	return 1;233}234 235static int samsung_gamepad_input_mapping(struct hid_device *hdev,236	struct hid_input *hi, struct hid_field *field, struct hid_usage *usage,237	unsigned long **bit, int *max)238{239	if (!(HID_UP_BUTTON == (usage->hid & HID_USAGE_PAGE) ||240			HID_UP_CONSUMER == (usage->hid & HID_USAGE_PAGE)))241		return 0;242 243	dbg_hid("samsung wireless gamepad input mapping event [0x%x], %ld, %ld, [0x%x]\n",244		usage->hid & HID_USAGE, hi->input->evbit[0], hi->input->absbit[0], usage->hid & HID_USAGE_PAGE);245 246	if (HID_UP_BUTTON == (usage->hid & HID_USAGE_PAGE)) {247		switch (usage->hid & HID_USAGE) {248		case 0x01:249			samsung_kbd_mouse_map_key_clear(BTN_A);250			break;251		case 0x02:252			samsung_kbd_mouse_map_key_clear(BTN_B);253			break;254		case 0x03:255			samsung_kbd_mouse_map_key_clear(BTN_C);256			break;257		case 0x04:258			samsung_kbd_mouse_map_key_clear(BTN_X);259			break;260		case 0x05:261			samsung_kbd_mouse_map_key_clear(BTN_Y);262			break;263		case 0x06:264			samsung_kbd_mouse_map_key_clear(BTN_Z);265			break;266		case 0x07:267			samsung_kbd_mouse_map_key_clear(BTN_TL);268			break;269		case 0x08:270			samsung_kbd_mouse_map_key_clear(BTN_TR);271			break;272		case 0x09:273			samsung_kbd_mouse_map_key_clear(BTN_TL2);274			break;275		case 0x0a:276			samsung_kbd_mouse_map_key_clear(BTN_TR2);277			break;278		case 0x0b:279			samsung_kbd_mouse_map_key_clear(BTN_SELECT);280			break;281		case 0x0c:282			samsung_kbd_mouse_map_key_clear(BTN_START);283			break;284		case 0x0d:285			samsung_kbd_mouse_map_key_clear(BTN_MODE);286			break;287		case 0x0e:288			samsung_kbd_mouse_map_key_clear(BTN_THUMBL);289			break;290		case 0x0f:291			samsung_kbd_mouse_map_key_clear(BTN_THUMBR);292			break;293		case 0x10:294			samsung_kbd_mouse_map_key_clear(0x13f);295			break;296		default:297			return 0;298		}299	}300 301	if (HID_UP_CONSUMER == (usage->hid & HID_USAGE_PAGE)) {302		switch (usage->hid & HID_USAGE) {303		case 0x040:304			samsung_kbd_mouse_map_key_clear(KEY_MENU);305			break;306		case 0x223:307			samsung_kbd_mouse_map_key_clear(KEY_HOMEPAGE);308			break;309		case 0x224:310			samsung_kbd_mouse_map_key_clear(KEY_BACK);311			break;312 313		/* Screen Capture */314		case 0x303:315			samsung_kbd_mouse_map_key_clear(KEY_SYSRQ);316			break;317 318		default:319			return 0;320		}321	}322 323	return 1;324}325 326static int samsung_actionmouse_input_mapping(struct hid_device *hdev,327	struct hid_input *hi, struct hid_field *field, struct hid_usage *usage,328	unsigned long **bit, int *max)329{330 331	dbg_hid("samsung wireless actionmouse input mapping event [0x%x], [0x%x], %ld, %ld, [0x%x]\n",332			usage->hid, usage->hid & HID_USAGE, hi->input->evbit[0], hi->input->absbit[0],333			usage->hid & HID_USAGE_PAGE);334 335	if (((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER) && ((usage->hid & HID_USAGE_PAGE) != HID_UP_BUTTON))336		return 0;337 338	switch (usage->hid & HID_USAGE) {339	case 0x301:340		samsung_kbd_mouse_map_key_clear(254);341		break;342	default:343		return 0;344	}345 346	return 1;347}348 349static int samsung_universal_kbd_input_mapping(struct hid_device *hdev,350	struct hid_input *hi, struct hid_field *field, struct hid_usage *usage,351	unsigned long **bit, int *max)352{353	if (!(HID_UP_CONSUMER == (usage->hid & HID_USAGE_PAGE) ||354			HID_UP_KEYBOARD == (usage->hid & HID_USAGE_PAGE)))355		return 0;356 357	dbg_hid("samsung wireless keyboard input mapping event [0x%x]\n",358		usage->hid & HID_USAGE);359 360	if (HID_UP_KEYBOARD == (usage->hid & HID_USAGE_PAGE)) {361		set_bit(EV_REP, hi->input->evbit);362		switch (usage->hid & HID_USAGE) {363		case 0x32:364			samsung_kbd_mouse_map_key_clear(KEY_BACKSLASH);365			break;366		case 0x64:367			samsung_kbd_mouse_map_key_clear(KEY_102ND);368			break;369		/* Only for BR keyboard */370		case 0x87:371			samsung_kbd_mouse_map_key_clear(KEY_RO);372			break;373		default:374			return 0;375		}376	}377 378	if (HID_UP_CONSUMER == (usage->hid & HID_USAGE_PAGE)) {379		switch (usage->hid & HID_USAGE) {380		/* report 2 */381		/* MENU */382		case 0x040:383			samsung_kbd_mouse_map_key_clear(KEY_MENU);384			break;385		case 0x18a:386			samsung_kbd_mouse_map_key_clear(KEY_MAIL);387			break;388		case 0x196:389			samsung_kbd_mouse_map_key_clear(KEY_WWW);390			break;391		case 0x19e:392			samsung_kbd_mouse_map_key_clear(KEY_SCREENLOCK);393			break;394		case 0x221:395			samsung_kbd_mouse_map_key_clear(KEY_SEARCH);396			break;397		case 0x223:398			samsung_kbd_mouse_map_key_clear(KEY_HOMEPAGE);399			break;400		/* RECENTAPPS */401		case 0x301:402			samsung_kbd_mouse_map_key_clear(BTN_TRIGGER_HAPPY1);403			break;404		/* APPLICATION */405		case 0x302:406			samsung_kbd_mouse_map_key_clear(BTN_TRIGGER_HAPPY2);407			break;408		/* Voice search */409		case 0x305:410			samsung_kbd_mouse_map_key_clear(BTN_TRIGGER_HAPPY4);411			break;412		/* QPANEL on/off */413		case 0x306:414			samsung_kbd_mouse_map_key_clear(BTN_TRIGGER_HAPPY5);415			break;416		/* SIP on/off */417		case 0x307:418			samsung_kbd_mouse_map_key_clear(BTN_TRIGGER_HAPPY3);419			break;420		/* LANG */421		case 0x308:422			samsung_kbd_mouse_map_key_clear(KEY_LANGUAGE);423			break;424		case 0x30a:425			samsung_kbd_mouse_map_key_clear(KEY_BRIGHTNESSDOWN);426			break;427		case 0x070:428			samsung_kbd_mouse_map_key_clear(KEY_BRIGHTNESSDOWN);429			break;430		case 0x30b:431			samsung_kbd_mouse_map_key_clear(KEY_BRIGHTNESSUP);432			break;433		case 0x06f:434			samsung_kbd_mouse_map_key_clear(KEY_BRIGHTNESSUP);435			break;436		/* S-Finder */437		case 0x304:438			samsung_kbd_mouse_map_key_clear(BTN_TRIGGER_HAPPY7);439			break;440		/* Screen Capture */441		case 0x303:442			samsung_kbd_mouse_map_key_clear(KEY_SYSRQ);443			break;444		/* Multi Window */445		case 0x309:446			samsung_kbd_mouse_map_key_clear(BTN_TRIGGER_HAPPY9);447			break;448		/* HotKey App 1 */449		case 0x071:450			samsung_kbd_mouse_map_key_clear(0x2f5);451			break;452		/* HotKey App 2 */453		case 0x072:454			samsung_kbd_mouse_map_key_clear(0x2f6);455			break;456		/* HotKey App 3 */457		case 0x073:458			samsung_kbd_mouse_map_key_clear(0x2f7);459			break;460		/* Dex */461		case 0x06e:462			samsung_kbd_mouse_map_key_clear(0x2bd);463			break;464		default:465			return 0;466		}467	}468 469	return 1;470}471 472static const __u8 *samsung_report_fixup(struct hid_device *hdev, __u8 *rdesc,473	unsigned int *rsize)474{475	if (hdev->product == USB_DEVICE_ID_SAMSUNG_IR_REMOTE && hid_is_usb(hdev))476		rdesc = samsung_irda_report_fixup(hdev, rdesc, rsize);477	return rdesc;478}479 480static int samsung_input_mapping(struct hid_device *hdev, struct hid_input *hi,481	struct hid_field *field, struct hid_usage *usage,482	unsigned long **bit, int *max)483{484	int ret = 0;485 486	if (hdev->product == USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD_MOUSE && hid_is_usb(hdev))487		ret = samsung_kbd_mouse_input_mapping(hdev,488			hi, field, usage, bit, max);489	else if (hdev->product == USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD)490		ret = samsung_kbd_input_mapping(hdev,491			hi, field, usage, bit, max);492	else if (hdev->product == USB_DEVICE_ID_SAMSUNG_WIRELESS_GAMEPAD)493		ret = samsung_gamepad_input_mapping(hdev,494			hi, field, usage, bit, max);495	else if (hdev->product == USB_DEVICE_ID_SAMSUNG_WIRELESS_ACTIONMOUSE)496		ret = samsung_actionmouse_input_mapping(hdev,497			hi, field, usage, bit, max);498	else if (hdev->product == USB_DEVICE_ID_SAMSUNG_WIRELESS_UNIVERSAL_KBD)499		ret = samsung_universal_kbd_input_mapping(hdev,500			hi, field, usage, bit, max);501	else if (hdev->product == USB_DEVICE_ID_SAMSUNG_WIRELESS_MULTI_HOGP_KBD)502		ret = samsung_universal_kbd_input_mapping(hdev,503			hi, field, usage, bit, max);504 505	return ret;506}507 508static int samsung_probe(struct hid_device *hdev,509		const struct hid_device_id *id)510{511	int ret;512	unsigned int cmask = HID_CONNECT_DEFAULT;513 514	ret = hid_parse(hdev);515	if (ret) {516		hid_err(hdev, "parse failed\n");517		goto err_free;518	}519 520	if (hdev->product == USB_DEVICE_ID_SAMSUNG_IR_REMOTE) {521		if (!hid_is_usb(hdev)) {522			ret = -EINVAL;523			goto err_free;524		}525		if (hdev->rsize == 184) {526			/* disable hidinput, force hiddev */527			cmask = (cmask & ~HID_CONNECT_HIDINPUT) |528				HID_CONNECT_HIDDEV_FORCE;529		}530	}531 532	ret = hid_hw_start(hdev, cmask);533	if (ret) {534		hid_err(hdev, "hw start failed\n");535		goto err_free;536	}537 538	return 0;539err_free:540	return ret;541}542 543static const struct hid_device_id samsung_devices[] = {544	{ HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE) },545	{ HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD_MOUSE) },546	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SAMSUNG_ELECTRONICS, USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD) },547	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SAMSUNG_ELECTRONICS, USB_DEVICE_ID_SAMSUNG_WIRELESS_GAMEPAD) },548	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SAMSUNG_ELECTRONICS, USB_DEVICE_ID_SAMSUNG_WIRELESS_ACTIONMOUSE) },549	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SAMSUNG_ELECTRONICS, USB_DEVICE_ID_SAMSUNG_WIRELESS_UNIVERSAL_KBD) },550	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SAMSUNG_ELECTRONICS, USB_DEVICE_ID_SAMSUNG_WIRELESS_MULTI_HOGP_KBD) },551	{ }552};553MODULE_DEVICE_TABLE(hid, samsung_devices);554 555static struct hid_driver samsung_driver = {556	.name = "samsung",557	.id_table = samsung_devices,558	.report_fixup = samsung_report_fixup,559	.input_mapping = samsung_input_mapping,560	.probe = samsung_probe,561};562module_hid_driver(samsung_driver);563 564MODULE_DESCRIPTION("HID driver for some samsung \"special\" devices");565MODULE_LICENSE("GPL");566