162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  Plantronics USB HID Driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  Copyright (c) 2014 JD Cole <jd.cole@plantronics.com>
662306a36Sopenharmony_ci *  Copyright (c) 2015-2018 Terry Junge <terry.junge@plantronics.com>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci/*
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include "hid-ids.h"
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include <linux/hid.h>
1562306a36Sopenharmony_ci#include <linux/module.h>
1662306a36Sopenharmony_ci#include <linux/jiffies.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#define PLT_HID_1_0_PAGE	0xffa00000
1962306a36Sopenharmony_ci#define PLT_HID_2_0_PAGE	0xffa20000
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#define PLT_BASIC_TELEPHONY	0x0003
2262306a36Sopenharmony_ci#define PLT_BASIC_EXCEPTION	0x0005
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#define PLT_VOL_UP		0x00b1
2562306a36Sopenharmony_ci#define PLT_VOL_DOWN		0x00b2
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#define PLT1_VOL_UP		(PLT_HID_1_0_PAGE | PLT_VOL_UP)
2862306a36Sopenharmony_ci#define PLT1_VOL_DOWN		(PLT_HID_1_0_PAGE | PLT_VOL_DOWN)
2962306a36Sopenharmony_ci#define PLT2_VOL_UP		(PLT_HID_2_0_PAGE | PLT_VOL_UP)
3062306a36Sopenharmony_ci#define PLT2_VOL_DOWN		(PLT_HID_2_0_PAGE | PLT_VOL_DOWN)
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci#define PLT_DA60		0xda60
3362306a36Sopenharmony_ci#define PLT_BT300_MIN		0x0413
3462306a36Sopenharmony_ci#define PLT_BT300_MAX		0x0418
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#define PLT_ALLOW_CONSUMER (field->application == HID_CP_CONSUMERCONTROL && \
3862306a36Sopenharmony_ci			    (usage->hid & HID_USAGE_PAGE) == HID_UP_CONSUMER)
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci#define PLT_QUIRK_DOUBLE_VOLUME_KEYS BIT(0)
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci#define PLT_DOUBLE_KEY_TIMEOUT 5 /* ms */
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistruct plt_drv_data {
4562306a36Sopenharmony_ci	unsigned long device_type;
4662306a36Sopenharmony_ci	unsigned long last_volume_key_ts;
4762306a36Sopenharmony_ci	u32 quirks;
4862306a36Sopenharmony_ci};
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_cistatic int plantronics_input_mapping(struct hid_device *hdev,
5162306a36Sopenharmony_ci				     struct hid_input *hi,
5262306a36Sopenharmony_ci				     struct hid_field *field,
5362306a36Sopenharmony_ci				     struct hid_usage *usage,
5462306a36Sopenharmony_ci				     unsigned long **bit, int *max)
5562306a36Sopenharmony_ci{
5662306a36Sopenharmony_ci	unsigned short mapped_key;
5762306a36Sopenharmony_ci	struct plt_drv_data *drv_data = hid_get_drvdata(hdev);
5862306a36Sopenharmony_ci	unsigned long plt_type = drv_data->device_type;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	/* special case for PTT products */
6162306a36Sopenharmony_ci	if (field->application == HID_GD_JOYSTICK)
6262306a36Sopenharmony_ci		goto defaulted;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	/* handle volume up/down mapping */
6562306a36Sopenharmony_ci	/* non-standard types or multi-HID interfaces - plt_type is PID */
6662306a36Sopenharmony_ci	if (!(plt_type & HID_USAGE_PAGE)) {
6762306a36Sopenharmony_ci		switch (plt_type) {
6862306a36Sopenharmony_ci		case PLT_DA60:
6962306a36Sopenharmony_ci			if (PLT_ALLOW_CONSUMER)
7062306a36Sopenharmony_ci				goto defaulted;
7162306a36Sopenharmony_ci			goto ignored;
7262306a36Sopenharmony_ci		default:
7362306a36Sopenharmony_ci			if (PLT_ALLOW_CONSUMER)
7462306a36Sopenharmony_ci				goto defaulted;
7562306a36Sopenharmony_ci		}
7662306a36Sopenharmony_ci	}
7762306a36Sopenharmony_ci	/* handle standard types - plt_type is 0xffa0uuuu or 0xffa2uuuu */
7862306a36Sopenharmony_ci	/* 'basic telephony compliant' - allow default consumer page map */
7962306a36Sopenharmony_ci	else if ((plt_type & HID_USAGE) >= PLT_BASIC_TELEPHONY &&
8062306a36Sopenharmony_ci		 (plt_type & HID_USAGE) != PLT_BASIC_EXCEPTION) {
8162306a36Sopenharmony_ci		if (PLT_ALLOW_CONSUMER)
8262306a36Sopenharmony_ci			goto defaulted;
8362306a36Sopenharmony_ci	}
8462306a36Sopenharmony_ci	/* not 'basic telephony' - apply legacy mapping */
8562306a36Sopenharmony_ci	/* only map if the field is in the device's primary vendor page */
8662306a36Sopenharmony_ci	else if (!((field->application ^ plt_type) & HID_USAGE_PAGE)) {
8762306a36Sopenharmony_ci		switch (usage->hid) {
8862306a36Sopenharmony_ci		case PLT1_VOL_UP:
8962306a36Sopenharmony_ci		case PLT2_VOL_UP:
9062306a36Sopenharmony_ci			mapped_key = KEY_VOLUMEUP;
9162306a36Sopenharmony_ci			goto mapped;
9262306a36Sopenharmony_ci		case PLT1_VOL_DOWN:
9362306a36Sopenharmony_ci		case PLT2_VOL_DOWN:
9462306a36Sopenharmony_ci			mapped_key = KEY_VOLUMEDOWN;
9562306a36Sopenharmony_ci			goto mapped;
9662306a36Sopenharmony_ci		}
9762306a36Sopenharmony_ci	}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci/*
10062306a36Sopenharmony_ci * Future mapping of call control or other usages,
10162306a36Sopenharmony_ci * if and when keys are defined would go here
10262306a36Sopenharmony_ci * otherwise, ignore everything else that was not mapped
10362306a36Sopenharmony_ci */
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ciignored:
10662306a36Sopenharmony_ci	return -1;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_cidefaulted:
10962306a36Sopenharmony_ci	hid_dbg(hdev, "usage: %08x (appl: %08x) - defaulted\n",
11062306a36Sopenharmony_ci		usage->hid, field->application);
11162306a36Sopenharmony_ci	return 0;
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_cimapped:
11462306a36Sopenharmony_ci	hid_map_usage_clear(hi, usage, bit, max, EV_KEY, mapped_key);
11562306a36Sopenharmony_ci	hid_dbg(hdev, "usage: %08x (appl: %08x) - mapped to key %d\n",
11662306a36Sopenharmony_ci		usage->hid, field->application, mapped_key);
11762306a36Sopenharmony_ci	return 1;
11862306a36Sopenharmony_ci}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_cistatic int plantronics_event(struct hid_device *hdev, struct hid_field *field,
12162306a36Sopenharmony_ci			     struct hid_usage *usage, __s32 value)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	struct plt_drv_data *drv_data = hid_get_drvdata(hdev);
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	if (drv_data->quirks & PLT_QUIRK_DOUBLE_VOLUME_KEYS) {
12662306a36Sopenharmony_ci		unsigned long prev_ts, cur_ts;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci		/* Usages are filtered in plantronics_usages. */
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci		if (!value) /* Handle key presses only. */
13162306a36Sopenharmony_ci			return 0;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci		prev_ts = drv_data->last_volume_key_ts;
13462306a36Sopenharmony_ci		cur_ts = jiffies;
13562306a36Sopenharmony_ci		if (jiffies_to_msecs(cur_ts - prev_ts) <= PLT_DOUBLE_KEY_TIMEOUT)
13662306a36Sopenharmony_ci			return 1; /* Ignore the repeated key. */
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci		drv_data->last_volume_key_ts = cur_ts;
13962306a36Sopenharmony_ci	}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	return 0;
14262306a36Sopenharmony_ci}
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_cistatic unsigned long plantronics_device_type(struct hid_device *hdev)
14562306a36Sopenharmony_ci{
14662306a36Sopenharmony_ci	unsigned i, col_page;
14762306a36Sopenharmony_ci	unsigned long plt_type = hdev->product;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	/* multi-HID interfaces? - plt_type is PID */
15062306a36Sopenharmony_ci	if (plt_type >= PLT_BT300_MIN && plt_type <= PLT_BT300_MAX)
15162306a36Sopenharmony_ci		goto exit;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	/* determine primary vendor page */
15462306a36Sopenharmony_ci	for (i = 0; i < hdev->maxcollection; i++) {
15562306a36Sopenharmony_ci		col_page = hdev->collection[i].usage & HID_USAGE_PAGE;
15662306a36Sopenharmony_ci		if (col_page == PLT_HID_2_0_PAGE) {
15762306a36Sopenharmony_ci			plt_type = hdev->collection[i].usage;
15862306a36Sopenharmony_ci			break;
15962306a36Sopenharmony_ci		}
16062306a36Sopenharmony_ci		if (col_page == PLT_HID_1_0_PAGE)
16162306a36Sopenharmony_ci			plt_type = hdev->collection[i].usage;
16262306a36Sopenharmony_ci	}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ciexit:
16562306a36Sopenharmony_ci	hid_dbg(hdev, "plt_type decoded as: %08lx\n", plt_type);
16662306a36Sopenharmony_ci	return plt_type;
16762306a36Sopenharmony_ci}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_cistatic int plantronics_probe(struct hid_device *hdev,
17062306a36Sopenharmony_ci			     const struct hid_device_id *id)
17162306a36Sopenharmony_ci{
17262306a36Sopenharmony_ci	struct plt_drv_data *drv_data;
17362306a36Sopenharmony_ci	int ret;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	drv_data = devm_kzalloc(&hdev->dev, sizeof(*drv_data), GFP_KERNEL);
17662306a36Sopenharmony_ci	if (!drv_data)
17762306a36Sopenharmony_ci		return -ENOMEM;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	ret = hid_parse(hdev);
18062306a36Sopenharmony_ci	if (ret) {
18162306a36Sopenharmony_ci		hid_err(hdev, "parse failed\n");
18262306a36Sopenharmony_ci		goto err;
18362306a36Sopenharmony_ci	}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	drv_data->device_type = plantronics_device_type(hdev);
18662306a36Sopenharmony_ci	drv_data->quirks = id->driver_data;
18762306a36Sopenharmony_ci	drv_data->last_volume_key_ts = jiffies - msecs_to_jiffies(PLT_DOUBLE_KEY_TIMEOUT);
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	hid_set_drvdata(hdev, drv_data);
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT |
19262306a36Sopenharmony_ci		HID_CONNECT_HIDINPUT_FORCE | HID_CONNECT_HIDDEV_FORCE);
19362306a36Sopenharmony_ci	if (ret)
19462306a36Sopenharmony_ci		hid_err(hdev, "hw start failed\n");
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_cierr:
19762306a36Sopenharmony_ci	return ret;
19862306a36Sopenharmony_ci}
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_cistatic const struct hid_device_id plantronics_devices[] = {
20162306a36Sopenharmony_ci	{ HID_USB_DEVICE(USB_VENDOR_ID_PLANTRONICS,
20262306a36Sopenharmony_ci					 USB_DEVICE_ID_PLANTRONICS_BLACKWIRE_3210_SERIES),
20362306a36Sopenharmony_ci		.driver_data = PLT_QUIRK_DOUBLE_VOLUME_KEYS },
20462306a36Sopenharmony_ci	{ HID_USB_DEVICE(USB_VENDOR_ID_PLANTRONICS,
20562306a36Sopenharmony_ci					 USB_DEVICE_ID_PLANTRONICS_BLACKWIRE_3220_SERIES),
20662306a36Sopenharmony_ci		.driver_data = PLT_QUIRK_DOUBLE_VOLUME_KEYS },
20762306a36Sopenharmony_ci	{ HID_USB_DEVICE(USB_VENDOR_ID_PLANTRONICS,
20862306a36Sopenharmony_ci					 USB_DEVICE_ID_PLANTRONICS_BLACKWIRE_3215_SERIES),
20962306a36Sopenharmony_ci		.driver_data = PLT_QUIRK_DOUBLE_VOLUME_KEYS },
21062306a36Sopenharmony_ci	{ HID_USB_DEVICE(USB_VENDOR_ID_PLANTRONICS,
21162306a36Sopenharmony_ci					 USB_DEVICE_ID_PLANTRONICS_BLACKWIRE_3225_SERIES),
21262306a36Sopenharmony_ci		.driver_data = PLT_QUIRK_DOUBLE_VOLUME_KEYS },
21362306a36Sopenharmony_ci	{ HID_USB_DEVICE(USB_VENDOR_ID_PLANTRONICS, HID_ANY_ID) },
21462306a36Sopenharmony_ci	{ }
21562306a36Sopenharmony_ci};
21662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(hid, plantronics_devices);
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_cistatic const struct hid_usage_id plantronics_usages[] = {
21962306a36Sopenharmony_ci	{ HID_CP_VOLUMEUP, EV_KEY, HID_ANY_ID },
22062306a36Sopenharmony_ci	{ HID_CP_VOLUMEDOWN, EV_KEY, HID_ANY_ID },
22162306a36Sopenharmony_ci	{ HID_TERMINATOR, HID_TERMINATOR, HID_TERMINATOR }
22262306a36Sopenharmony_ci};
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_cistatic struct hid_driver plantronics_driver = {
22562306a36Sopenharmony_ci	.name = "plantronics",
22662306a36Sopenharmony_ci	.id_table = plantronics_devices,
22762306a36Sopenharmony_ci	.usage_table = plantronics_usages,
22862306a36Sopenharmony_ci	.input_mapping = plantronics_input_mapping,
22962306a36Sopenharmony_ci	.event = plantronics_event,
23062306a36Sopenharmony_ci	.probe = plantronics_probe,
23162306a36Sopenharmony_ci};
23262306a36Sopenharmony_cimodule_hid_driver(plantronics_driver);
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ciMODULE_AUTHOR("JD Cole <jd.cole@plantronics.com>");
23562306a36Sopenharmony_ciMODULE_AUTHOR("Terry Junge <terry.junge@plantronics.com>");
23662306a36Sopenharmony_ciMODULE_DESCRIPTION("Plantronics USB HID Driver");
23762306a36Sopenharmony_ciMODULE_LICENSE("GPL");
238