forked from mirrors/linux
		
	HID: plantronics: Update to map volume up/down controls
Update Kconfig with enhanced help text for hid-plantronics driver. Update hid-plantronics.c to identify device type and correctly map either the vendor unique or consumer control volume up/down usages to KEY_VOLUMEUP and KEY_VOLUMEDOWN events. Unmapped usages are ignored to prevent core mapping of unknown usages to random mouse events. Tested on ChromeBox/ChromeBook with various Plantronics devices. Signed-off-by: Terry Junge <terry.junge@plantronics.com> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
This commit is contained in:
		
							parent
							
								
									6efdb114b4
								
							
						
					
					
						commit
						81bb773fae
					
				
					 2 changed files with 130 additions and 9 deletions
				
			
		| 
						 | 
					@ -634,7 +634,12 @@ config HID_PLANTRONICS
 | 
				
			||||||
	tristate "Plantronics USB HID Driver"
 | 
						tristate "Plantronics USB HID Driver"
 | 
				
			||||||
	depends on HID
 | 
						depends on HID
 | 
				
			||||||
	---help---
 | 
						---help---
 | 
				
			||||||
	Provides HID support for Plantronics telephony devices.
 | 
						  Provides HID support for Plantronics USB audio devices.
 | 
				
			||||||
 | 
						  Correctly maps vendor unique volume up/down HID usages to
 | 
				
			||||||
 | 
						  KEY_VOLUMEUP and KEY_VOLUMEDOWN events and prevents core mapping
 | 
				
			||||||
 | 
						  of other vendor unique HID usages to random mouse events.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						  Say M here if you may ever plug in a Plantronics USB audio device.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
config HID_PRIMAX
 | 
					config HID_PRIMAX
 | 
				
			||||||
	tristate "Primax non-fully HID-compliant devices"
 | 
						tristate "Primax non-fully HID-compliant devices"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,7 +2,7 @@
 | 
				
			||||||
 *  Plantronics USB HID Driver
 | 
					 *  Plantronics USB HID Driver
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 *  Copyright (c) 2014 JD Cole <jd.cole@plantronics.com>
 | 
					 *  Copyright (c) 2014 JD Cole <jd.cole@plantronics.com>
 | 
				
			||||||
 *  Copyright (c) 2014 Terry Junge <terry.junge@plantronics.com>
 | 
					 *  Copyright (c) 2015 Terry Junge <terry.junge@plantronics.com>
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
| 
						 | 
					@ -17,23 +17,138 @@
 | 
				
			||||||
#include <linux/hid.h>
 | 
					#include <linux/hid.h>
 | 
				
			||||||
#include <linux/module.h>
 | 
					#include <linux/module.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define PLT_HID_1_0_PAGE	0xffa00000
 | 
				
			||||||
 | 
					#define PLT_HID_2_0_PAGE	0xffa20000
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define PLT_BASIC_TELEPHONY	0x0003
 | 
				
			||||||
 | 
					#define PLT_BASIC_EXCEPTION	0x0005
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define PLT_VOL_UP		0x00b1
 | 
				
			||||||
 | 
					#define PLT_VOL_DOWN		0x00b2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define PLT1_VOL_UP		(PLT_HID_1_0_PAGE | PLT_VOL_UP)
 | 
				
			||||||
 | 
					#define PLT1_VOL_DOWN		(PLT_HID_1_0_PAGE | PLT_VOL_DOWN)
 | 
				
			||||||
 | 
					#define PLT2_VOL_UP		(PLT_HID_2_0_PAGE | PLT_VOL_UP)
 | 
				
			||||||
 | 
					#define PLT2_VOL_DOWN		(PLT_HID_2_0_PAGE | PLT_VOL_DOWN)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define PLT_DA60		0xda60
 | 
				
			||||||
 | 
					#define PLT_BT300_MIN		0x0413
 | 
				
			||||||
 | 
					#define PLT_BT300_MAX		0x0418
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define PLT_ALLOW_CONSUMER (field->application == HID_CP_CONSUMERCONTROL && \
 | 
				
			||||||
 | 
								    (usage->hid & HID_USAGE_PAGE) == HID_UP_CONSUMER)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int plantronics_input_mapping(struct hid_device *hdev,
 | 
					static int plantronics_input_mapping(struct hid_device *hdev,
 | 
				
			||||||
				     struct hid_input *hi,
 | 
									     struct hid_input *hi,
 | 
				
			||||||
				     struct hid_field *field,
 | 
									     struct hid_field *field,
 | 
				
			||||||
				     struct hid_usage *usage,
 | 
									     struct hid_usage *usage,
 | 
				
			||||||
				     unsigned long **bit, int *max)
 | 
									     unsigned long **bit, int *max)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	if (field->application == HID_CP_CONSUMERCONTROL
 | 
						unsigned short mapped_key;
 | 
				
			||||||
	    && (usage->hid & HID_USAGE_PAGE) == HID_UP_CONSUMER) {
 | 
						unsigned long plt_type = (unsigned long)hid_get_drvdata(hdev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* handle volume up/down mapping */
 | 
				
			||||||
 | 
						/* non-standard types or multi-HID interfaces - plt_type is PID */
 | 
				
			||||||
 | 
						if (!(plt_type & HID_USAGE_PAGE)) {
 | 
				
			||||||
 | 
							switch (plt_type) {
 | 
				
			||||||
 | 
							case PLT_DA60:
 | 
				
			||||||
 | 
								if (PLT_ALLOW_CONSUMER)
 | 
				
			||||||
 | 
									goto defaulted;
 | 
				
			||||||
 | 
								goto ignored;
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								if (PLT_ALLOW_CONSUMER)
 | 
				
			||||||
 | 
									goto defaulted;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						/* handle standard types - plt_type is 0xffa0uuuu or 0xffa2uuuu */
 | 
				
			||||||
 | 
						/* 'basic telephony compliant' - allow default consumer page map */
 | 
				
			||||||
 | 
						else if ((plt_type & HID_USAGE) >= PLT_BASIC_TELEPHONY &&
 | 
				
			||||||
 | 
							 (plt_type & HID_USAGE) != PLT_BASIC_EXCEPTION) {
 | 
				
			||||||
 | 
							if (PLT_ALLOW_CONSUMER)
 | 
				
			||||||
 | 
								goto defaulted;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						/* not 'basic telephony' - apply legacy mapping */
 | 
				
			||||||
 | 
						/* only map if the field is in the device's primary vendor page */
 | 
				
			||||||
 | 
						else if (!((field->application ^ plt_type) & HID_USAGE_PAGE)) {
 | 
				
			||||||
 | 
							switch (usage->hid) {
 | 
				
			||||||
 | 
							case PLT1_VOL_UP:
 | 
				
			||||||
 | 
							case PLT2_VOL_UP:
 | 
				
			||||||
 | 
								mapped_key = KEY_VOLUMEUP;
 | 
				
			||||||
 | 
								goto mapped;
 | 
				
			||||||
 | 
							case PLT1_VOL_DOWN:
 | 
				
			||||||
 | 
							case PLT2_VOL_DOWN:
 | 
				
			||||||
 | 
								mapped_key = KEY_VOLUMEDOWN;
 | 
				
			||||||
 | 
								goto mapped;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Future mapping of call control or other usages,
 | 
				
			||||||
 | 
					 * if and when keys are defined would go here
 | 
				
			||||||
 | 
					 * otherwise, ignore everything else that was not mapped
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ignored:
 | 
				
			||||||
 | 
						return -1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					defaulted:
 | 
				
			||||||
	hid_dbg(hdev, "usage: %08x (appl: %08x) - defaulted\n",
 | 
						hid_dbg(hdev, "usage: %08x (appl: %08x) - defaulted\n",
 | 
				
			||||||
		usage->hid, field->application);
 | 
							usage->hid, field->application);
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					mapped:
 | 
				
			||||||
 | 
						hid_map_usage_clear(hi, usage, bit, max, EV_KEY, mapped_key);
 | 
				
			||||||
 | 
						hid_dbg(hdev, "usage: %08x (appl: %08x) - mapped to key %d\n",
 | 
				
			||||||
 | 
							usage->hid, field->application, mapped_key);
 | 
				
			||||||
 | 
						return 1;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	hid_dbg(hdev, "usage: %08x (appl: %08x) - ignored\n",
 | 
					static unsigned long plantronics_device_type(struct hid_device *hdev)
 | 
				
			||||||
		usage->hid, field->application);
 | 
					{
 | 
				
			||||||
 | 
						unsigned i, col_page;
 | 
				
			||||||
 | 
						unsigned long plt_type = hdev->product;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return -1;
 | 
						/* multi-HID interfaces? - plt_type is PID */
 | 
				
			||||||
 | 
						if (plt_type >= PLT_BT300_MIN && plt_type <= PLT_BT300_MAX)
 | 
				
			||||||
 | 
							goto exit;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* determine primary vendor page */
 | 
				
			||||||
 | 
						for (i = 0; i < hdev->maxcollection; i++) {
 | 
				
			||||||
 | 
							col_page = hdev->collection[i].usage & HID_USAGE_PAGE;
 | 
				
			||||||
 | 
							if (col_page == PLT_HID_2_0_PAGE) {
 | 
				
			||||||
 | 
								plt_type = hdev->collection[i].usage;
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if (col_page == PLT_HID_1_0_PAGE)
 | 
				
			||||||
 | 
								plt_type = hdev->collection[i].usage;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					exit:
 | 
				
			||||||
 | 
						hid_dbg(hdev, "plt_type decoded as: %08lx\n", plt_type);
 | 
				
			||||||
 | 
						return plt_type;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int plantronics_probe(struct hid_device *hdev,
 | 
				
			||||||
 | 
								     const struct hid_device_id *id)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ret = hid_parse(hdev);
 | 
				
			||||||
 | 
						if (ret) {
 | 
				
			||||||
 | 
							hid_err(hdev, "parse failed\n");
 | 
				
			||||||
 | 
							goto err;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						hid_set_drvdata(hdev, (void *)plantronics_device_type(hdev));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT |
 | 
				
			||||||
 | 
							HID_CONNECT_HIDINPUT_FORCE | HID_CONNECT_HIDDEV_FORCE);
 | 
				
			||||||
 | 
						if (ret)
 | 
				
			||||||
 | 
							hid_err(hdev, "hw start failed\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					err:
 | 
				
			||||||
 | 
						return ret;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static const struct hid_device_id plantronics_devices[] = {
 | 
					static const struct hid_device_id plantronics_devices[] = {
 | 
				
			||||||
| 
						 | 
					@ -46,6 +161,7 @@ static struct hid_driver plantronics_driver = {
 | 
				
			||||||
	.name = "plantronics",
 | 
						.name = "plantronics",
 | 
				
			||||||
	.id_table = plantronics_devices,
 | 
						.id_table = plantronics_devices,
 | 
				
			||||||
	.input_mapping = plantronics_input_mapping,
 | 
						.input_mapping = plantronics_input_mapping,
 | 
				
			||||||
 | 
						.probe = plantronics_probe,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
module_hid_driver(plantronics_driver);
 | 
					module_hid_driver(plantronics_driver);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue