Commit 179023e6 authored by Jiri Kosina's avatar Jiri Kosina

Merge branches 'for-4.8/upstream-fixes', 'for-4.9/alps', 'for-4.9/hid-input',...

Merge branches 'for-4.8/upstream-fixes', 'for-4.9/alps', 'for-4.9/hid-input', 'for-4.9/intel-ish', 'for-4.9/kye-uclogic-waltop-fixes', 'for-4.9/logitech', 'for-4.9/sony', 'for-4.9/upstream' and 'for-4.9/wacom' into for-linus
...@@ -35,6 +35,12 @@ Description: Displays a set of alternate modes supported by a wheel. Each ...@@ -35,6 +35,12 @@ Description: Displays a set of alternate modes supported by a wheel. Each
DF-EX <*--------> G25 <-> G27 DF-EX <*--------> G25 <-> G27
DF-EX <*----------------> G27 DF-EX <*----------------> G27
G29:
DF-EX <*> DFP <-> G25 <-> G27 <-> G29
DF-EX <*--------> G25 <-> G27 <-> G29
DF-EX <*----------------> G27 <-> G29
DF-EX <*------------------------> G29
DFGT: DFGT:
DF-EX <*> DFP <-> DFGT DF-EX <*> DFP <-> DFGT
DF-EX <*--------> DFGT DF-EX <*--------> DFGT
...@@ -50,3 +56,12 @@ Description: Displays the real model of the wheel regardless of any ...@@ -50,3 +56,12 @@ Description: Displays the real model of the wheel regardless of any
alternate mode the wheel might be switched to. alternate mode the wheel might be switched to.
It is a read-only value. It is a read-only value.
This entry is not created for devices that have only one mode. This entry is not created for devices that have only one mode.
What: /sys/bus/hid/drivers/logitech/<dev>/combine_pedals
Date: Sep 2016
KernelVersion: 4.9
Contact: Simon Wood <simon@mungewell.org>
Description: Controls whether a combined value of accelerator and brake is
reported on the Y axis of the controller. Useful for older games
which can do not work with separate accelerator/brake axis.
Off ('0') by default, enabled by setting '1'.
...@@ -24,6 +24,7 @@ What: /sys/bus/hid/devices/<bus>:<vid>:<pid>.<n>/wacom_led/status0_luminance ...@@ -24,6 +24,7 @@ What: /sys/bus/hid/devices/<bus>:<vid>:<pid>.<n>/wacom_led/status0_luminance
Date: August 2014 Date: August 2014
Contact: linux-input@vger.kernel.org Contact: linux-input@vger.kernel.org
Description: Description:
<obsoleted by the LED class API now exported by the driver>
Writing to this file sets the status LED luminance (1..127) Writing to this file sets the status LED luminance (1..127)
when the stylus does not touch the tablet surface, and no when the stylus does not touch the tablet surface, and no
button is pressed on the stylus. This luminance level is button is pressed on the stylus. This luminance level is
...@@ -33,6 +34,7 @@ What: /sys/bus/hid/devices/<bus>:<vid>:<pid>.<n>/wacom_led/status1_luminance ...@@ -33,6 +34,7 @@ What: /sys/bus/hid/devices/<bus>:<vid>:<pid>.<n>/wacom_led/status1_luminance
Date: August 2014 Date: August 2014
Contact: linux-input@vger.kernel.org Contact: linux-input@vger.kernel.org
Description: Description:
<obsoleted by the LED class API now exported by the driver>
Writing to this file sets the status LED luminance (1..127) Writing to this file sets the status LED luminance (1..127)
when the stylus touches the tablet surface, or any button is when the stylus touches the tablet surface, or any button is
pressed on the stylus. pressed on the stylus.
...@@ -41,6 +43,7 @@ What: /sys/bus/hid/devices/<bus>:<vid>:<pid>.<n>/wacom_led/status_led0_select ...@@ -41,6 +43,7 @@ What: /sys/bus/hid/devices/<bus>:<vid>:<pid>.<n>/wacom_led/status_led0_select
Date: August 2014 Date: August 2014
Contact: linux-input@vger.kernel.org Contact: linux-input@vger.kernel.org
Description: Description:
<obsoleted by the LED class API now exported by the driver>
Writing to this file sets which one of the four (for Intuos 4 Writing to this file sets which one of the four (for Intuos 4
and Intuos 5) or of the right four (for Cintiq 21UX2 and Cintiq and Intuos 5) or of the right four (for Cintiq 21UX2 and Cintiq
24HD) status LEDs is active (0..3). The other three LEDs on the 24HD) status LEDs is active (0..3). The other three LEDs on the
...@@ -50,6 +53,7 @@ What: /sys/bus/hid/devices/<bus>:<vid>:<pid>.<n>/wacom_led/status_led1_select ...@@ -50,6 +53,7 @@ What: /sys/bus/hid/devices/<bus>:<vid>:<pid>.<n>/wacom_led/status_led1_select
Date: August 2014 Date: August 2014
Contact: linux-input@vger.kernel.org Contact: linux-input@vger.kernel.org
Description: Description:
<obsoleted by the LED class API now exported by the driver>
Writing to this file sets which one of the left four (for Cintiq 21UX2 Writing to this file sets which one of the left four (for Cintiq 21UX2
and Cintiq 24HD) status LEDs is active (0..3). The other three LEDs on and Cintiq 24HD) status LEDs is active (0..3). The other three LEDs on
the left are always inactive. the left are always inactive.
...@@ -91,6 +95,7 @@ What: /sys/bus/hid/devices/<bus>:<vid>:<pid>.<n>/wacom_remote/<serial_number>/r ...@@ -91,6 +95,7 @@ What: /sys/bus/hid/devices/<bus>:<vid>:<pid>.<n>/wacom_remote/<serial_number>/r
Date: July 2015 Date: July 2015
Contact: linux-input@vger.kernel.org Contact: linux-input@vger.kernel.org
Description: Description:
<obsoleted by the LED class API now exported by the driver>
Reading from this file reports the mode status of the Reading from this file reports the mode status of the
remote as indicated by the LED lights on the device. If no remote as indicated by the LED lights on the device. If no
reports have been received from the paired device, reading reports have been received from the paired device, reading
......
This diff is collapsed.
...@@ -5992,6 +5992,13 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/lenb/linux.git ...@@ -5992,6 +5992,13 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/lenb/linux.git
S: Supported S: Supported
F: drivers/idle/intel_idle.c F: drivers/idle/intel_idle.c
INTEL INTEGRATED SENSOR HUB DRIVER
M: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
M: Jiri Kosina <jikos@kernel.org>
L: linux-input@vger.kernel.org
S: Maintained
F: drivers/hid/intel-ish-hid/
INTEL PSTATE DRIVER INTEL PSTATE DRIVER
M: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com> M: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
M: Len Brown <lenb@kernel.org> M: Len Brown <lenb@kernel.org>
......
...@@ -457,8 +457,6 @@ config LOGITECH_FF ...@@ -457,8 +457,6 @@ config LOGITECH_FF
- Logitech WingMan Cordless RumblePad - Logitech WingMan Cordless RumblePad
- Logitech WingMan Cordless RumblePad 2 - Logitech WingMan Cordless RumblePad 2
- Logitech WingMan Force 3D - Logitech WingMan Force 3D
- Logitech Formula Force EX
- Logitech WingMan Formula Force GP
and if you want to enable force feedback for them. and if you want to enable force feedback for them.
Note: if you say N here, this device will still be supported, but without Note: if you say N here, this device will still be supported, but without
...@@ -488,15 +486,22 @@ config LOGIWHEELS_FF ...@@ -488,15 +486,22 @@ config LOGIWHEELS_FF
select INPUT_FF_MEMLESS select INPUT_FF_MEMLESS
default LOGITECH_FF default LOGITECH_FF
help help
Say Y here if you want to enable force feedback and range setting Say Y here if you want to enable force feedback and range setting(*)
support for following Logitech wheels: support for following Logitech wheels:
- Logitech G25 (*)
- Logitech G27 (*)
- Logitech G29 (*)
- Logitech Driving Force - Logitech Driving Force
- Logitech Driving Force Pro - Logitech Driving Force Pro (*)
- Logitech Driving Force GT - Logitech Driving Force GT (*)
- Logitech G25 - Logitech Driving Force EX/RX
- Logitech G27 - Logitech Driving Force Wireless
- Logitech MOMO/MOMO 2 - Logitech Speed Force Wireless
- Logitech Formula Force EX - Logitech MOMO Force
- Logitech MOMO Racing Force
- Logitech Formula Force GP
- Logitech Formula Force EX/RX
- Logitech Wingman Formula Force GP
config HID_MAGICMOUSE config HID_MAGICMOUSE
tristate "Apple Magic Mouse/Trackpad multi-touch support" tristate "Apple Magic Mouse/Trackpad multi-touch support"
...@@ -862,6 +867,7 @@ config HID_WACOM ...@@ -862,6 +867,7 @@ config HID_WACOM
select POWER_SUPPLY select POWER_SUPPLY
select NEW_LEDS select NEW_LEDS
select LEDS_CLASS select LEDS_CLASS
select LEDS_TRIGGERS
help help
Say Y here if you want to use the USB or BT version of the Wacom Intuos Say Y here if you want to use the USB or BT version of the Wacom Intuos
or Graphire tablet. or Graphire tablet.
...@@ -967,4 +973,6 @@ source "drivers/hid/usbhid/Kconfig" ...@@ -967,4 +973,6 @@ source "drivers/hid/usbhid/Kconfig"
source "drivers/hid/i2c-hid/Kconfig" source "drivers/hid/i2c-hid/Kconfig"
source "drivers/hid/intel-ish-hid/Kconfig"
endmenu endmenu
...@@ -113,3 +113,5 @@ obj-$(CONFIG_USB_MOUSE) += usbhid/ ...@@ -113,3 +113,5 @@ obj-$(CONFIG_USB_MOUSE) += usbhid/
obj-$(CONFIG_USB_KBD) += usbhid/ obj-$(CONFIG_USB_KBD) += usbhid/
obj-$(CONFIG_I2C_HID) += i2c-hid/ obj-$(CONFIG_I2C_HID) += i2c-hid/
obj-$(CONFIG_INTEL_ISH_HID) += intel-ish-hid/
...@@ -191,16 +191,16 @@ static int alps_raw_event(struct hid_device *hdev, ...@@ -191,16 +191,16 @@ static int alps_raw_event(struct hid_device *hdev,
if (z != 0) { if (z != 0) {
input_mt_report_slot_state(hdata->input, input_mt_report_slot_state(hdata->input,
MT_TOOL_FINGER, 1); MT_TOOL_FINGER, 1);
input_report_abs(hdata->input,
ABS_MT_POSITION_X, x);
input_report_abs(hdata->input,
ABS_MT_POSITION_Y, y);
input_report_abs(hdata->input,
ABS_MT_PRESSURE, z);
} else { } else {
input_mt_report_slot_state(hdata->input, input_mt_report_slot_state(hdata->input,
MT_TOOL_FINGER, 0); MT_TOOL_FINGER, 0);
break;
} }
input_report_abs(hdata->input, ABS_MT_POSITION_X, x);
input_report_abs(hdata->input, ABS_MT_POSITION_Y, y);
input_report_abs(hdata->input, ABS_MT_PRESSURE, z);
} }
input_mt_sync_frame(hdata->input); input_mt_sync_frame(hdata->input);
...@@ -384,7 +384,7 @@ static int alps_input_configured(struct hid_device *hdev, struct hid_input *hi) ...@@ -384,7 +384,7 @@ static int alps_input_configured(struct hid_device *hdev, struct hid_input *hi)
input2 = input_allocate_device(); input2 = input_allocate_device();
if (!input2) { if (!input2) {
input_free_device(input2); ret = -ENOMEM;
goto exit; goto exit;
} }
...@@ -426,7 +426,8 @@ static int alps_input_configured(struct hid_device *hdev, struct hid_input *hi) ...@@ -426,7 +426,8 @@ static int alps_input_configured(struct hid_device *hdev, struct hid_input *hi)
__set_bit(INPUT_PROP_POINTER, input2->propbit); __set_bit(INPUT_PROP_POINTER, input2->propbit);
__set_bit(INPUT_PROP_POINTING_STICK, input2->propbit); __set_bit(INPUT_PROP_POINTING_STICK, input2->propbit);
if (input_register_device(data->input2)) { ret = input_register_device(data->input2);
if (ret) {
input_free_device(input2); input_free_device(input2);
goto exit; goto exit;
} }
......
...@@ -1917,7 +1917,7 @@ static const struct hid_device_id hid_have_special_driver[] = { ...@@ -1917,7 +1917,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_ERGO_525V) }, { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_ERGO_525V) },
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_I405X) }, { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_I405X) },
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_I608X) }, { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_I608X) },
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_I608X_2) }, { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_I608X_V2) },
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_M610X) }, { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_M610X) },
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_PENSKETCH_M912) }, { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_PENSKETCH_M912) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LABTEC, USB_DEVICE_ID_LABTEC_WIRELESS_KEYBOARD) }, { HID_USB_DEVICE(USB_VENDOR_ID_LABTEC, USB_DEVICE_ID_LABTEC_WIRELESS_KEYBOARD) },
...@@ -2086,6 +2086,11 @@ static const struct hid_device_id hid_have_special_driver[] = { ...@@ -2086,6 +2086,11 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP1062) }, { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP1062) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850) }, { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60) }, { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_YIYNOVA_TABLET) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UGEE_TABLET_81) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UGEE_TABLET_45) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UGTIZER, USB_DEVICE_ID_UGTIZER_TABLET_GP0610) },
{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_SMARTJOY_PLUS) }, { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_SMARTJOY_PLUS) },
{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_SUPER_JOY_BOX_3) }, { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_SUPER_JOY_BOX_3) },
{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_DUAL_USB_JOYPAD) }, { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_DUAL_USB_JOYPAD) },
...@@ -2483,7 +2488,7 @@ static const struct hid_device_id hid_ignore_list[] = { ...@@ -2483,7 +2488,7 @@ static const struct hid_device_id hid_ignore_list[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_PANJIT, 0x0004) }, { HID_USB_DEVICE(USB_VENDOR_ID_PANJIT, 0x0004) },
{ HID_USB_DEVICE(USB_VENDOR_ID_PHILIPS, USB_DEVICE_ID_PHILIPS_IEEE802154_DONGLE) }, { HID_USB_DEVICE(USB_VENDOR_ID_PHILIPS, USB_DEVICE_ID_PHILIPS_IEEE802154_DONGLE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_POWERCOM, USB_DEVICE_ID_POWERCOM_UPS) }, { HID_USB_DEVICE(USB_VENDOR_ID_POWERCOM, USB_DEVICE_ID_POWERCOM_UPS) },
#if defined(CONFIG_MOUSE_SYNAPTICS_USB) || defined(CONFIG_MOUSE_SYNAPTICS_USB_MODULE) #if IS_ENABLED(CONFIG_MOUSE_SYNAPTICS_USB)
{ HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_TP) }, { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_TP) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_INT_TP) }, { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_INT_TP) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_CPAD) }, { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_CPAD) },
......
...@@ -268,6 +268,7 @@ ...@@ -268,6 +268,7 @@
#define USB_DEVICE_ID_CORSAIR_K95RGB 0x1b11 #define USB_DEVICE_ID_CORSAIR_K95RGB 0x1b11
#define USB_DEVICE_ID_CORSAIR_M65RGB 0x1b12 #define USB_DEVICE_ID_CORSAIR_M65RGB 0x1b12
#define USB_DEVICE_ID_CORSAIR_K70RGB 0x1b13 #define USB_DEVICE_ID_CORSAIR_K70RGB 0x1b13
#define USB_DEVICE_ID_CORSAIR_STRAFE 0x1b15
#define USB_DEVICE_ID_CORSAIR_K65RGB 0x1b17 #define USB_DEVICE_ID_CORSAIR_K65RGB 0x1b17
#define USB_VENDOR_ID_CREATIVELABS 0x041e #define USB_VENDOR_ID_CREATIVELABS 0x041e
...@@ -565,7 +566,7 @@ ...@@ -565,7 +566,7 @@
#define USB_DEVICE_ID_KYE_GPEN_560 0x5003 #define USB_DEVICE_ID_KYE_GPEN_560 0x5003
#define USB_DEVICE_ID_KYE_EASYPEN_I405X 0x5010 #define USB_DEVICE_ID_KYE_EASYPEN_I405X 0x5010
#define USB_DEVICE_ID_KYE_MOUSEPEN_I608X 0x5011 #define USB_DEVICE_ID_KYE_MOUSEPEN_I608X 0x5011
#define USB_DEVICE_ID_KYE_MOUSEPEN_I608X_2 0x501a #define USB_DEVICE_ID_KYE_MOUSEPEN_I608X_V2 0x501a
#define USB_DEVICE_ID_KYE_EASYPEN_M610X 0x5013 #define USB_DEVICE_ID_KYE_EASYPEN_M610X 0x5013
#define USB_DEVICE_ID_KYE_PENSKETCH_M912 0x5015 #define USB_DEVICE_ID_KYE_PENSKETCH_M912 0x5015
...@@ -998,6 +999,10 @@ ...@@ -998,6 +999,10 @@
#define USB_DEVICE_ID_UCLOGIC_TABLET_WP1062 0x0064 #define USB_DEVICE_ID_UCLOGIC_TABLET_WP1062 0x0064
#define USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850 0x0522 #define USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850 0x0522
#define USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60 0x0781 #define USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60 0x0781
#define USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3 0x3031
#define USB_DEVICE_ID_UGEE_TABLET_81 0x0081
#define USB_DEVICE_ID_UGEE_TABLET_45 0x0045
#define USB_DEVICE_ID_YIYNOVA_TABLET 0x004d
#define USB_VENDOR_ID_UNITEC 0x227d #define USB_VENDOR_ID_UNITEC 0x227d
#define USB_DEVICE_ID_UNITEC_USB_TOUCH_0709 0x0709 #define USB_DEVICE_ID_UNITEC_USB_TOUCH_0709 0x0709
...@@ -1087,4 +1092,7 @@ ...@@ -1087,4 +1092,7 @@
#define USB_DEVICE_ID_RAPHNET_2NES2SNES 0x0002 #define USB_DEVICE_ID_RAPHNET_2NES2SNES 0x0002
#define USB_DEVICE_ID_RAPHNET_4NES4SNES 0x0003 #define USB_DEVICE_ID_RAPHNET_4NES4SNES 0x0003
#define USB_VENDOR_ID_UGTIZER 0x2179
#define USB_DEVICE_ID_UGTIZER_TABLET_GP0610 0x0053
#endif #endif
...@@ -604,6 +604,15 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel ...@@ -604,6 +604,15 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
break; break;
} }
/*
* Some lazy vendors declare 255 usages for System Control,
* leading to the creation of ABS_X|Y axis and too many others.
* It wouldn't be a problem if joydev doesn't consider the
* device as a joystick then.
*/
if (field->application == HID_GD_SYSTEM_CONTROL)
goto ignore;
if ((usage->hid & 0xf0) == 0x90) { /* D-pad */ if ((usage->hid & 0xf0) == 0x90) { /* D-pad */
switch (usage->hid) { switch (usage->hid) {
case HID_GD_UP: usage->hat_dir = 1; break; case HID_GD_UP: usage->hat_dir = 1; break;
......
...@@ -19,11 +19,6 @@ ...@@ -19,11 +19,6 @@
#include "hid-ids.h" #include "hid-ids.h"
/*
* See EasyPen i405X description, device and HID report descriptors at
* http://sf.net/apps/mediawiki/digimend/?title=KYE_EasyPen_i405X
*/
/* Original EasyPen i405X report descriptor size */ /* Original EasyPen i405X report descriptor size */
#define EASYPEN_I405X_RDESC_ORIG_SIZE 476 #define EASYPEN_I405X_RDESC_ORIG_SIZE 476
...@@ -82,11 +77,6 @@ static __u8 easypen_i405x_rdesc_fixed[] = { ...@@ -82,11 +77,6 @@ static __u8 easypen_i405x_rdesc_fixed[] = {
0xC0 /* End Collection */ 0xC0 /* End Collection */
}; };
/*
* See MousePen i608X description, device and HID report descriptors at
* http://sf.net/apps/mediawiki/digimend/?title=KYE_MousePen_i608X
*/
/* Original MousePen i608X report descriptor size */ /* Original MousePen i608X report descriptor size */
#define MOUSEPEN_I608X_RDESC_ORIG_SIZE 476 #define MOUSEPEN_I608X_RDESC_ORIG_SIZE 476
...@@ -186,10 +176,104 @@ static __u8 mousepen_i608x_rdesc_fixed[] = { ...@@ -186,10 +176,104 @@ static __u8 mousepen_i608x_rdesc_fixed[] = {
0xC0 /* End Collection */ 0xC0 /* End Collection */
}; };
/* /* Original MousePen i608X v2 report descriptor size */
* See EasyPen M610X description, device and HID report descriptors at #define MOUSEPEN_I608X_V2_RDESC_ORIG_SIZE 482
* http://sf.net/apps/mediawiki/digimend/?title=KYE_EasyPen_M610X
*/ /* Fixed MousePen i608X v2 report descriptor */
static __u8 mousepen_i608x_v2_rdesc_fixed[] = {
0x06, 0x00, 0xFF, /* Usage Page (FF00h), */
0x09, 0x01, /* Usage (01h), */
0xA1, 0x01, /* Collection (Application), */
0x85, 0x05, /* Report ID (5), */
0x09, 0x01, /* Usage (01h), */
0x15, 0x80, /* Logical Minimum (-128), */
0x25, 0x7F, /* Logical Maximum (127), */
0x75, 0x08, /* Report Size (8), */
0x95, 0x07, /* Report Count (7), */
0xB1, 0x02, /* Feature (Variable), */
0xC0, /* End Collection, */
0x05, 0x0D, /* Usage Page (Digitizer), */
0x09, 0x02, /* Usage (Pen), */
0xA1, 0x01, /* Collection (Application), */
0x85, 0x10, /* Report ID (16), */
0x09, 0x20, /* Usage (Stylus), */
0xA0, /* Collection (Physical), */
0x14, /* Logical Minimum (0), */
0x25, 0x01, /* Logical Maximum (1), */
0x75, 0x01, /* Report Size (1), */
0x09, 0x42, /* Usage (Tip Switch), */
0x09, 0x44, /* Usage (Barrel Switch), */
0x09, 0x46, /* Usage (Tablet Pick), */
0x95, 0x03, /* Report Count (3), */
0x81, 0x02, /* Input (Variable), */
0x95, 0x04, /* Report Count (4), */
0x81, 0x03, /* Input (Constant, Variable), */
0x09, 0x32, /* Usage (In Range), */
0x95, 0x01, /* Report Count (1), */
0x81, 0x02, /* Input (Variable), */
0x75, 0x10, /* Report Size (16), */
0x95, 0x01, /* Report Count (1), */
0xA4, /* Push, */
0x05, 0x01, /* Usage Page (Desktop), */
0x55, 0xFD, /* Unit Exponent (-3), */
0x65, 0x13, /* Unit (Inch), */
0x34, /* Physical Minimum (0), */
0x09, 0x30, /* Usage (X), */
0x46, 0x40, 0x1F, /* Physical Maximum (8000), */
0x27, 0x00, 0xA0, 0x00, 0x00, /* Logical Maximum (40960), */
0x81, 0x02, /* Input (Variable), */
0x09, 0x31, /* Usage (Y), */
0x46, 0x70, 0x17, /* Physical Maximum (6000), */
0x26, 0x00, 0x78, /* Logical Maximum (30720), */
0x81, 0x02, /* Input (Variable), */
0xB4, /* Pop, */
0x09, 0x30, /* Usage (Tip Pressure), */
0x26, 0xFF, 0x07, /* Logical Maximum (2047), */
0x81, 0x02, /* Input (Variable), */
0xC0, /* End Collection, */
0xC0, /* End Collection, */
0x05, 0x01, /* Usage Page (Desktop), */
0x09, 0x02, /* Usage (Mouse), */
0xA1, 0x01, /* Collection (Application), */
0x85, 0x11, /* Report ID (17), */
0x09, 0x01, /* Usage (Pointer), */
0xA0, /* Collection (Physical), */
0x14, /* Logical Minimum (0), */
0xA4, /* Push, */
0x05, 0x09, /* Usage Page (Button), */
0x75, 0x01, /* Report Size (1), */
0x19, 0x01, /* Usage Minimum (01h), */
0x29, 0x03, /* Usage Maximum (03h), */
0x25, 0x01, /* Logical Maximum (1), */
0x95, 0x03, /* Report Count (3), */
0x81, 0x02, /* Input (Variable), */
0x95, 0x05, /* Report Count (5), */
0x81, 0x01, /* Input (Constant), */
0xB4, /* Pop, */
0x95, 0x01, /* Report Count (1), */
0xA4, /* Push, */
0x55, 0xFD, /* Unit Exponent (-3), */
0x65, 0x13, /* Unit (Inch), */
0x34, /* Physical Minimum (0), */
0x75, 0x10, /* Report Size (16), */
0x09, 0x30, /* Usage (X), */
0x46, 0x40, 0x1F, /* Physical Maximum (8000), */
0x27, 0x00, 0xA0, 0x00, 0x00, /* Logical Maximum (40960), */
0x81, 0x02, /* Input (Variable), */
0x09, 0x31, /* Usage (Y), */
0x46, 0x70, 0x17, /* Physical Maximum (6000), */
0x26, 0x00, 0x78, /* Logical Maximum (30720), */
0x81, 0x02, /* Input (Variable), */
0xB4, /* Pop, */
0x75, 0x08, /* Report Size (8), */
0x09, 0x38, /* Usage (Wheel), */
0x15, 0xFF, /* Logical Minimum (-1), */
0x25, 0x01, /* Logical Maximum (1), */
0x81, 0x06, /* Input (Variable, Relative), */
0x81, 0x01, /* Input (Constant), */
0xC0, /* End Collection, */
0xC0 /* End Collection */
};
/* Original EasyPen M610X report descriptor size */ /* Original EasyPen M610X report descriptor size */
#define EASYPEN_M610X_RDESC_ORIG_SIZE 476 #define EASYPEN_M610X_RDESC_ORIG_SIZE 476
...@@ -454,12 +538,17 @@ static __u8 *kye_report_fixup(struct hid_device *hdev, __u8 *rdesc, ...@@ -454,12 +538,17 @@ static __u8 *kye_report_fixup(struct hid_device *hdev, __u8 *rdesc,
} }
break; break;
case USB_DEVICE_ID_KYE_MOUSEPEN_I608X: case USB_DEVICE_ID_KYE_MOUSEPEN_I608X:
case USB_DEVICE_ID_KYE_MOUSEPEN_I608X_2:
if (*rsize == MOUSEPEN_I608X_RDESC_ORIG_SIZE) { if (*rsize == MOUSEPEN_I608X_RDESC_ORIG_SIZE) {
rdesc = mousepen_i608x_rdesc_fixed; rdesc = mousepen_i608x_rdesc_fixed;
*rsize = sizeof(mousepen_i608x_rdesc_fixed); *rsize = sizeof(mousepen_i608x_rdesc_fixed);
} }
break; break;
case USB_DEVICE_ID_KYE_MOUSEPEN_I608X_V2:
if (*rsize == MOUSEPEN_I608X_V2_RDESC_ORIG_SIZE) {
rdesc = mousepen_i608x_v2_rdesc_fixed;
*rsize = sizeof(mousepen_i608x_v2_rdesc_fixed);
}
break;
case USB_DEVICE_ID_KYE_EASYPEN_M610X: case USB_DEVICE_ID_KYE_EASYPEN_M610X:
if (*rsize == EASYPEN_M610X_RDESC_ORIG_SIZE) { if (*rsize == EASYPEN_M610X_RDESC_ORIG_SIZE) {
rdesc = easypen_m610x_rdesc_fixed; rdesc = easypen_m610x_rdesc_fixed;
...@@ -553,7 +642,7 @@ static int kye_probe(struct hid_device *hdev, const struct hid_device_id *id) ...@@ -553,7 +642,7 @@ static int kye_probe(struct hid_device *hdev, const struct hid_device_id *id)
switch (id->product) { switch (id->product) {
case USB_DEVICE_ID_KYE_EASYPEN_I405X: case USB_DEVICE_ID_KYE_EASYPEN_I405X:
case USB_DEVICE_ID_KYE_MOUSEPEN_I608X: case USB_DEVICE_ID_KYE_MOUSEPEN_I608X:
case USB_DEVICE_ID_KYE_MOUSEPEN_I608X_2: case USB_DEVICE_ID_KYE_MOUSEPEN_I608X_V2:
case USB_DEVICE_ID_KYE_EASYPEN_M610X: case USB_DEVICE_ID_KYE_EASYPEN_M610X:
case USB_DEVICE_ID_KYE_PENSKETCH_M912: case USB_DEVICE_ID_KYE_PENSKETCH_M912:
ret = kye_tablet_enable(hdev); ret = kye_tablet_enable(hdev);
...@@ -586,7 +675,7 @@ static const struct hid_device_id kye_devices[] = { ...@@ -586,7 +675,7 @@ static const struct hid_device_id kye_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, { HID_USB_DEVICE(USB_VENDOR_ID_KYE,
USB_DEVICE_ID_KYE_MOUSEPEN_I608X) }, USB_DEVICE_ID_KYE_MOUSEPEN_I608X) },
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, { HID_USB_DEVICE(USB_VENDOR_ID_KYE,
USB_DEVICE_ID_KYE_MOUSEPEN_I608X_2) }, USB_DEVICE_ID_KYE_MOUSEPEN_I608X_V2) },
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, { HID_USB_DEVICE(USB_VENDOR_ID_KYE,
USB_DEVICE_ID_KYE_EASYPEN_M610X) }, USB_DEVICE_ID_KYE_EASYPEN_M610X) },
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, { HID_USB_DEVICE(USB_VENDOR_ID_KYE,
......
...@@ -49,6 +49,7 @@ ...@@ -49,6 +49,7 @@
#define FV_RDESC_ORIG_SIZE 130 #define FV_RDESC_ORIG_SIZE 130
#define MOMO_RDESC_ORIG_SIZE 87 #define MOMO_RDESC_ORIG_SIZE 87
#define MOMO2_RDESC_ORIG_SIZE 87 #define MOMO2_RDESC_ORIG_SIZE 87
#define FFG_RDESC_ORIG_SIZE 85
/* Fixed report descriptors for Logitech Driving Force (and Pro) /* Fixed report descriptors for Logitech Driving Force (and Pro)
* wheel controllers * wheel controllers
...@@ -334,6 +335,52 @@ static __u8 momo2_rdesc_fixed[] = { ...@@ -334,6 +335,52 @@ static __u8 momo2_rdesc_fixed[] = {
0xC0 /* End Collection */ 0xC0 /* End Collection */
}; };
static __u8 ffg_rdesc_fixed[] = {
0x05, 0x01, /* Usage Page (Desktop), */
0x09, 0x04, /* Usage (Joystik), */
0xA1, 0x01, /* Collection (Application), */
0xA1, 0x02, /* Collection (Logical), */
0x95, 0x01, /* Report Count (1), */
0x75, 0x0A, /* Report Size (10), */
0x15, 0x00, /* Logical Minimum (0), */
0x26, 0xFF, 0x03, /* Logical Maximum (1023), */
0x35, 0x00, /* Physical Minimum (0), */
0x46, 0xFF, 0x03, /* Physical Maximum (1023), */
0x09, 0x30, /* Usage (X), */
0x81, 0x02, /* Input (Variable), */
0x95, 0x06, /* Report Count (6), */
0x75, 0x01, /* Report Size (1), */
0x25, 0x01, /* Logical Maximum (1), */
0x45, 0x01, /* Physical Maximum (1), */
0x05, 0x09, /* Usage Page (Button), */
0x19, 0x01, /* Usage Minimum (01h), */
0x29, 0x06, /* Usage Maximum (06h), */
0x81, 0x02, /* Input (Variable), */
0x95, 0x01, /* Report Count (1), */
0x75, 0x08, /* Report Size (8), */
0x26, 0xFF, 0x00, /* Logical Maximum (255), */
0x46, 0xFF, 0x00, /* Physical Maximum (255), */
0x06, 0x00, 0xFF, /* Usage Page (FF00h), */
0x09, 0x01, /* Usage (01h), */
0x81, 0x02, /* Input (Variable), */
0x05, 0x01, /* Usage Page (Desktop), */
0x81, 0x01, /* Input (Constant), */
0x09, 0x31, /* Usage (Y), */
0x81, 0x02, /* Input (Variable), */
0x09, 0x32, /* Usage (Z), */
0x81, 0x02, /* Input (Variable), */
0x06, 0x00, 0xFF, /* Usage Page (FF00h), */
0x09, 0x01, /* Usage (01h), */
0x81, 0x02, /* Input (Variable), */
0xC0, /* End Collection, */
0xA1, 0x02, /* Collection (Logical), */
0x09, 0x02, /* Usage (02h), */
0x95, 0x07, /* Report Count (7), */
0x91, 0x02, /* Output (Variable), */
0xC0, /* End Collection, */
0xC0 /* End Collection */
};
/* /*
* Certain Logitech keyboards send in report #3 keys which are far * Certain Logitech keyboards send in report #3 keys which are far
* above the logical maximum described in descriptor. This extends * above the logical maximum described in descriptor. This extends
...@@ -343,8 +390,6 @@ static __u8 *lg_report_fixup(struct hid_device *hdev, __u8 *rdesc, ...@@ -343,8 +390,6 @@ static __u8 *lg_report_fixup(struct hid_device *hdev, __u8 *rdesc,
unsigned int *rsize) unsigned int *rsize)
{ {
struct lg_drv_data *drv_data = hid_get_drvdata(hdev); struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
struct usb_device_descriptor *udesc;
__u16 bcdDevice, rev_maj, rev_min;
if ((drv_data->quirks & LG_RDESC) && *rsize >= 91 && rdesc[83] == 0x26 && if ((drv_data->quirks & LG_RDESC) && *rsize >= 91 && rdesc[83] == 0x26 &&
rdesc[84] == 0x8c && rdesc[85] == 0x02) { rdesc[84] == 0x8c && rdesc[85] == 0x02) {
...@@ -363,20 +408,18 @@ static __u8 *lg_report_fixup(struct hid_device *hdev, __u8 *rdesc, ...@@ -363,20 +408,18 @@ static __u8 *lg_report_fixup(struct hid_device *hdev, __u8 *rdesc,
switch (hdev->product) { switch (hdev->product) {
/* Several wheels report as this id when operating in emulation mode. */ case USB_DEVICE_ID_LOGITECH_WINGMAN_FFG:
case USB_DEVICE_ID_LOGITECH_WHEEL: if (*rsize == FFG_RDESC_ORIG_SIZE) {
udesc = &(hid_to_usb_dev(hdev)->descriptor); hid_info(hdev,
if (!udesc) { "fixing up Logitech Wingman Formula Force GP report descriptor\n");
hid_err(hdev, "NULL USB device descriptor\n"); rdesc = ffg_rdesc_fixed;
break; *rsize = sizeof(ffg_rdesc_fixed);
} }
bcdDevice = le16_to_cpu(udesc->bcdDevice); break;
rev_maj = bcdDevice >> 8;
rev_min = bcdDevice & 0xff;
/* Update the report descriptor for only the Driving Force wheel */ /* Several wheels report as this id when operating in emulation mode. */
if (rev_maj == 1 && rev_min == 2 && case USB_DEVICE_ID_LOGITECH_WHEEL:
*rsize == DF_RDESC_ORIG_SIZE) { if (*rsize == DF_RDESC_ORIG_SIZE) {
hid_info(hdev, hid_info(hdev,
"fixing up Logitech Driving Force report descriptor\n"); "fixing up Logitech Driving Force report descriptor\n");
rdesc = df_rdesc_fixed; rdesc = df_rdesc_fixed;
...@@ -621,6 +664,7 @@ static int lg_input_mapped(struct hid_device *hdev, struct hid_input *hi, ...@@ -621,6 +664,7 @@ static int lg_input_mapped(struct hid_device *hdev, struct hid_input *hi,
usage->code == ABS_RZ)) { usage->code == ABS_RZ)) {
switch (hdev->product) { switch (hdev->product) {
case USB_DEVICE_ID_LOGITECH_G29_WHEEL: case USB_DEVICE_ID_LOGITECH_G29_WHEEL:
case USB_DEVICE_ID_LOGITECH_WINGMAN_FFG:
case USB_DEVICE_ID_LOGITECH_WHEEL: case USB_DEVICE_ID_LOGITECH_WHEEL:
case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL: case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL:
case USB_DEVICE_ID_LOGITECH_DFP_WHEEL: case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:
...@@ -657,6 +701,17 @@ static int lg_event(struct hid_device *hdev, struct hid_field *field, ...@@ -657,6 +701,17 @@ static int lg_event(struct hid_device *hdev, struct hid_field *field,
return 0; return 0;
} }
static int lg_raw_event(struct hid_device *hdev, struct hid_report *report,
u8 *rd, int size)
{
struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
if (drv_data->quirks & LG_FF4)
return lg4ff_raw_event(hdev, report, rd, size, drv_data);
return 0;
}
static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id) static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id)
{ {
struct usb_interface *iface = to_usb_interface(hdev->dev.parent); struct usb_interface *iface = to_usb_interface(hdev->dev.parent);
...@@ -809,7 +864,7 @@ static const struct hid_device_id lg_devices[] = { ...@@ -809,7 +864,7 @@ static const struct hid_device_id lg_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WII_WHEEL), { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WII_WHEEL),
.driver_data = LG_FF4 }, .driver_data = LG_FF4 },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG), { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG),
.driver_data = LG_FF }, .driver_data = LG_NOGET | LG_FF4 },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2), { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2),
.driver_data = LG_FF2 }, .driver_data = LG_FF2 },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FLIGHT_SYSTEM_G940), { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FLIGHT_SYSTEM_G940),
...@@ -830,6 +885,7 @@ static struct hid_driver lg_driver = { ...@@ -830,6 +885,7 @@ static struct hid_driver lg_driver = {
.input_mapping = lg_input_mapping, .input_mapping = lg_input_mapping,
.input_mapped = lg_input_mapped, .input_mapped = lg_input_mapped,
.event = lg_event, .event = lg_event,
.raw_event = lg_raw_event,
.probe = lg_probe, .probe = lg_probe,
.remove = lg_remove, .remove = lg_remove,
}; };
......
...@@ -75,6 +75,7 @@ static void lg4ff_set_range_g25(struct hid_device *hid, u16 range); ...@@ -75,6 +75,7 @@ static void lg4ff_set_range_g25(struct hid_device *hid, u16 range);
struct lg4ff_wheel_data { struct lg4ff_wheel_data {
const u32 product_id; const u32 product_id;
u16 combine;
u16 range; u16 range;
const u16 min_range; const u16 min_range;
const u16 max_range; const u16 max_range;
...@@ -136,6 +137,7 @@ struct lg4ff_alternate_mode { ...@@ -136,6 +137,7 @@ struct lg4ff_alternate_mode {
}; };
static const struct lg4ff_wheel lg4ff_devices[] = { static const struct lg4ff_wheel lg4ff_devices[] = {
{USB_DEVICE_ID_LOGITECH_WINGMAN_FFG, lg4ff_wheel_effects, 40, 180, NULL},
{USB_DEVICE_ID_LOGITECH_WHEEL, lg4ff_wheel_effects, 40, 270, NULL}, {USB_DEVICE_ID_LOGITECH_WHEEL, lg4ff_wheel_effects, 40, 270, NULL},
{USB_DEVICE_ID_LOGITECH_MOMO_WHEEL, lg4ff_wheel_effects, 40, 270, NULL}, {USB_DEVICE_ID_LOGITECH_MOMO_WHEEL, lg4ff_wheel_effects, 40, 270, NULL},
{USB_DEVICE_ID_LOGITECH_DFP_WHEEL, lg4ff_wheel_effects, 40, 900, lg4ff_set_range_dfp}, {USB_DEVICE_ID_LOGITECH_DFP_WHEEL, lg4ff_wheel_effects, 40, 900, lg4ff_set_range_dfp},
...@@ -328,6 +330,56 @@ int lg4ff_adjust_input_event(struct hid_device *hid, struct hid_field *field, ...@@ -328,6 +330,56 @@ int lg4ff_adjust_input_event(struct hid_device *hid, struct hid_field *field,
} }
} }
int lg4ff_raw_event(struct hid_device *hdev, struct hid_report *report,
u8 *rd, int size, struct lg_drv_data *drv_data)
{
int offset;
struct lg4ff_device_entry *entry = drv_data->device_props;
if (!entry)
return 0;
/* adjust HID report present combined pedals data */
if (entry->wdata.combine) {
switch (entry->wdata.product_id) {
case USB_DEVICE_ID_LOGITECH_WHEEL:
rd[5] = rd[3];
rd[6] = 0x7F;
return 1;
case USB_DEVICE_ID_LOGITECH_WINGMAN_FFG:
case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL:
case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2:
rd[4] = rd[3];
rd[5] = 0x7F;
return 1;
case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:
rd[5] = rd[4];
rd[6] = 0x7F;
return 1;
case USB_DEVICE_ID_LOGITECH_G25_WHEEL:
case USB_DEVICE_ID_LOGITECH_G27_WHEEL:
offset = 5;
break;
case USB_DEVICE_ID_LOGITECH_DFGT_WHEEL:
case USB_DEVICE_ID_LOGITECH_G29_WHEEL:
offset = 6;
break;
case USB_DEVICE_ID_LOGITECH_WII_WHEEL:
offset = 3;
break;
default:
return 0;
}
/* Compute a combined axis when wheel does not supply it */
rd[offset] = (0xFF + rd[offset] - rd[offset+1]) >> 1;
rd[offset+1] = 0x7F;
return 1;
}
return 0;
}
static void lg4ff_init_wheel_data(struct lg4ff_wheel_data * const wdata, const struct lg4ff_wheel *wheel, static void lg4ff_init_wheel_data(struct lg4ff_wheel_data * const wdata, const struct lg4ff_wheel *wheel,
const struct lg4ff_multimode_wheel *mmode_wheel, const struct lg4ff_multimode_wheel *mmode_wheel,
const u16 real_product_id) const u16 real_product_id)
...@@ -345,6 +397,7 @@ static void lg4ff_init_wheel_data(struct lg4ff_wheel_data * const wdata, const s ...@@ -345,6 +397,7 @@ static void lg4ff_init_wheel_data(struct lg4ff_wheel_data * const wdata, const s
{ {
struct lg4ff_wheel_data t_wdata = { .product_id = wheel->product_id, struct lg4ff_wheel_data t_wdata = { .product_id = wheel->product_id,
.real_product_id = real_product_id, .real_product_id = real_product_id,
.combine = 0,
.min_range = wheel->min_range, .min_range = wheel->min_range,
.max_range = wheel->max_range, .max_range = wheel->max_range,
.set_range = wheel->set_range, .set_range = wheel->set_range,
...@@ -885,6 +938,58 @@ static ssize_t lg4ff_alternate_modes_store(struct device *dev, struct device_att ...@@ -885,6 +938,58 @@ static ssize_t lg4ff_alternate_modes_store(struct device *dev, struct device_att
} }
static DEVICE_ATTR(alternate_modes, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH, lg4ff_alternate_modes_show, lg4ff_alternate_modes_store); static DEVICE_ATTR(alternate_modes, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH, lg4ff_alternate_modes_show, lg4ff_alternate_modes_store);
static ssize_t lg4ff_combine_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct hid_device *hid = to_hid_device(dev);
struct lg4ff_device_entry *entry;
struct lg_drv_data *drv_data;
size_t count;
drv_data = hid_get_drvdata(hid);
if (!drv_data) {
hid_err(hid, "Private driver data not found!\n");
return 0;
}
entry = drv_data->device_props;
if (!entry) {
hid_err(hid, "Device properties not found!\n");
return 0;
}
count = scnprintf(buf, PAGE_SIZE, "%u\n", entry->wdata.combine);
return count;
}
static ssize_t lg4ff_combine_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct hid_device *hid = to_hid_device(dev);
struct lg4ff_device_entry *entry;
struct lg_drv_data *drv_data;
u16 combine = simple_strtoul(buf, NULL, 10);
drv_data = hid_get_drvdata(hid);
if (!drv_data) {
hid_err(hid, "Private driver data not found!\n");
return -EINVAL;
}
entry = drv_data->device_props;
if (!entry) {
hid_err(hid, "Device properties not found!\n");
return -EINVAL;
}
if (combine > 1)
combine = 1;
entry->wdata.combine = combine;
return count;
}
static DEVICE_ATTR(combine_pedals, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH, lg4ff_combine_show, lg4ff_combine_store);
/* Export the currently set range of the wheel */ /* Export the currently set range of the wheel */
static ssize_t lg4ff_range_show(struct device *dev, struct device_attribute *attr, static ssize_t lg4ff_range_show(struct device *dev, struct device_attribute *attr,
char *buf) char *buf)
...@@ -1259,6 +1364,9 @@ int lg4ff_init(struct hid_device *hid) ...@@ -1259,6 +1364,9 @@ int lg4ff_init(struct hid_device *hid)
} }
/* Create sysfs interface */ /* Create sysfs interface */
error = device_create_file(&hid->dev, &dev_attr_combine_pedals);
if (error)
hid_warn(hid, "Unable to create sysfs interface for \"combine\", errno %d\n", error);
error = device_create_file(&hid->dev, &dev_attr_range); error = device_create_file(&hid->dev, &dev_attr_range);
if (error) if (error)
hid_warn(hid, "Unable to create sysfs interface for \"range\", errno %d\n", error); hid_warn(hid, "Unable to create sysfs interface for \"range\", errno %d\n", error);
...@@ -1358,6 +1466,7 @@ int lg4ff_deinit(struct hid_device *hid) ...@@ -1358,6 +1466,7 @@ int lg4ff_deinit(struct hid_device *hid)
device_remove_file(&hid->dev, &dev_attr_alternate_modes); device_remove_file(&hid->dev, &dev_attr_alternate_modes);
} }
device_remove_file(&hid->dev, &dev_attr_combine_pedals);
device_remove_file(&hid->dev, &dev_attr_range); device_remove_file(&hid->dev, &dev_attr_range);
#ifdef CONFIG_LEDS_CLASS #ifdef CONFIG_LEDS_CLASS
{ {
......
...@@ -6,11 +6,15 @@ extern int lg4ff_no_autoswitch; /* From hid-lg.c */ ...@@ -6,11 +6,15 @@ extern int lg4ff_no_autoswitch; /* From hid-lg.c */
int lg4ff_adjust_input_event(struct hid_device *hid, struct hid_field *field, int lg4ff_adjust_input_event(struct hid_device *hid, struct hid_field *field,
struct hid_usage *usage, s32 value, struct lg_drv_data *drv_data); struct hid_usage *usage, s32 value, struct lg_drv_data *drv_data);
int lg4ff_raw_event(struct hid_device *hdev, struct hid_report *report,
u8 *rd, int size, struct lg_drv_data *drv_data);
int lg4ff_init(struct hid_device *hdev); int lg4ff_init(struct hid_device *hdev);
int lg4ff_deinit(struct hid_device *hdev); int lg4ff_deinit(struct hid_device *hdev);
#else #else
static inline int lg4ff_adjust_input_event(struct hid_device *hid, struct hid_field *field, static inline int lg4ff_adjust_input_event(struct hid_device *hid, struct hid_field *field,
struct hid_usage *usage, s32 value, struct lg_drv_data *drv_data) { return 0; } struct hid_usage *usage, s32 value, struct lg_drv_data *drv_data) { return 0; }
static inline int lg4ff_raw_event(struct hid_device *hdev, struct hid_report *report,
u8 *rd, int size, struct lg_drv_data *drv_data) { return 0; }
static inline int lg4ff_init(struct hid_device *hdev) { return -1; } static inline int lg4ff_init(struct hid_device *hdev) { return -1; }
static inline int lg4ff_deinit(struct hid_device *hdev) { return -1; } static inline int lg4ff_deinit(struct hid_device *hdev) { return -1; }
#endif #endif
......
...@@ -28,7 +28,6 @@ ...@@ -28,7 +28,6 @@
#define MS_RDESC 0x08 #define MS_RDESC 0x08
#define MS_NOGET 0x10 #define MS_NOGET 0x10
#define MS_DUPLICATE_USAGES 0x20 #define MS_DUPLICATE_USAGES 0x20
#define MS_RDESC_3K 0x40
static __u8 *ms_report_fixup(struct hid_device *hdev, __u8 *rdesc, static __u8 *ms_report_fixup(struct hid_device *hdev, __u8 *rdesc,
unsigned int *rsize) unsigned int *rsize)
...@@ -45,13 +44,6 @@ static __u8 *ms_report_fixup(struct hid_device *hdev, __u8 *rdesc, ...@@ -45,13 +44,6 @@ static __u8 *ms_report_fixup(struct hid_device *hdev, __u8 *rdesc,
rdesc[557] = 0x35; rdesc[557] = 0x35;
rdesc[559] = 0x45; rdesc[559] = 0x45;
} }
/* the same as above (s/usage/physical/) */
if ((quirks & MS_RDESC_3K) && *rsize == 106 && rdesc[94] == 0x19 &&
rdesc[95] == 0x00 && rdesc[96] == 0x29 &&
rdesc[97] == 0xff) {
rdesc[94] = 0x35;
rdesc[96] = 0x45;
}
return rdesc; return rdesc;
} }
...@@ -271,7 +263,7 @@ static const struct hid_device_id ms_devices[] = { ...@@ -271,7 +263,7 @@ static const struct hid_device_id ms_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_USB), { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_USB),
.driver_data = MS_PRESENTER }, .driver_data = MS_PRESENTER },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_3K), { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_3K),
.driver_data = MS_ERGONOMY | MS_RDESC_3K }, .driver_data = MS_ERGONOMY },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_7K), { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_7K),
.driver_data = MS_ERGONOMY }, .driver_data = MS_ERGONOMY },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_600), { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_600),
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
* *
*/ */
#include <linux/device.h> #include <linux/device.h>
#include <linux/hid.h> #include <linux/hid.h>
#include <linux/module.h> #include <linux/module.h>
...@@ -798,6 +799,9 @@ static const struct hid_device_id sensor_hub_devices[] = { ...@@ -798,6 +799,9 @@ static const struct hid_device_id sensor_hub_devices[] = {
{ HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_ITE, { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_ITE,
USB_DEVICE_ID_ITE_LENOVO_YOGA900), USB_DEVICE_ID_ITE_LENOVO_YOGA900),
.driver_data = HID_SENSOR_HUB_ENUM_QUIRK}, .driver_data = HID_SENSOR_HUB_ENUM_QUIRK},
{ HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_INTEL_0,
0x22D8),
.driver_data = HID_SENSOR_HUB_ENUM_QUIRK},
{ HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, HID_ANY_ID, { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, HID_ANY_ID,
HID_ANY_ID) }, HID_ANY_ID) },
{ } { }
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* Copyright (c) 2012 David Dillow <dave@thedillows.org> * Copyright (c) 2012 David Dillow <dave@thedillows.org>
* Copyright (c) 2006-2013 Jiri Kosina * Copyright (c) 2006-2013 Jiri Kosina
* Copyright (c) 2013 Colin Leitner <colin.leitner@gmail.com> * Copyright (c) 2013 Colin Leitner <colin.leitner@gmail.com>
* Copyright (c) 2014 Frank Praznik <frank.praznik@gmail.com> * Copyright (c) 2014-2016 Frank Praznik <frank.praznik@gmail.com>
*/ */
/* /*
...@@ -66,6 +66,8 @@ ...@@ -66,6 +66,8 @@
MOTION_CONTROLLER_BT | NAVIGATION_CONTROLLER) MOTION_CONTROLLER_BT | NAVIGATION_CONTROLLER)
#define SONY_FF_SUPPORT (SIXAXIS_CONTROLLER | DUALSHOCK4_CONTROLLER |\ #define SONY_FF_SUPPORT (SIXAXIS_CONTROLLER | DUALSHOCK4_CONTROLLER |\
MOTION_CONTROLLER) MOTION_CONTROLLER)
#define SONY_BT_DEVICE (SIXAXIS_CONTROLLER_BT | DUALSHOCK4_CONTROLLER_BT |\
MOTION_CONTROLLER_BT | NAVIGATION_CONTROLLER_BT)
#define MAX_LEDS 4 #define MAX_LEDS 4
...@@ -1049,6 +1051,7 @@ struct sony_sc { ...@@ -1049,6 +1051,7 @@ struct sony_sc {
u8 mac_address[6]; u8 mac_address[6];
u8 worker_initialized; u8 worker_initialized;
u8 defer_initialization;
u8 cable_state; u8 cable_state;
u8 battery_charging; u8 battery_charging;
u8 battery_capacity; u8 battery_capacity;
...@@ -1059,6 +1062,12 @@ struct sony_sc { ...@@ -1059,6 +1062,12 @@ struct sony_sc {
u8 led_count; u8 led_count;
}; };
static inline void sony_schedule_work(struct sony_sc *sc)
{
if (!sc->defer_initialization)
schedule_work(&sc->state_worker);
}
static u8 *sixaxis_fixup(struct hid_device *hdev, u8 *rdesc, static u8 *sixaxis_fixup(struct hid_device *hdev, u8 *rdesc,
unsigned int *rsize) unsigned int *rsize)
{ {
...@@ -1318,6 +1327,11 @@ static int sony_raw_event(struct hid_device *hdev, struct hid_report *report, ...@@ -1318,6 +1327,11 @@ static int sony_raw_event(struct hid_device *hdev, struct hid_report *report,
dualshock4_parse_report(sc, rd, size); dualshock4_parse_report(sc, rd, size);
} }
if (sc->defer_initialization) {
sc->defer_initialization = 0;
sony_schedule_work(sc);
}
return 0; return 0;
} }
...@@ -1555,7 +1569,7 @@ static void buzz_set_leds(struct sony_sc *sc) ...@@ -1555,7 +1569,7 @@ static void buzz_set_leds(struct sony_sc *sc)
static void sony_set_leds(struct sony_sc *sc) static void sony_set_leds(struct sony_sc *sc)
{ {
if (!(sc->quirks & BUZZ_CONTROLLER)) if (!(sc->quirks & BUZZ_CONTROLLER))
schedule_work(&sc->state_worker); sony_schedule_work(sc);
else else
buzz_set_leds(sc); buzz_set_leds(sc);
} }
...@@ -1666,7 +1680,7 @@ static int sony_led_blink_set(struct led_classdev *led, unsigned long *delay_on, ...@@ -1666,7 +1680,7 @@ static int sony_led_blink_set(struct led_classdev *led, unsigned long *delay_on,
new_off != drv_data->led_delay_off[n]) { new_off != drv_data->led_delay_off[n]) {
drv_data->led_delay_on[n] = new_on; drv_data->led_delay_on[n] = new_on;
drv_data->led_delay_off[n] = new_off; drv_data->led_delay_off[n] = new_off;
schedule_work(&drv_data->state_worker); sony_schedule_work(drv_data);
} }
return 0; return 0;
...@@ -1866,6 +1880,17 @@ static void dualshock4_send_output_report(struct sony_sc *sc) ...@@ -1866,6 +1880,17 @@ static void dualshock4_send_output_report(struct sony_sc *sc)
u8 *buf = sc->output_report_dmabuf; u8 *buf = sc->output_report_dmabuf;
int offset; int offset;
/*
* NOTE: The buf[1] field of the Bluetooth report controls
* the Dualshock 4 reporting rate.
*
* Known values include:
*
* 0x80 - 1000hz (full speed)
* 0xA0 - 31hz
* 0xB0 - 20hz
* 0xD0 - 66hz
*/
if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) { if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) {
memset(buf, 0, DS4_REPORT_0x05_SIZE); memset(buf, 0, DS4_REPORT_0x05_SIZE);
buf[0] = 0x05; buf[0] = 0x05;
...@@ -1977,7 +2002,7 @@ static int sony_play_effect(struct input_dev *dev, void *data, ...@@ -1977,7 +2002,7 @@ static int sony_play_effect(struct input_dev *dev, void *data,
sc->left = effect->u.rumble.strong_magnitude / 256; sc->left = effect->u.rumble.strong_magnitude / 256;
sc->right = effect->u.rumble.weak_magnitude / 256; sc->right = effect->u.rumble.weak_magnitude / 256;
schedule_work(&sc->state_worker); sony_schedule_work(sc);
return 0; return 0;
} }
...@@ -2040,8 +2065,11 @@ static int sony_battery_get_property(struct power_supply *psy, ...@@ -2040,8 +2065,11 @@ static int sony_battery_get_property(struct power_supply *psy,
return ret; return ret;
} }
static int sony_battery_probe(struct sony_sc *sc) static int sony_battery_probe(struct sony_sc *sc, int append_dev_id)
{ {
const char *battery_str_fmt = append_dev_id ?
"sony_controller_battery_%pMR_%i" :
"sony_controller_battery_%pMR";
struct power_supply_config psy_cfg = { .drv_data = sc, }; struct power_supply_config psy_cfg = { .drv_data = sc, };
struct hid_device *hdev = sc->hdev; struct hid_device *hdev = sc->hdev;
int ret; int ret;
...@@ -2057,9 +2085,8 @@ static int sony_battery_probe(struct sony_sc *sc) ...@@ -2057,9 +2085,8 @@ static int sony_battery_probe(struct sony_sc *sc)
sc->battery_desc.get_property = sony_battery_get_property; sc->battery_desc.get_property = sony_battery_get_property;
sc->battery_desc.type = POWER_SUPPLY_TYPE_BATTERY; sc->battery_desc.type = POWER_SUPPLY_TYPE_BATTERY;
sc->battery_desc.use_for_apm = 0; sc->battery_desc.use_for_apm = 0;
sc->battery_desc.name = kasprintf(GFP_KERNEL, sc->battery_desc.name = kasprintf(GFP_KERNEL, battery_str_fmt,
"sony_controller_battery_%pMR", sc->mac_address, sc->device_id);
sc->mac_address);
if (!sc->battery_desc.name) if (!sc->battery_desc.name)
return -ENOMEM; return -ENOMEM;
...@@ -2095,7 +2122,21 @@ static void sony_battery_remove(struct sony_sc *sc) ...@@ -2095,7 +2122,21 @@ static void sony_battery_remove(struct sony_sc *sc)
* it will show up as two devices. A global list of connected controllers and * it will show up as two devices. A global list of connected controllers and
* their MAC addresses is maintained to ensure that a device is only connected * their MAC addresses is maintained to ensure that a device is only connected
* once. * once.
*
* Some USB-only devices masquerade as Sixaxis controllers and all have the
* same dummy Bluetooth address, so a comparison of the connection type is
* required. Devices are only rejected in the case where two devices have
* matching Bluetooth addresses on different bus types.
*/ */
static inline int sony_compare_connection_type(struct sony_sc *sc0,
struct sony_sc *sc1)
{
const int sc0_not_bt = !(sc0->quirks & SONY_BT_DEVICE);
const int sc1_not_bt = !(sc1->quirks & SONY_BT_DEVICE);
return sc0_not_bt == sc1_not_bt;
}
static int sony_check_add_dev_list(struct sony_sc *sc) static int sony_check_add_dev_list(struct sony_sc *sc)
{ {
struct sony_sc *entry; struct sony_sc *entry;
...@@ -2108,9 +2149,14 @@ static int sony_check_add_dev_list(struct sony_sc *sc) ...@@ -2108,9 +2149,14 @@ static int sony_check_add_dev_list(struct sony_sc *sc)
ret = memcmp(sc->mac_address, entry->mac_address, ret = memcmp(sc->mac_address, entry->mac_address,
sizeof(sc->mac_address)); sizeof(sc->mac_address));
if (!ret) { if (!ret) {
ret = -EEXIST; if (sony_compare_connection_type(sc, entry)) {
hid_info(sc->hdev, "controller with MAC address %pMR already connected\n", ret = 1;
} else {
ret = -EEXIST;
hid_info(sc->hdev,
"controller with MAC address %pMR already connected\n",
sc->mac_address); sc->mac_address);
}
goto unlock; goto unlock;
} }
} }
...@@ -2286,6 +2332,7 @@ static inline void sony_cancel_work_sync(struct sony_sc *sc) ...@@ -2286,6 +2332,7 @@ static inline void sony_cancel_work_sync(struct sony_sc *sc)
static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id) static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
{ {
int ret; int ret;
int append_dev_id;
unsigned long quirks = id->driver_data; unsigned long quirks = id->driver_data;
struct sony_sc *sc; struct sony_sc *sc;
unsigned int connect_mask = HID_CONNECT_DEFAULT; unsigned int connect_mask = HID_CONNECT_DEFAULT;
...@@ -2345,9 +2392,16 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id) ...@@ -2345,9 +2392,16 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
* the Sixaxis does not want the report_id as part of the data * the Sixaxis does not want the report_id as part of the data
* packet, so we have to discard buf[0] when sending the actual * packet, so we have to discard buf[0] when sending the actual
* control message, even for numbered reports, humpf! * control message, even for numbered reports, humpf!
*
* Additionally, the Sixaxis on USB isn't properly initialized
* until the PS logo button is pressed and as such won't retain
* any state set by an output report, so the initial
* configuration report is deferred until the first input
* report arrives.
*/ */
hdev->quirks |= HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP; hdev->quirks |= HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP;
hdev->quirks |= HID_QUIRK_SKIP_OUTPUT_REPORT_ID; hdev->quirks |= HID_QUIRK_SKIP_OUTPUT_REPORT_ID;
sc->defer_initialization = 1;
ret = sixaxis_set_operational_usb(hdev); ret = sixaxis_set_operational_usb(hdev);
sony_init_output_report(sc, sixaxis_send_output_report); sony_init_output_report(sc, sixaxis_send_output_report);
} else if ((sc->quirks & SIXAXIS_CONTROLLER_BT) || } else if ((sc->quirks & SIXAXIS_CONTROLLER_BT) ||
...@@ -2383,7 +2437,7 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id) ...@@ -2383,7 +2437,7 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
if (ret < 0) if (ret < 0)
goto err_stop; goto err_stop;
ret = sony_check_add(sc); ret = append_dev_id = sony_check_add(sc);
if (ret < 0) if (ret < 0)
goto err_stop; goto err_stop;
...@@ -2394,7 +2448,7 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id) ...@@ -2394,7 +2448,7 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
} }
if (sc->quirks & SONY_BATTERY_SUPPORT) { if (sc->quirks & SONY_BATTERY_SUPPORT) {
ret = sony_battery_probe(sc); ret = sony_battery_probe(sc, append_dev_id);
if (ret < 0) if (ret < 0)
goto err_stop; goto err_stop;
...@@ -2490,8 +2544,10 @@ static int sony_resume(struct hid_device *hdev) ...@@ -2490,8 +2544,10 @@ static int sony_resume(struct hid_device *hdev)
* reinitialized on resume or they won't behave properly. * reinitialized on resume or they won't behave properly.
*/ */
if ((sc->quirks & SIXAXIS_CONTROLLER_USB) || if ((sc->quirks & SIXAXIS_CONTROLLER_USB) ||
(sc->quirks & NAVIGATION_CONTROLLER_USB)) (sc->quirks & NAVIGATION_CONTROLLER_USB)) {
sixaxis_set_operational_usb(sc->hdev); sixaxis_set_operational_usb(sc->hdev);
sc->defer_initialization = 1;
}
sony_set_leds(sc); sony_set_leds(sc);
} }
......
...@@ -21,13 +21,6 @@ ...@@ -21,13 +21,6 @@
#include "hid-ids.h" #include "hid-ids.h"
/*
* See WPXXXXU model descriptions, device and HID report descriptors at
* http://sf.net/apps/mediawiki/digimend/?title=UC-Logic_Tablet_WP4030U
* http://sf.net/apps/mediawiki/digimend/?title=UC-Logic_Tablet_WP5540U
* http://sf.net/apps/mediawiki/digimend/?title=UC-Logic_Tablet_WP8060U
*/
/* Size of the original descriptor of WPXXXXU tablets */ /* Size of the original descriptor of WPXXXXU tablets */
#define WPXXXXU_RDESC_ORIG_SIZE 212 #define WPXXXXU_RDESC_ORIG_SIZE 212
...@@ -221,11 +214,6 @@ static __u8 wp8060u_rdesc_fixed[] = { ...@@ -221,11 +214,6 @@ static __u8 wp8060u_rdesc_fixed[] = {
0xC0 /* End Collection */ 0xC0 /* End Collection */
}; };
/*
* See WP1062 description, device and HID report descriptors at
* http://sf.net/apps/mediawiki/digimend/?title=UC-Logic_Tablet_WP1062
*/
/* Size of the original descriptor of WP1062 tablet */ /* Size of the original descriptor of WP1062 tablet */
#define WP1062_RDESC_ORIG_SIZE 254 #define WP1062_RDESC_ORIG_SIZE 254
...@@ -274,11 +262,6 @@ static __u8 wp1062_rdesc_fixed[] = { ...@@ -274,11 +262,6 @@ static __u8 wp1062_rdesc_fixed[] = {
0xC0 /* End Collection */ 0xC0 /* End Collection */
}; };
/*
* See PF1209 description, device and HID report descriptors at
* http://sf.net/apps/mediawiki/digimend/?title=UC-Logic_Tablet_PF1209
*/
/* Size of the original descriptor of PF1209 tablet */ /* Size of the original descriptor of PF1209 tablet */
#define PF1209_RDESC_ORIG_SIZE 234 #define PF1209_RDESC_ORIG_SIZE 234
...@@ -356,11 +339,6 @@ static __u8 pf1209_rdesc_fixed[] = { ...@@ -356,11 +339,6 @@ static __u8 pf1209_rdesc_fixed[] = {
0xC0 /* End Collection */ 0xC0 /* End Collection */
}; };
/*
* See TWHL850 description, device and HID report descriptors at
* http://sf.net/apps/mediawiki/digimend/?title=UC-Logic_Wireless_Tablet_TWHL850
*/
/* Size of the original descriptors of TWHL850 tablet */ /* Size of the original descriptors of TWHL850 tablet */
#define TWHL850_RDESC_ORIG_SIZE0 182 #define TWHL850_RDESC_ORIG_SIZE0 182
#define TWHL850_RDESC_ORIG_SIZE1 161 #define TWHL850_RDESC_ORIG_SIZE1 161
...@@ -469,11 +447,6 @@ static __u8 twhl850_rdesc_fixed2[] = { ...@@ -469,11 +447,6 @@ static __u8 twhl850_rdesc_fixed2[] = {
0xC0 /* End Collection */ 0xC0 /* End Collection */
}; };
/*
* See TWHA60 description, device and HID report descriptors at
* http://sf.net/apps/mediawiki/digimend/?title=UC-Logic_Tablet_TWHA60
*/
/* Size of the original descriptors of TWHA60 tablet */ /* Size of the original descriptors of TWHA60 tablet */
#define TWHA60_RDESC_ORIG_SIZE0 254 #define TWHA60_RDESC_ORIG_SIZE0 254
#define TWHA60_RDESC_ORIG_SIZE1 139 #define TWHA60_RDESC_ORIG_SIZE1 139
...@@ -613,6 +586,27 @@ static const __u8 uclogic_tablet_rdesc_template[] = { ...@@ -613,6 +586,27 @@ static const __u8 uclogic_tablet_rdesc_template[] = {
0xC0 /* End Collection */ 0xC0 /* End Collection */
}; };
/* Fixed virtual pad report descriptor */
static const __u8 uclogic_buttonpad_rdesc[] = {
0x05, 0x01, /* Usage Page (Desktop), */
0x09, 0x07, /* Usage (Keypad), */
0xA1, 0x01, /* Collection (Application), */
0x85, 0xF7, /* Report ID (247), */
0x05, 0x0D, /* Usage Page (Digitizers), */
0x09, 0x39, /* Usage (Tablet Function Keys), */
0xA0, /* Collection (Physical), */
0x05, 0x09, /* Usage Page (Button), */
0x75, 0x01, /* Report Size (1), */
0x95, 0x18, /* Report Count (24), */
0x81, 0x03, /* Input (Constant, Variable), */
0x19, 0x01, /* Usage Minimum (01h), */
0x29, 0x08, /* Usage Maximum (08h), */
0x95, 0x08, /* Report Count (8), */
0x81, 0x02, /* Input (Variable), */
0xC0, /* End Collection */
0xC0 /* End Collection */
};
/* Parameter indices */ /* Parameter indices */
enum uclogic_prm { enum uclogic_prm {
UCLOGIC_PRM_X_LM = 1, UCLOGIC_PRM_X_LM = 1,
...@@ -628,6 +622,7 @@ struct uclogic_drvdata { ...@@ -628,6 +622,7 @@ struct uclogic_drvdata {
unsigned int rsize; unsigned int rsize;
bool invert_pen_inrange; bool invert_pen_inrange;
bool ignore_pen_usage; bool ignore_pen_usage;
bool has_virtual_pad_interface;
}; };
static __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc, static __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc,
...@@ -637,6 +632,12 @@ static __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc, ...@@ -637,6 +632,12 @@ static __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc,
__u8 iface_num = iface->cur_altsetting->desc.bInterfaceNumber; __u8 iface_num = iface->cur_altsetting->desc.bInterfaceNumber;
struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev); struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev);
if (drvdata->rdesc != NULL) {
rdesc = drvdata->rdesc;
*rsize = drvdata->rsize;
return rdesc;
}
switch (hdev->product) { switch (hdev->product) {
case USB_DEVICE_ID_UCLOGIC_TABLET_PF1209: case USB_DEVICE_ID_UCLOGIC_TABLET_PF1209:
if (*rsize == PF1209_RDESC_ORIG_SIZE) { if (*rsize == PF1209_RDESC_ORIG_SIZE) {
...@@ -706,11 +707,6 @@ static __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc, ...@@ -706,11 +707,6 @@ static __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc,
break; break;
} }
break; break;
default:
if (drvdata->rdesc != NULL) {
rdesc = drvdata->rdesc;
*rsize = drvdata->rsize;
}
} }
return rdesc; return rdesc;
...@@ -804,7 +800,6 @@ static int uclogic_tablet_enable(struct hid_device *hdev) ...@@ -804,7 +800,6 @@ static int uclogic_tablet_enable(struct hid_device *hdev)
len = UCLOGIC_PRM_NUM * sizeof(*buf); len = UCLOGIC_PRM_NUM * sizeof(*buf);
buf = kmalloc(len, GFP_KERNEL); buf = kmalloc(len, GFP_KERNEL);
if (buf == NULL) { if (buf == NULL) {
hid_err(hdev, "failed to allocate parameter buffer\n");
rc = -ENOMEM; rc = -ENOMEM;
goto cleanup; goto cleanup;
} }
...@@ -848,7 +843,6 @@ static int uclogic_tablet_enable(struct hid_device *hdev) ...@@ -848,7 +843,6 @@ static int uclogic_tablet_enable(struct hid_device *hdev)
sizeof(uclogic_tablet_rdesc_template), sizeof(uclogic_tablet_rdesc_template),
GFP_KERNEL); GFP_KERNEL);
if (drvdata->rdesc == NULL) { if (drvdata->rdesc == NULL) {
hid_err(hdev, "failed to allocate fixed rdesc\n");
rc = -ENOMEM; rc = -ENOMEM;
goto cleanup; goto cleanup;
} }
...@@ -876,11 +870,75 @@ static int uclogic_tablet_enable(struct hid_device *hdev) ...@@ -876,11 +870,75 @@ static int uclogic_tablet_enable(struct hid_device *hdev)
return rc; return rc;
} }
/**
* Enable actual button mode.
*
* @hdev: HID device
*/
static int uclogic_button_enable(struct hid_device *hdev)
{
int rc;
struct usb_device *usb_dev = hid_to_usb_dev(hdev);
struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev);
char *str_buf;
size_t str_len = 16;
unsigned char *rdesc;
size_t rdesc_len;
str_buf = kzalloc(str_len, GFP_KERNEL);
if (str_buf == NULL) {
rc = -ENOMEM;
goto cleanup;
}
/* Enable abstract keyboard mode */
rc = usb_string(usb_dev, 0x7b, str_buf, str_len);
if (rc == -EPIPE) {
hid_info(hdev, "button mode setting not found\n");
rc = 0;
goto cleanup;
} else if (rc < 0) {
hid_err(hdev, "failed to enable abstract keyboard\n");
goto cleanup;
} else if (strncmp(str_buf, "HK On", rc)) {
hid_info(hdev, "invalid answer when requesting buttons: '%s'\n",
str_buf);
rc = -EINVAL;
goto cleanup;
}
/* Re-allocate fixed report descriptor */
rdesc_len = drvdata->rsize + sizeof(uclogic_buttonpad_rdesc);
rdesc = devm_kzalloc(&hdev->dev, rdesc_len, GFP_KERNEL);
if (!rdesc) {
rc = -ENOMEM;
goto cleanup;
}
memcpy(rdesc, drvdata->rdesc, drvdata->rsize);
/* Append the buttonpad descriptor */
memcpy(rdesc + drvdata->rsize, uclogic_buttonpad_rdesc,
sizeof(uclogic_buttonpad_rdesc));
/* clean up old rdesc and use the new one */
drvdata->rsize = rdesc_len;
devm_kfree(&hdev->dev, drvdata->rdesc);
drvdata->rdesc = rdesc;
rc = 0;
cleanup:
kfree(str_buf);
return rc;
}
static int uclogic_probe(struct hid_device *hdev, static int uclogic_probe(struct hid_device *hdev,
const struct hid_device_id *id) const struct hid_device_id *id)
{ {
int rc; int rc;
struct usb_interface *intf = to_usb_interface(hdev->dev.parent); struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
struct usb_device *udev = hid_to_usb_dev(hdev);
struct uclogic_drvdata *drvdata; struct uclogic_drvdata *drvdata;
/* /*
...@@ -899,6 +957,10 @@ static int uclogic_probe(struct hid_device *hdev, ...@@ -899,6 +957,10 @@ static int uclogic_probe(struct hid_device *hdev,
switch (id->product) { switch (id->product) {
case USB_DEVICE_ID_HUION_TABLET: case USB_DEVICE_ID_HUION_TABLET:
case USB_DEVICE_ID_YIYNOVA_TABLET:
case USB_DEVICE_ID_UGEE_TABLET_81:
case USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3:
case USB_DEVICE_ID_UGEE_TABLET_45:
/* If this is the pen interface */ /* If this is the pen interface */
if (intf->cur_altsetting->desc.bInterfaceNumber == 0) { if (intf->cur_altsetting->desc.bInterfaceNumber == 0) {
rc = uclogic_tablet_enable(hdev); rc = uclogic_tablet_enable(hdev);
...@@ -907,10 +969,48 @@ static int uclogic_probe(struct hid_device *hdev, ...@@ -907,10 +969,48 @@ static int uclogic_probe(struct hid_device *hdev,
return rc; return rc;
} }
drvdata->invert_pen_inrange = true; drvdata->invert_pen_inrange = true;
rc = uclogic_button_enable(hdev);
drvdata->has_virtual_pad_interface = !rc;
} else { } else {
drvdata->ignore_pen_usage = true; drvdata->ignore_pen_usage = true;
} }
break; break;
case USB_DEVICE_ID_UGTIZER_TABLET_GP0610:
/* If this is the pen interface */
if (intf->cur_altsetting->desc.bInterfaceNumber == 1) {
rc = uclogic_tablet_enable(hdev);
if (rc) {
hid_err(hdev, "tablet enabling failed\n");
return rc;
}
drvdata->invert_pen_inrange = true;
} else {
drvdata->ignore_pen_usage = true;
}
break;
case USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60:
/*
* If it is the three-interface version, which is known to
* respond to initialization.
*/
if (udev->config->desc.bNumInterfaces == 3) {
/* If it is the pen interface */
if (intf->cur_altsetting->desc.bInterfaceNumber == 0) {
rc = uclogic_tablet_enable(hdev);
if (rc) {
hid_err(hdev, "tablet enabling failed\n");
return rc;
}
drvdata->invert_pen_inrange = true;
rc = uclogic_button_enable(hdev);
drvdata->has_virtual_pad_interface = !rc;
} else {
drvdata->ignore_pen_usage = true;
}
}
break;
} }
rc = hid_parse(hdev); rc = hid_parse(hdev);
...@@ -933,12 +1033,16 @@ static int uclogic_raw_event(struct hid_device *hdev, struct hid_report *report, ...@@ -933,12 +1033,16 @@ static int uclogic_raw_event(struct hid_device *hdev, struct hid_report *report,
{ {
struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev); struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev);
if ((drvdata->invert_pen_inrange) && if ((report->type == HID_INPUT_REPORT) &&
(report->type == HID_INPUT_REPORT) &&
(report->id == UCLOGIC_PEN_REPORT_ID) && (report->id == UCLOGIC_PEN_REPORT_ID) &&
(size >= 2)) (size >= 2)) {
/* Invert the in-range bit */ if (drvdata->has_virtual_pad_interface && (data[1] & 0x20))
data[1] ^= 0x40; /* Change to virtual frame button report ID */
data[0] = 0xf7;
else if (drvdata->invert_pen_inrange)
/* Invert the in-range bit */
data[1] ^= 0x40;
}
return 0; return 0;
} }
...@@ -960,6 +1064,11 @@ static const struct hid_device_id uclogic_devices[] = { ...@@ -960,6 +1064,11 @@ static const struct hid_device_id uclogic_devices[] = {
USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60) }, USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60) },
{ HID_USB_DEVICE(USB_VENDOR_ID_HUION, USB_DEVICE_ID_HUION_TABLET) }, { HID_USB_DEVICE(USB_VENDOR_ID_HUION, USB_DEVICE_ID_HUION_TABLET) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_HUION_TABLET) }, { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_HUION_TABLET) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_YIYNOVA_TABLET) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UGEE_TABLET_81) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UGEE_TABLET_45) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UGTIZER, USB_DEVICE_ID_UGTIZER_TABLET_GP0610) },
{ } { }
}; };
MODULE_DEVICE_TABLE(hid, uclogic_devices); MODULE_DEVICE_TABLE(hid, uclogic_devices);
......
...@@ -42,11 +42,6 @@ ...@@ -42,11 +42,6 @@
* 02 16 02 ink * 02 16 02 ink
*/ */
/*
* See Slim Tablet 5.8 inch description, device and HID report descriptors at
* http://sf.net/apps/mediawiki/digimend/?title=Waltop_Slim_Tablet_5.8%22
*/
/* Size of the original report descriptor of Slim Tablet 5.8 inch */ /* Size of the original report descriptor of Slim Tablet 5.8 inch */
#define SLIM_TABLET_5_8_INCH_RDESC_ORIG_SIZE 222 #define SLIM_TABLET_5_8_INCH_RDESC_ORIG_SIZE 222
...@@ -98,11 +93,6 @@ static __u8 slim_tablet_5_8_inch_rdesc_fixed[] = { ...@@ -98,11 +93,6 @@ static __u8 slim_tablet_5_8_inch_rdesc_fixed[] = {
0xC0 /* End Collection */ 0xC0 /* End Collection */
}; };
/*
* See Slim Tablet 12.1 inch description, device and HID report descriptors at
* http://sf.net/apps/mediawiki/digimend/?title=Waltop_Slim_Tablet_12.1%22
*/
/* Size of the original report descriptor of Slim Tablet 12.1 inch */ /* Size of the original report descriptor of Slim Tablet 12.1 inch */
#define SLIM_TABLET_12_1_INCH_RDESC_ORIG_SIZE 269 #define SLIM_TABLET_12_1_INCH_RDESC_ORIG_SIZE 269
...@@ -154,11 +144,6 @@ static __u8 slim_tablet_12_1_inch_rdesc_fixed[] = { ...@@ -154,11 +144,6 @@ static __u8 slim_tablet_12_1_inch_rdesc_fixed[] = {
0xC0 /* End Collection */ 0xC0 /* End Collection */
}; };
/*
* See Q Pad description, device and HID report descriptors at
* http://sf.net/apps/mediawiki/digimend/?title=Waltop_Q_Pad
*/
/* Size of the original report descriptor of Q Pad */ /* Size of the original report descriptor of Q Pad */
#define Q_PAD_RDESC_ORIG_SIZE 241 #define Q_PAD_RDESC_ORIG_SIZE 241
...@@ -210,11 +195,6 @@ static __u8 q_pad_rdesc_fixed[] = { ...@@ -210,11 +195,6 @@ static __u8 q_pad_rdesc_fixed[] = {
0xC0 /* End Collection */ 0xC0 /* End Collection */
}; };
/*
* See description, device and HID report descriptors of tablet with PID 0038 at
* http://sf.net/apps/mediawiki/digimend/?title=Waltop_PID_0038
*/
/* Size of the original report descriptor of tablet with PID 0038 */ /* Size of the original report descriptor of tablet with PID 0038 */
#define PID_0038_RDESC_ORIG_SIZE 241 #define PID_0038_RDESC_ORIG_SIZE 241
...@@ -268,11 +248,6 @@ static __u8 pid_0038_rdesc_fixed[] = { ...@@ -268,11 +248,6 @@ static __u8 pid_0038_rdesc_fixed[] = {
0xC0 /* End Collection */ 0xC0 /* End Collection */
}; };
/*
* See Media Tablet 10.6 inch description, device and HID report descriptors at
* http://sf.net/apps/mediawiki/digimend/?title=Waltop_Media_Tablet_10.6%22
*/
/* Size of the original report descriptor of Media Tablet 10.6 inch */ /* Size of the original report descriptor of Media Tablet 10.6 inch */
#define MEDIA_TABLET_10_6_INCH_RDESC_ORIG_SIZE 300 #define MEDIA_TABLET_10_6_INCH_RDESC_ORIG_SIZE 300
...@@ -386,11 +361,6 @@ static __u8 media_tablet_10_6_inch_rdesc_fixed[] = { ...@@ -386,11 +361,6 @@ static __u8 media_tablet_10_6_inch_rdesc_fixed[] = {
0xC0 /* End Collection */ 0xC0 /* End Collection */
}; };
/*
* See Media Tablet 14.1 inch description, device and HID report descriptors at
* http://sf.net/apps/mediawiki/digimend/?title=Waltop_Media_Tablet_14.1%22
*/
/* Size of the original report descriptor of Media Tablet 14.1 inch */ /* Size of the original report descriptor of Media Tablet 14.1 inch */
#define MEDIA_TABLET_14_1_INCH_RDESC_ORIG_SIZE 309 #define MEDIA_TABLET_14_1_INCH_RDESC_ORIG_SIZE 309
...@@ -502,12 +472,6 @@ static __u8 media_tablet_14_1_inch_rdesc_fixed[] = { ...@@ -502,12 +472,6 @@ static __u8 media_tablet_14_1_inch_rdesc_fixed[] = {
0xC0 /* End Collection */ 0xC0 /* End Collection */
}; };
/*
* See Sirius Battery Free Tablet description, device and HID report descriptors
* at
* http://sf.net/apps/mediawiki/digimend/?title=Waltop_Sirius_Battery_Free_Tablet
*/
/* Size of the original report descriptor of Sirius Battery Free Tablet */ /* Size of the original report descriptor of Sirius Battery Free Tablet */
#define SIRIUS_BATTERY_FREE_TABLET_RDESC_ORIG_SIZE 335 #define SIRIUS_BATTERY_FREE_TABLET_RDESC_ORIG_SIZE 335
......
menu "Intel ISH HID support"
depends on X86_64 && PCI
config INTEL_ISH_HID
tristate "Intel Integrated Sensor Hub"
default n
select HID
help
The Integrated Sensor Hub (ISH) enables the ability to offload
sensor polling and algorithm processing to a dedicated low power
processor in the chipset. This allows the core processor to go into
low power modes more often, resulting in the increased battery life.
The current processors that support ISH are: Cherrytrail, Skylake,
Broxton and Kaby Lake.
Say Y here if you want to support Intel ISH. If unsure, say N.
endmenu
#
# Makefile - Intel ISH HID drivers
# Copyright (c) 2014-2016, Intel Corporation.
#
#
obj-$(CONFIG_INTEL_ISH_HID) += intel-ishtp.o
intel-ishtp-objs := ishtp/init.o
intel-ishtp-objs += ishtp/hbm.o
intel-ishtp-objs += ishtp/client.o
intel-ishtp-objs += ishtp/bus.o
intel-ishtp-objs += ishtp/dma-if.o
intel-ishtp-objs += ishtp/client-buffers.o
obj-$(CONFIG_INTEL_ISH_HID) += intel-ish-ipc.o
intel-ish-ipc-objs := ipc/ipc.o
intel-ish-ipc-objs += ipc/pci-ish.o
obj-$(CONFIG_INTEL_ISH_HID) += intel-ishtp-hid.o
intel-ishtp-hid-objs := ishtp-hid.o
intel-ishtp-hid-objs += ishtp-hid-client.o
ccflags-y += -Idrivers/hid/intel-ish-hid/ishtp
/*
* ISH registers definitions
*
* Copyright (c) 2012-2016, Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/
#ifndef _ISHTP_ISH_REGS_H_
#define _ISHTP_ISH_REGS_H_
/*** IPC PCI Offsets and sizes ***/
/* ISH IPC Base Address */
#define IPC_REG_BASE 0x0000
/* Peripheral Interrupt Status Register */
#define IPC_REG_PISR_CHV_AB (IPC_REG_BASE + 0x00)
/* Peripheral Interrupt Mask Register */
#define IPC_REG_PIMR_CHV_AB (IPC_REG_BASE + 0x04)
/*BXT, CHV_K0*/
/*Peripheral Interrupt Status Register */
#define IPC_REG_PISR_BXT (IPC_REG_BASE + 0x0C)
/*Peripheral Interrupt Mask Register */
#define IPC_REG_PIMR_BXT (IPC_REG_BASE + 0x08)
/***********************************/
/* ISH Host Firmware status Register */
#define IPC_REG_ISH_HOST_FWSTS (IPC_REG_BASE + 0x34)
/* Host Communication Register */
#define IPC_REG_HOST_COMM (IPC_REG_BASE + 0x38)
/* Reset register */
#define IPC_REG_ISH_RST (IPC_REG_BASE + 0x44)
/* Inbound doorbell register Host to ISH */
#define IPC_REG_HOST2ISH_DRBL (IPC_REG_BASE + 0x48)
/* Outbound doorbell register ISH to Host */
#define IPC_REG_ISH2HOST_DRBL (IPC_REG_BASE + 0x54)
/* ISH to HOST message registers */
#define IPC_REG_ISH2HOST_MSG (IPC_REG_BASE + 0x60)
/* HOST to ISH message registers */
#define IPC_REG_HOST2ISH_MSG (IPC_REG_BASE + 0xE0)
/* REMAP2 to enable DMA (D3 RCR) */
#define IPC_REG_ISH_RMP2 (IPC_REG_BASE + 0x368)
#define IPC_REG_MAX (IPC_REG_BASE + 0x400)
/*** register bits - HISR ***/
/* bit corresponds HOST2ISH interrupt in PISR and PIMR registers */
#define IPC_INT_HOST2ISH_BIT (1<<0)
/***********************************/
/*CHV_A0, CHV_B0*/
/* bit corresponds ISH2HOST interrupt in PISR and PIMR registers */
#define IPC_INT_ISH2HOST_BIT_CHV_AB (1<<3)
/*BXT, CHV_K0*/
/* bit corresponds ISH2HOST interrupt in PISR and PIMR registers */
#define IPC_INT_ISH2HOST_BIT_BXT (1<<0)
/***********************************/
/* bit corresponds ISH2HOST busy clear interrupt in PIMR register */
#define IPC_INT_ISH2HOST_CLR_MASK_BIT (1<<11)
/* offset of ISH2HOST busy clear interrupt in IPC_BUSY_CLR register */
#define IPC_INT_ISH2HOST_CLR_OFFS (0)
/* bit corresponds ISH2HOST busy clear interrupt in IPC_BUSY_CLR register */
#define IPC_INT_ISH2HOST_CLR_BIT (1<<IPC_INT_ISH2HOST_CLR_OFFS)
/* bit corresponds busy bit in doorbell registers */
#define IPC_DRBL_BUSY_OFFS (31)
#define IPC_DRBL_BUSY_BIT (1<<IPC_DRBL_BUSY_OFFS)
#define IPC_HOST_OWNS_MSG_OFFS (30)
/*
* A0: bit means that host owns MSGnn registers and is reading them.
* ISH FW may not write to them
*/
#define IPC_HOST_OWNS_MSG_BIT (1<<IPC_HOST_OWNS_MSG_OFFS)
/*
* Host status bits (HOSTCOMM)
*/
/* bit corresponds host ready bit in Host Status Register (HOST_COMM) */
#define IPC_HOSTCOMM_READY_OFFS (7)
#define IPC_HOSTCOMM_READY_BIT (1<<IPC_HOSTCOMM_READY_OFFS)
/***********************************/
/*CHV_A0, CHV_B0*/
#define IPC_HOSTCOMM_INT_EN_OFFS_CHV_AB (31)
#define IPC_HOSTCOMM_INT_EN_BIT_CHV_AB \
(1<<IPC_HOSTCOMM_INT_EN_OFFS_CHV_AB)
/*BXT, CHV_K0*/
#define IPC_PIMR_INT_EN_OFFS_BXT (0)
#define IPC_PIMR_INT_EN_BIT_BXT (1<<IPC_PIMR_INT_EN_OFFS_BXT)
#define IPC_HOST2ISH_BUSYCLEAR_MASK_OFFS_BXT (8)
#define IPC_HOST2ISH_BUSYCLEAR_MASK_BIT \
(1<<IPC_HOST2ISH_BUSYCLEAR_MASK_OFFS_BXT)
/***********************************/
/*
* both Host and ISH have ILUP at bit 0
* bit corresponds host ready bit in both status registers
*/
#define IPC_ILUP_OFFS (0)
#define IPC_ILUP_BIT (1<<IPC_ILUP_OFFS)
/*
* FW status bits (relevant)
*/
#define IPC_FWSTS_ILUP 0x1
#define IPC_FWSTS_ISHTP_UP (1<<1)
#define IPC_FWSTS_DMA0 (1<<16)
#define IPC_FWSTS_DMA1 (1<<17)
#define IPC_FWSTS_DMA2 (1<<18)
#define IPC_FWSTS_DMA3 (1<<19)
#define IPC_ISH_IN_DMA \
(IPC_FWSTS_DMA0 | IPC_FWSTS_DMA1 | IPC_FWSTS_DMA2 | IPC_FWSTS_DMA3)
/* bit corresponds host ready bit in ISH FW Status Register */
#define IPC_ISH_ISHTP_READY_OFFS (1)
#define IPC_ISH_ISHTP_READY_BIT (1<<IPC_ISH_ISHTP_READY_OFFS)
#define IPC_RMP2_DMA_ENABLED 0x1 /* Value to enable DMA, per D3 RCR */
#define IPC_MSG_MAX_SIZE 0x80
#define IPC_HEADER_LENGTH_MASK 0x03FF
#define IPC_HEADER_PROTOCOL_MASK 0x0F
#define IPC_HEADER_MNG_CMD_MASK 0x0F
#define IPC_HEADER_LENGTH_OFFSET 0
#define IPC_HEADER_PROTOCOL_OFFSET 10
#define IPC_HEADER_MNG_CMD_OFFSET 16
#define IPC_HEADER_GET_LENGTH(drbl_reg) \
(((drbl_reg) >> IPC_HEADER_LENGTH_OFFSET)&IPC_HEADER_LENGTH_MASK)
#define IPC_HEADER_GET_PROTOCOL(drbl_reg) \
(((drbl_reg) >> IPC_HEADER_PROTOCOL_OFFSET)&IPC_HEADER_PROTOCOL_MASK)
#define IPC_HEADER_GET_MNG_CMD(drbl_reg) \
(((drbl_reg) >> IPC_HEADER_MNG_CMD_OFFSET)&IPC_HEADER_MNG_CMD_MASK)
#define IPC_IS_BUSY(drbl_reg) \
(((drbl_reg)&IPC_DRBL_BUSY_BIT) == ((uint32_t)IPC_DRBL_BUSY_BIT))
/***********************************/
/*CHV_A0, CHV_B0*/
#define IPC_INT_FROM_ISH_TO_HOST_CHV_AB(drbl_reg) \
(((drbl_reg)&IPC_INT_ISH2HOST_BIT_CHV_AB) == \
((u32)IPC_INT_ISH2HOST_BIT_CHV_AB))
/*BXT, CHV_K0*/
#define IPC_INT_FROM_ISH_TO_HOST_BXT(drbl_reg) \
(((drbl_reg)&IPC_INT_ISH2HOST_BIT_BXT) == \
((u32)IPC_INT_ISH2HOST_BIT_BXT))
/***********************************/
#define IPC_BUILD_HEADER(length, protocol, busy) \
(((busy)<<IPC_DRBL_BUSY_OFFS) | \
((protocol) << IPC_HEADER_PROTOCOL_OFFSET) | \
((length)<<IPC_HEADER_LENGTH_OFFSET))
#define IPC_BUILD_MNG_MSG(cmd, length) \
(((1)<<IPC_DRBL_BUSY_OFFS)| \
((IPC_PROTOCOL_MNG)<<IPC_HEADER_PROTOCOL_OFFSET)| \
((cmd)<<IPC_HEADER_MNG_CMD_OFFSET)| \
((length)<<IPC_HEADER_LENGTH_OFFSET))
#define IPC_SET_HOST_READY(host_status) \
((host_status) |= (IPC_HOSTCOMM_READY_BIT))
#define IPC_SET_HOST_ILUP(host_status) \
((host_status) |= (IPC_ILUP_BIT))
#define IPC_CLEAR_HOST_READY(host_status) \
((host_status) ^= (IPC_HOSTCOMM_READY_BIT))
#define IPC_CLEAR_HOST_ILUP(host_status) \
((host_status) ^= (IPC_ILUP_BIT))
/* todo - temp until PIMR HW ready */
#define IPC_HOST_BUSY_READING_OFFS 6
/* bit corresponds host ready bit in Host Status Register (HOST_COMM) */
#define IPC_HOST_BUSY_READING_BIT (1<<IPC_HOST_BUSY_READING_OFFS)
#define IPC_SET_HOST_BUSY_READING(host_status) \
((host_status) |= (IPC_HOST_BUSY_READING_BIT))
#define IPC_CLEAR_HOST_BUSY_READING(host_status)\
((host_status) ^= (IPC_HOST_BUSY_READING_BIT))
#define IPC_IS_ISH_ISHTP_READY(ish_status) \
(((ish_status) & IPC_ISH_ISHTP_READY_BIT) == \
((uint32_t)IPC_ISH_ISHTP_READY_BIT))
#define IPC_IS_ISH_ILUP(ish_status) \
(((ish_status) & IPC_ILUP_BIT) == ((uint32_t)IPC_ILUP_BIT))
#define IPC_PROTOCOL_ISHTP 1
#define IPC_PROTOCOL_MNG 3
#define MNG_RX_CMPL_ENABLE 0
#define MNG_RX_CMPL_DISABLE 1
#define MNG_RX_CMPL_INDICATION 2
#define MNG_RESET_NOTIFY 3
#define MNG_RESET_NOTIFY_ACK 4
#define MNG_SYNC_FW_CLOCK 5
#define MNG_ILLEGAL_CMD 0xFF
#endif /* _ISHTP_ISH_REGS_H_ */
/*
* H/W layer of ISHTP provider device (ISH)
*
* Copyright (c) 2014-2016, Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/
#ifndef _ISHTP_HW_ISH_H_
#define _ISHTP_HW_ISH_H_
#include <linux/pci.h>
#include <linux/interrupt.h>
#include "hw-ish-regs.h"
#include "ishtp-dev.h"
#define CHV_DEVICE_ID 0x22D8
#define BXT_Ax_DEVICE_ID 0x0AA2
#define BXT_Bx_DEVICE_ID 0x1AA2
#define APL_Ax_DEVICE_ID 0x5AA2
#define SPT_Ax_DEVICE_ID 0x9D35
#define REVISION_ID_CHT_A0 0x6
#define REVISION_ID_CHT_Ax_SI 0x0
#define REVISION_ID_CHT_Bx_SI 0x10
#define REVISION_ID_CHT_Kx_SI 0x20
#define REVISION_ID_CHT_Dx_SI 0x30
#define REVISION_ID_CHT_B0 0xB0
#define REVISION_ID_SI_MASK 0x70
struct ipc_rst_payload_type {
uint16_t reset_id;
uint16_t reserved;
};
struct time_sync_format {
uint8_t ts1_source;
uint8_t ts2_source;
uint16_t reserved;
} __packed;
struct ipc_time_update_msg {
uint64_t primary_host_time;
struct time_sync_format sync_info;
uint64_t secondary_host_time;
} __packed;
enum {
HOST_UTC_TIME_USEC = 0,
HOST_SYSTEM_TIME_USEC = 1
};
struct ish_hw {
void __iomem *mem_addr;
};
#define to_ish_hw(dev) (struct ish_hw *)((dev)->hw)
irqreturn_t ish_irq_handler(int irq, void *dev_id);
struct ishtp_device *ish_dev_init(struct pci_dev *pdev);
int ish_hw_start(struct ishtp_device *dev);
void ish_device_disable(struct ishtp_device *dev);
#endif /* _ISHTP_HW_ISH_H_ */
This diff is collapsed.
/*
* PCI glue for ISHTP provider device (ISH) driver
*
* Copyright (c) 2014-2016, Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/pci.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>
#include <linux/miscdevice.h>
#define CREATE_TRACE_POINTS
#include <trace/events/intel_ish.h>
#include "ishtp-dev.h"
#include "hw-ish.h"
static const struct pci_device_id ish_pci_tbl[] = {
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, CHV_DEVICE_ID)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, BXT_Ax_DEVICE_ID)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, BXT_Bx_DEVICE_ID)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, APL_Ax_DEVICE_ID)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, SPT_Ax_DEVICE_ID)},
{0, }
};
MODULE_DEVICE_TABLE(pci, ish_pci_tbl);
/**
* ish_event_tracer() - Callback function to dump trace messages
* @dev: ishtp device
* @format: printf style format
*
* Callback to direct log messages to Linux trace buffers
*/
static void ish_event_tracer(struct ishtp_device *dev, char *format, ...)
{
if (trace_ishtp_dump_enabled()) {
va_list args;
char tmp_buf[100];
va_start(args, format);
vsnprintf(tmp_buf, sizeof(tmp_buf), format, args);
va_end(args);
trace_ishtp_dump(tmp_buf);
}
}
/**
* ish_init() - Init function
* @dev: ishtp device
*
* This function initialize wait queues for suspend/resume and call
* calls hadware initialization function. This will initiate
* startup sequence
*
* Return: 0 for success or error code for failure
*/
static int ish_init(struct ishtp_device *dev)
{
int ret;
/* Set the state of ISH HW to start */
ret = ish_hw_start(dev);
if (ret) {
dev_err(dev->devc, "ISH: hw start failed.\n");
return ret;
}
/* Start the inter process communication to ISH processor */
ret = ishtp_start(dev);
if (ret) {
dev_err(dev->devc, "ISHTP: Protocol init failed.\n");
return ret;
}
return 0;
}
/**
* ish_probe() - PCI driver probe callback
* @pdev: pci device
* @ent: pci device id
*
* Initialize PCI function, setup interrupt and call for ISH initialization
*
* Return: 0 for success or error code for failure
*/
static int ish_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
struct ishtp_device *dev;
struct ish_hw *hw;
int ret;
/* enable pci dev */
ret = pci_enable_device(pdev);
if (ret) {
dev_err(&pdev->dev, "ISH: Failed to enable PCI device\n");
return ret;
}
/* set PCI host mastering */
pci_set_master(pdev);
/* pci request regions for ISH driver */
ret = pci_request_regions(pdev, KBUILD_MODNAME);
if (ret) {
dev_err(&pdev->dev, "ISH: Failed to get PCI regions\n");
goto disable_device;
}
/* allocates and initializes the ISH dev structure */
dev = ish_dev_init(pdev);
if (!dev) {
ret = -ENOMEM;
goto release_regions;
}
hw = to_ish_hw(dev);
dev->print_log = ish_event_tracer;
/* mapping IO device memory */
hw->mem_addr = pci_iomap(pdev, 0, 0);
if (!hw->mem_addr) {
dev_err(&pdev->dev, "ISH: mapping I/O range failure\n");
ret = -ENOMEM;
goto free_device;
}
dev->pdev = pdev;
pdev->dev_flags |= PCI_DEV_FLAGS_NO_D3;
/* request and enable interrupt */
ret = request_irq(pdev->irq, ish_irq_handler, IRQF_NO_SUSPEND,
KBUILD_MODNAME, dev);
if (ret) {
dev_err(&pdev->dev, "ISH: request IRQ failure (%d)\n",
pdev->irq);
goto free_device;
}
dev_set_drvdata(dev->devc, dev);
init_waitqueue_head(&dev->suspend_wait);
init_waitqueue_head(&dev->resume_wait);
ret = ish_init(dev);
if (ret)
goto free_irq;
return 0;
free_irq:
free_irq(pdev->irq, dev);
free_device:
pci_iounmap(pdev, hw->mem_addr);
kfree(dev);
release_regions:
pci_release_regions(pdev);
disable_device:
pci_clear_master(pdev);
pci_disable_device(pdev);
dev_err(&pdev->dev, "ISH: PCI driver initialization failed.\n");
return ret;
}
/**
* ish_remove() - PCI driver remove callback
* @pdev: pci device
*
* This function does cleanup of ISH on pci remove callback
*/
static void ish_remove(struct pci_dev *pdev)
{
struct ishtp_device *ishtp_dev = pci_get_drvdata(pdev);
struct ish_hw *hw = to_ish_hw(ishtp_dev);
ishtp_bus_remove_all_clients(ishtp_dev, false);
ish_device_disable(ishtp_dev);
free_irq(pdev->irq, ishtp_dev);
pci_iounmap(pdev, hw->mem_addr);
pci_release_regions(pdev);
pci_clear_master(pdev);
pci_disable_device(pdev);
kfree(ishtp_dev);
}
static struct device *ish_resume_device;
/**
* ish_resume_handler() - Work function to complete resume
* @work: work struct
*
* The resume work function to complete resume function asynchronously.
* There are two types of platforms, one where ISH is not powered off,
* in that case a simple resume message is enough, others we need
* a reset sequence.
*/
static void ish_resume_handler(struct work_struct *work)
{
struct pci_dev *pdev = to_pci_dev(ish_resume_device);
struct ishtp_device *dev = pci_get_drvdata(pdev);
int ret;
ishtp_send_resume(dev);
/* 50 ms to get resume response */
if (dev->resume_flag)
ret = wait_event_interruptible_timeout(dev->resume_wait,
!dev->resume_flag,
msecs_to_jiffies(50));
/*
* If no resume response. This platform is not S0ix compatible
* So on resume full reboot of ISH processor will happen, so
* need to go through init sequence again
*/
if (dev->resume_flag)
ish_init(dev);
}
/**
* ish_suspend() - ISH suspend callback
* @device: device pointer
*
* ISH suspend callback
*
* Return: 0 to the pm core
*/
static int ish_suspend(struct device *device)
{
struct pci_dev *pdev = to_pci_dev(device);
struct ishtp_device *dev = pci_get_drvdata(pdev);
enable_irq_wake(pdev->irq);
/*
* If previous suspend hasn't been asnwered then ISH is likely dead,
* don't attempt nested notification
*/
if (dev->suspend_flag)
return 0;
dev->resume_flag = 0;
dev->suspend_flag = 1;
ishtp_send_suspend(dev);
/* 25 ms should be enough for live ISH to flush all IPC buf */
if (dev->suspend_flag)
wait_event_interruptible_timeout(dev->suspend_wait,
!dev->suspend_flag,
msecs_to_jiffies(25));
return 0;
}
static DECLARE_WORK(resume_work, ish_resume_handler);
/**
* ish_resume() - ISH resume callback
* @device: device pointer
*
* ISH resume callback
*
* Return: 0 to the pm core
*/
static int ish_resume(struct device *device)
{
struct pci_dev *pdev = to_pci_dev(device);
struct ishtp_device *dev = pci_get_drvdata(pdev);
ish_resume_device = device;
dev->resume_flag = 1;
disable_irq_wake(pdev->irq);
schedule_work(&resume_work);
return 0;
}
#ifdef CONFIG_PM
static const struct dev_pm_ops ish_pm_ops = {
.suspend = ish_suspend,
.resume = ish_resume,
};
#define ISHTP_ISH_PM_OPS (&ish_pm_ops)
#else
#define ISHTP_ISH_PM_OPS NULL
#endif
static struct pci_driver ish_driver = {
.name = KBUILD_MODNAME,
.id_table = ish_pci_tbl,
.probe = ish_probe,
.remove = ish_remove,
.driver.pm = ISHTP_ISH_PM_OPS,
};
module_pci_driver(ish_driver);
/* Original author */
MODULE_AUTHOR("Daniel Drubin <daniel.drubin@intel.com>");
/* Adoption to upstream Linux kernel */
MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
MODULE_DESCRIPTION("Intel(R) Integrated Sensor Hub PCI Device Driver");
MODULE_LICENSE("GPL");
/*
* Utility macros of ISH
*
* Copyright (c) 2014-2016, Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/
#ifndef UTILS__H
#define UTILS__H
#define WAIT_FOR_SEND_SLICE (HZ / 10)
#define WAIT_FOR_CONNECT_SLICE (HZ / 10)
/*
* Waits for specified event when a thread that triggers event can't signal
* Also, waits *at_least* `timeinc` after condition is satisfied
*/
#define timed_wait_for(timeinc, condition) \
do { \
int completed = 0; \
do { \
unsigned long j; \
int done = 0; \
\
completed = (condition); \
for (j = jiffies, done = 0; !done; ) { \
schedule_timeout(timeinc); \
if (time_is_before_eq_jiffies(j + timeinc)) \
done = 1; \
} \
} while (!(completed)); \
} while (0)
/*
* Waits for specified event when a thread that triggers event
* can't signal with timeout (use whenever we may hang)
*/
#define timed_wait_for_timeout(timeinc, condition, timeout) \
do { \
int t = timeout; \
do { \
unsigned long j; \
int done = 0; \
\
for (j = jiffies, done = 0; !done; ) { \
schedule_timeout(timeinc); \
if (time_is_before_eq_jiffies(j + timeinc)) \
done = 1; \
} \
t -= timeinc; \
if (t <= 0) \
break; \
} while (!(condition)); \
} while (0)
#endif /* UTILS__H */
This diff is collapsed.
/*
* ISHTP-HID glue driver.
*
* Copyright (c) 2012-2016, Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/
#include <linux/hid.h>
#include <uapi/linux/input.h>
#include "ishtp/client.h"
#include "ishtp-hid.h"
/**
* ishtp_hid_parse() - hid-core .parse() callback
* @hid: hid device instance
*
* This function gets called during call to hid_add_device
*
* Return: 0 on success and non zero on error
*/
static int ishtp_hid_parse(struct hid_device *hid)
{
struct ishtp_hid_data *hid_data = hid->driver_data;
struct ishtp_cl_data *client_data = hid_data->client_data;
int rv;
rv = hid_parse_report(hid, client_data->report_descr[hid_data->index],
client_data->report_descr_size[hid_data->index]);
if (rv)
return rv;
return 0;
}
/* Empty callbacks with success return code */
static int ishtp_hid_start(struct hid_device *hid)
{
return 0;
}
static void ishtp_hid_stop(struct hid_device *hid)
{
}
static int ishtp_hid_open(struct hid_device *hid)
{
return 0;
}
static void ishtp_hid_close(struct hid_device *hid)
{
}
static int ishtp_raw_request(struct hid_device *hdev, unsigned char reportnum,
__u8 *buf, size_t len, unsigned char rtype, int reqtype)
{
return 0;
}
/**
* ishtp_hid_request() - hid-core .request() callback
* @hid: hid device instance
* @rep: pointer to hid_report
* @reqtype: type of req. [GET|SET]_REPORT
*
* This function is used to set/get feaure/input report.
*/
static void ishtp_hid_request(struct hid_device *hid, struct hid_report *rep,
int reqtype)
{
struct ishtp_hid_data *hid_data = hid->driver_data;
/* the specific report length, just HID part of it */
unsigned int len = ((rep->size - 1) >> 3) + 1 + (rep->id > 0);
char *buf;
unsigned int header_size = sizeof(struct hostif_msg);
len += header_size;
hid_data->request_done = false;
switch (reqtype) {
case HID_REQ_GET_REPORT:
hid_ishtp_get_report(hid, rep->id, rep->type);
break;
case HID_REQ_SET_REPORT:
/*
* Spare 7 bytes for 64b accesses through
* get/put_unaligned_le64()
*/
buf = kzalloc(len + 7, GFP_KERNEL);
if (!buf)
return;
hid_output_report(rep, buf + header_size);
hid_ishtp_set_feature(hid, buf, len, rep->id);
kfree(buf);
break;
}
}
/**
* ishtp_wait_for_response() - hid-core .wait() callback
* @hid: hid device instance
*
* This function is used to wait after get feaure/input report.
*
* Return: 0 on success and non zero on error
*/
static int ishtp_wait_for_response(struct hid_device *hid)
{
struct ishtp_hid_data *hid_data = hid->driver_data;
struct ishtp_cl_data *client_data = hid_data->client_data;
int rv;
hid_ishtp_trace(client_data, "%s hid %p\n", __func__, hid);
rv = ishtp_hid_link_ready_wait(hid_data->client_data);
if (rv)
return rv;
if (!hid_data->request_done)
wait_event_interruptible_timeout(hid_data->hid_wait,
hid_data->request_done, 3 * HZ);
if (!hid_data->request_done) {
hid_err(hid,
"timeout waiting for response from ISHTP device\n");
return -ETIMEDOUT;
}
hid_ishtp_trace(client_data, "%s hid %p done\n", __func__, hid);
hid_data->request_done = false;
return 0;
}
/**
* ishtp_hid_wakeup() - Wakeup caller
* @hid: hid device instance
*
* This function will wakeup caller waiting for Get/Set feature report
*/
void ishtp_hid_wakeup(struct hid_device *hid)
{
struct ishtp_hid_data *hid_data = hid->driver_data;
hid_data->request_done = true;
wake_up_interruptible(&hid_data->hid_wait);
}
static struct hid_ll_driver ishtp_hid_ll_driver = {
.parse = ishtp_hid_parse,
.start = ishtp_hid_start,
.stop = ishtp_hid_stop,
.open = ishtp_hid_open,
.close = ishtp_hid_close,
.request = ishtp_hid_request,
.wait = ishtp_wait_for_response,
.raw_request = ishtp_raw_request
};
/**
* ishtp_hid_probe() - hid register ll driver
* @cur_hid_dev: Index of hid device calling to register
* @client_data: Client data pointer
*
* This function is used to allocate and add HID device.
*
* Return: 0 on success, non zero on error
*/
int ishtp_hid_probe(unsigned int cur_hid_dev,
struct ishtp_cl_data *client_data)
{
int rv;
struct hid_device *hid;
struct ishtp_hid_data *hid_data;
hid = hid_allocate_device();
if (IS_ERR(hid)) {
rv = PTR_ERR(hid);
return -ENOMEM;
}
hid_data = kzalloc(sizeof(*hid_data), GFP_KERNEL);
if (!hid_data) {
rv = -ENOMEM;
goto err_hid_data;
}
hid_data->index = cur_hid_dev;
hid_data->client_data = client_data;
init_waitqueue_head(&hid_data->hid_wait);
hid->driver_data = hid_data;
client_data->hid_sensor_hubs[cur_hid_dev] = hid;
hid->ll_driver = &ishtp_hid_ll_driver;
hid->bus = BUS_INTEL_ISHTP;
hid->dev.parent = &client_data->cl_device->dev;
hid->version = le16_to_cpu(ISH_HID_VERSION);
hid->vendor = le16_to_cpu(ISH_HID_VENDOR);
hid->product = le16_to_cpu(ISH_HID_PRODUCT);
snprintf(hid->name, sizeof(hid->name), "%s %04hX:%04hX", "hid-ishtp",
hid->vendor, hid->product);
rv = hid_add_device(hid);
if (rv)
goto err_hid_device;
hid_ishtp_trace(client_data, "%s allocated hid %p\n", __func__, hid);
return 0;
err_hid_device:
kfree(hid_data);
err_hid_data:
kfree(hid);
return rv;
}
/**
* ishtp_hid_probe() - Remove registered hid device
* @client_data: client data pointer
*
* This function is used to destroy allocatd HID device.
*/
void ishtp_hid_remove(struct ishtp_cl_data *client_data)
{
int i;
for (i = 0; i < client_data->num_hid_devices; ++i) {
if (client_data->hid_sensor_hubs[i]) {
kfree(client_data->hid_sensor_hubs[i]->driver_data);
hid_destroy_device(client_data->hid_sensor_hubs[i]);
client_data->hid_sensor_hubs[i] = NULL;
}
}
}
/*
* ISHTP-HID glue driver's definitions.
*
* Copyright (c) 2014-2016, Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/
#ifndef ISHTP_HID__H
#define ISHTP_HID__H
/* The fixed ISH product and vendor id */
#define ISH_HID_VENDOR 0x8086
#define ISH_HID_PRODUCT 0x22D8
#define ISH_HID_VERSION 0x0200
#define CMD_MASK 0x7F
#define IS_RESPONSE 0x80
/* Used to dump to Linux trace buffer, if enabled */
#define hid_ishtp_trace(client, ...) \
client->cl_device->ishtp_dev->print_log(\
client->cl_device->ishtp_dev, __VA_ARGS__)
/* ISH Transport protocol (ISHTP in short) GUID */
static const uuid_le hid_ishtp_guid = UUID_LE(0x33AECD58, 0xB679, 0x4E54,
0x9B, 0xD9, 0xA0, 0x4D, 0x34,
0xF0, 0xC2, 0x26);
/* ISH HID message structure */
struct hostif_msg_hdr {
uint8_t command; /* Bit 7: is_response */
uint8_t device_id;
uint8_t status;
uint8_t flags;
uint16_t size;
} __packed;
struct hostif_msg {
struct hostif_msg_hdr hdr;
} __packed;
struct hostif_msg_to_sensor {
struct hostif_msg_hdr hdr;
uint8_t report_id;
} __packed;
struct device_info {
uint32_t dev_id;
uint8_t dev_class;
uint16_t pid;
uint16_t vid;
} __packed;
struct ishtp_version {
uint8_t major;
uint8_t minor;
uint8_t hotfix;
uint16_t build;
} __packed;
/* struct for ISHTP aggregated input data */
struct report_list {
uint16_t total_size;
uint8_t num_of_reports;
uint8_t flags;
struct {
uint16_t size_of_report;
uint8_t report[1];
} __packed reports[1];
} __packed;
/* HOSTIF commands */
#define HOSTIF_HID_COMMAND_BASE 0
#define HOSTIF_GET_HID_DESCRIPTOR 0
#define HOSTIF_GET_REPORT_DESCRIPTOR 1
#define HOSTIF_GET_FEATURE_REPORT 2
#define HOSTIF_SET_FEATURE_REPORT 3
#define HOSTIF_GET_INPUT_REPORT 4
#define HOSTIF_PUBLISH_INPUT_REPORT 5
#define HOSTIF_PUBLISH_INPUT_REPORT_LIST 6
#define HOSTIF_DM_COMMAND_BASE 32
#define HOSTIF_DM_ENUM_DEVICES 33
#define HOSTIF_DM_ADD_DEVICE 34
#define MAX_HID_DEVICES 32
/**
* struct ishtp_cl_data - Encapsulate per ISH TP HID Client
* @enum_device_done: Enum devices response complete flag
* @hid_descr_done: HID descriptor complete flag
* @report_descr_done: Get report descriptor complete flag
* @init_done: Init process completed successfully
* @suspended: System is under suspend state or in progress
* @num_hid_devices: Number of HID devices enumerated in this client
* @cur_hid_dev: This keeps track of the device index for which
* initialization and registration with HID core
* in progress.
* @hid_devices: Store vid/pid/devid for each enumerated HID device
* @report_descr: Stores the raw report descriptors for each HID device
* @report_descr_size: Report description of size of above repo_descr[]
* @hid_sensor_hubs: Pointer to hid_device for all HID device, so that
* when clients are removed, they can be freed
* @hid_descr: Pointer to hid descriptor for each enumerated hid
* device
* @hid_descr_size: Size of each above report descriptor
* @init_wait: Wait queue to wait during initialization, where the
* client send message to ISH FW and wait for response
* @ishtp_hid_wait: The wait for get report during wait callback from hid
* core
* @bad_recv_cnt: Running count of packets received with error
* @multi_packet_cnt: Count of fragmented packet count
*
* This structure is used to store completion flags and per client data like
* like report description, number of HID devices etc.
*/
struct ishtp_cl_data {
/* completion flags */
bool enum_devices_done;
bool hid_descr_done;
bool report_descr_done;
bool init_done;
bool suspended;
unsigned int num_hid_devices;
unsigned int cur_hid_dev;
unsigned int hid_dev_count;
struct device_info *hid_devices;
unsigned char *report_descr[MAX_HID_DEVICES];
int report_descr_size[MAX_HID_DEVICES];
struct hid_device *hid_sensor_hubs[MAX_HID_DEVICES];
unsigned char *hid_descr[MAX_HID_DEVICES];
int hid_descr_size[MAX_HID_DEVICES];
wait_queue_head_t init_wait;
wait_queue_head_t ishtp_resume_wait;
struct ishtp_cl *hid_ishtp_cl;
/* Statistics */
unsigned int bad_recv_cnt;
int multi_packet_cnt;
struct work_struct work;
struct ishtp_cl_device *cl_device;
};
/**
* struct ishtp_hid_data - Per instance HID data
* @index: Device index in the order of enumeration
* @request_done: Get Feature/Input report complete flag
* used during get/set request from hid core
* @client_data: Link to the client instance
* @hid_wait: Completion waitq
*
* Used to tie hid hid->driver data to driver client instance
*/
struct ishtp_hid_data {
int index;
bool request_done;
struct ishtp_cl_data *client_data;
wait_queue_head_t hid_wait;
};
/* Interface functions between HID LL driver and ISH TP client */
void hid_ishtp_set_feature(struct hid_device *hid, char *buf, unsigned int len,
int report_id);
void hid_ishtp_get_report(struct hid_device *hid, int report_id,
int report_type);
int ishtp_hid_probe(unsigned int cur_hid_dev,
struct ishtp_cl_data *client_data);
void ishtp_hid_remove(struct ishtp_cl_data *client_data);
int ishtp_hid_link_ready_wait(struct ishtp_cl_data *client_data);
void ishtp_hid_wakeup(struct hid_device *hid);
#endif /* ISHTP_HID__H */
This diff is collapsed.
/*
* ISHTP bus definitions
*
* Copyright (c) 2014-2016, Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/
#ifndef _LINUX_ISHTP_CL_BUS_H
#define _LINUX_ISHTP_CL_BUS_H
#include <linux/device.h>
#include <linux/mod_devicetable.h>
struct ishtp_cl;
struct ishtp_cl_device;
struct ishtp_device;
struct ishtp_msg_hdr;
/**
* struct ishtp_cl_device - ISHTP device handle
* @dev: device pointer
* @ishtp_dev: pointer to ishtp device structure to primarily to access
* hw device operation callbacks and properties
* @fw_client: fw_client pointer to get fw information like protocol name
* max message length etc.
* @device_link: Link to next client in the list on a bus
* @event_work: Used to schedule rx event for client
* @driver_data: Storage driver private data
* @reference_count: Used for get/put device
* @event_cb: Callback to driver to send events
*
* An ishtp_cl_device pointer is returned from ishtp_add_device()
* and links ISHTP bus clients to their actual host client pointer.
* Drivers for ISHTP devices will get an ishtp_cl_device pointer
* when being probed and shall use it for doing bus I/O.
*/
struct ishtp_cl_device {
struct device dev;
struct ishtp_device *ishtp_dev;
struct ishtp_fw_client *fw_client;
struct list_head device_link;
struct work_struct event_work;
void *driver_data;
int reference_count;
void (*event_cb)(struct ishtp_cl_device *device);
};
/**
* struct ishtp_cl_device - ISHTP device handle
* @driver: driver instance on a bus
* @name: Name of the device for probe
* @probe: driver callback for device probe
* @remove: driver callback on device removal
*
* Client drivers defines to get probed/removed for ISHTP client device.
*/
struct ishtp_cl_driver {
struct device_driver driver;
const char *name;
int (*probe)(struct ishtp_cl_device *dev);
int (*remove)(struct ishtp_cl_device *dev);
int (*reset)(struct ishtp_cl_device *dev);
const struct dev_pm_ops *pm;
};
int ishtp_bus_new_client(struct ishtp_device *dev);
void ishtp_remove_all_clients(struct ishtp_device *dev);
int ishtp_cl_device_bind(struct ishtp_cl *cl);
void ishtp_cl_bus_rx_event(struct ishtp_cl_device *device);
/* Write a multi-fragment message */
int ishtp_send_msg(struct ishtp_device *dev,
struct ishtp_msg_hdr *hdr, void *msg,
void (*ipc_send_compl)(void *),
void *ipc_send_compl_prm);
/* Write a single-fragment message */
int ishtp_write_message(struct ishtp_device *dev,
struct ishtp_msg_hdr *hdr,
unsigned char *buf);
/* Use DMA to send/receive messages */
int ishtp_use_dma_transfer(void);
/* Exported functions */
void ishtp_bus_remove_all_clients(struct ishtp_device *ishtp_dev,
bool warm_reset);
void ishtp_recv(struct ishtp_device *dev);
void ishtp_reset_handler(struct ishtp_device *dev);
void ishtp_reset_compl_handler(struct ishtp_device *dev);
void ishtp_put_device(struct ishtp_cl_device *);
void ishtp_get_device(struct ishtp_cl_device *);
int __ishtp_cl_driver_register(struct ishtp_cl_driver *driver,
struct module *owner);
#define ishtp_cl_driver_register(driver) \
__ishtp_cl_driver_register(driver, THIS_MODULE)
void ishtp_cl_driver_unregister(struct ishtp_cl_driver *driver);
int ishtp_register_event_cb(struct ishtp_cl_device *device,
void (*read_cb)(struct ishtp_cl_device *));
int ishtp_fw_cl_by_uuid(struct ishtp_device *dev, const uuid_le *cuuid);
#endif /* _LINUX_ISHTP_CL_BUS_H */
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -76,6 +76,7 @@ static const struct hid_blacklist { ...@@ -76,6 +76,7 @@ static const struct hid_blacklist {
{ USB_VENDOR_ID_CORSAIR, USB_DEVICE_ID_CORSAIR_K95RGB, HID_QUIRK_NO_INIT_REPORTS | HID_QUIRK_ALWAYS_POLL }, { USB_VENDOR_ID_CORSAIR, USB_DEVICE_ID_CORSAIR_K95RGB, HID_QUIRK_NO_INIT_REPORTS | HID_QUIRK_ALWAYS_POLL },
{ USB_VENDOR_ID_CORSAIR, USB_DEVICE_ID_CORSAIR_K70RGB, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_CORSAIR, USB_DEVICE_ID_CORSAIR_K70RGB, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_CORSAIR, USB_DEVICE_ID_CORSAIR_K65RGB, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_CORSAIR, USB_DEVICE_ID_CORSAIR_K65RGB, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_CORSAIR, USB_DEVICE_ID_CORSAIR_STRAFE, HID_QUIRK_NO_INIT_REPORTS | HID_QUIRK_ALWAYS_POLL },
{ USB_VENDOR_ID_CREATIVELABS, USB_DEVICE_ID_CREATIVE_SB_OMNI_SURROUND_51, HID_QUIRK_NOGET }, { USB_VENDOR_ID_CREATIVELABS, USB_DEVICE_ID_CREATIVE_SB_OMNI_SURROUND_51, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_DMI, USB_DEVICE_ID_DMI_ENC, HID_QUIRK_NOGET }, { USB_VENDOR_ID_DMI, USB_DEVICE_ID_DMI_ENC, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_WIIU, HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_WIIU, HID_QUIRK_MULTI_INPUT },
...@@ -144,7 +145,7 @@ static const struct hid_blacklist { ...@@ -144,7 +145,7 @@ static const struct hid_blacklist {
{ USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_WIRELESS, HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_WIRELESS, HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_SIGMA_MICRO, USB_DEVICE_ID_SIGMA_MICRO_KEYBOARD, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_SIGMA_MICRO, USB_DEVICE_ID_SIGMA_MICRO_KEYBOARD, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_I608X, HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_I608X, HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_I608X_2, HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_I608X_V2, HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_M610X, HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_M610X, HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_PENSKETCH_M912, HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_PENSKETCH_M912, HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_DUOSENSE, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_DUOSENSE, HID_QUIRK_NO_INIT_REPORTS },
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -82,6 +82,7 @@ ...@@ -82,6 +82,7 @@
#define WACOM_DEVICETYPE_TOUCH 0x0002 #define WACOM_DEVICETYPE_TOUCH 0x0002
#define WACOM_DEVICETYPE_PAD 0x0004 #define WACOM_DEVICETYPE_PAD 0x0004
#define WACOM_DEVICETYPE_WL_MONITOR 0x0008 #define WACOM_DEVICETYPE_WL_MONITOR 0x0008
#define WACOM_DEVICETYPE_DIRECT 0x0010
#define WACOM_VENDORDEFINED_PEN 0xff0d0001 #define WACOM_VENDORDEFINED_PEN 0xff0d0001
#define WACOM_G9_PAGE 0xff090000 #define WACOM_G9_PAGE 0xff090000
...@@ -218,31 +219,30 @@ struct hid_data { ...@@ -218,31 +219,30 @@ struct hid_data {
int num_received; int num_received;
}; };
struct wacom_remote_data {
struct {
u32 serial;
bool connected;
} remote[WACOM_MAX_REMOTES];
};
struct wacom_wac { struct wacom_wac {
char name[WACOM_NAME_MAX];
char pen_name[WACOM_NAME_MAX]; char pen_name[WACOM_NAME_MAX];
char touch_name[WACOM_NAME_MAX]; char touch_name[WACOM_NAME_MAX];
char pad_name[WACOM_NAME_MAX]; char pad_name[WACOM_NAME_MAX];
char bat_name[WACOM_NAME_MAX];
char ac_name[WACOM_NAME_MAX];
unsigned char data[WACOM_PKGLEN_MAX]; unsigned char data[WACOM_PKGLEN_MAX];
int tool[2]; int tool[2];
int id[2]; int id[2];
__u32 serial[5]; __u32 serial[2];
bool reporting_data; bool reporting_data;
struct wacom_features features; struct wacom_features features;
struct wacom_shared *shared; struct wacom_shared *shared;
struct input_dev *pen_input; struct input_dev *pen_input;
struct input_dev *touch_input; struct input_dev *touch_input;
struct input_dev *pad_input; struct input_dev *pad_input;
bool pen_registered;
bool touch_registered;
bool pad_registered;
int pid; int pid;
int battery_capacity;
int num_contacts_left; int num_contacts_left;
int bat_charging;
int bat_connected;
int ps_connected;
u8 bt_features; u8 bt_features;
u8 bt_high_speed; u8 bt_high_speed;
int mode_report; int mode_report;
......
...@@ -837,7 +837,7 @@ __u32 hid_field_extract(const struct hid_device *hid, __u8 *report, ...@@ -837,7 +837,7 @@ __u32 hid_field_extract(const struct hid_device *hid, __u8 *report,
*/ */
static inline void hid_device_io_start(struct hid_device *hid) { static inline void hid_device_io_start(struct hid_device *hid) {
if (hid->io_started) { if (hid->io_started) {
dev_warn(&hid->dev, "io already started"); dev_warn(&hid->dev, "io already started\n");
return; return;
} }
hid->io_started = true; hid->io_started = true;
...@@ -857,7 +857,7 @@ static inline void hid_device_io_start(struct hid_device *hid) { ...@@ -857,7 +857,7 @@ static inline void hid_device_io_start(struct hid_device *hid) {
*/ */
static inline void hid_device_io_stop(struct hid_device *hid) { static inline void hid_device_io_stop(struct hid_device *hid) {
if (!hid->io_started) { if (!hid->io_started) {
dev_warn(&hid->dev, "io already stopped"); dev_warn(&hid->dev, "io already stopped\n");
return; return;
} }
hid->io_started = false; hid->io_started = false;
......
#undef TRACE_SYSTEM
#define TRACE_SYSTEM intel_ish
#if !defined(_TRACE_INTEL_ISH_H) || defined(TRACE_HEADER_MULTI_READ)
#define _TRACE_INTEL_ISH_H
#include <linux/tracepoint.h>
TRACE_EVENT(ishtp_dump,
TP_PROTO(const char *message),
TP_ARGS(message),
TP_STRUCT__entry(
__string(message, message)
),
TP_fast_assign(
__assign_str(message, message);
),
TP_printk("%s", __get_str(message))
);
#endif /* _TRACE_INTEL_ISH_H */
/* This part must be outside protection */
#include <trace/define_trace.h>
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment