mirror of
https://github.com/torvalds/linux.git
synced 2024-11-11 14:42:24 +00:00
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid into next
Pull HID patches from Jiri Kosina: - RMI driver for Synaptics touchpads, by Benjamin Tissoires, Andrew Duggan and Jiri Kosina - cleanup of hid-sony driver and improved support for Sixaxis and Dualshock 4, by Frank Praznik - other usual small fixes and support for new device IDs * 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid: (29 commits) HID: thingm: thingm_fwinfo[] doesn't need to be global HID: core: add two new usages for digitizer HID: hid-sensor-hub: new device id and quirk for STM Sensor hub HID: usbhid: enable NO_INIT_REPORTS quirk for Semico USB Keykoard HID: hid-sensor-hub: Set report quirk for Microsoft Surface HID: debug: add labels for HID Sensor Usages HID: uhid: Use kmemdup instead of kmalloc + memcpy HID: rmi: do not handle touchscreens through hid-rmi HID: quirk for Saitek RAT7 and MMO7 mices' mode button HID: core: fix validation of report id 0 HID: rmi: fix masks for x and w_x data HID: rmi: fix wrong struct field name HID: rmi: do not fetch more than 16 bytes in a query HID: rmi: check for the existence of some optional queries before reading query 12 HID: i2c-hid: hid report descriptor retrieval changes HID: add missing hid usages HID: hid-sony - allow 3rd party INTEC controller to turn off all leds HID: sony: Add blink support to the Sixaxis and DualShock 4 LEDs HID: sony: Initialize the controller LEDs with a device ID value HID: sony: Use the controller Bluetooth MAC address as the unique value in the battery name string ...
This commit is contained in:
commit
d6b92c2c37
@ -1,23 +0,0 @@
|
||||
What: /sys/class/leds/blink1::<serial>/rgb
|
||||
Date: January 2013
|
||||
Contact: Vivien Didelot <vivien.didelot@savoirfairelinux.com>
|
||||
Description: The ThingM blink1 is an USB RGB LED. The color notation is
|
||||
3-byte hexadecimal. Read this attribute to get the last set
|
||||
color. Write the 24-bit hexadecimal color to change the current
|
||||
LED color. The default color is full white (0xFFFFFF).
|
||||
For instance, set the color to green with: echo 00FF00 > rgb
|
||||
|
||||
What: /sys/class/leds/blink1::<serial>/fade
|
||||
Date: January 2013
|
||||
Contact: Vivien Didelot <vivien.didelot@savoirfairelinux.com>
|
||||
Description: This attribute allows to set a fade time in milliseconds for
|
||||
the next color change. Read the attribute to know the current
|
||||
fade time. The default value is set to 0 (no fade time). For
|
||||
instance, set a fade time of 2 seconds with: echo 2000 > fade
|
||||
|
||||
What: /sys/class/leds/blink1::<serial>/play
|
||||
Date: January 2013
|
||||
Contact: Vivien Didelot <vivien.didelot@savoirfairelinux.com>
|
||||
Description: This attribute is used to play/pause the light patterns. Write 1
|
||||
to start playing, 0 to stop. Reading this attribute returns the
|
||||
current playing status.
|
@ -608,7 +608,10 @@ config HID_SAITEK
|
||||
Support for Saitek devices that are not fully compliant with the
|
||||
HID standard.
|
||||
|
||||
Currently only supports the PS1000 controller.
|
||||
Supported devices:
|
||||
- PS1000 Dual Analog Pad
|
||||
- R.A.T.7 Gaming Mouse
|
||||
- M.M.O.7 Gaming Mouse
|
||||
|
||||
config HID_SAMSUNG
|
||||
tristate "Samsung InfraRed remote control or keyboards"
|
||||
@ -657,6 +660,14 @@ config HID_SUNPLUS
|
||||
---help---
|
||||
Support for Sunplus wireless desktop.
|
||||
|
||||
config HID_RMI
|
||||
tristate "Synaptics RMI4 device support"
|
||||
depends on HID
|
||||
---help---
|
||||
Support for Synaptics RMI4 touchpads.
|
||||
Say Y here if you have a Synaptics RMI4 touchpads over i2c-hid or usbhid
|
||||
and want support for its special functionalities.
|
||||
|
||||
config HID_GREENASIA
|
||||
tristate "GreenAsia (Product ID 0x12) game controller support"
|
||||
depends on HID
|
||||
|
@ -97,6 +97,7 @@ obj-$(CONFIG_HID_ROCCAT) += hid-roccat.o hid-roccat-common.o \
|
||||
hid-roccat-arvo.o hid-roccat-isku.o hid-roccat-kone.o \
|
||||
hid-roccat-koneplus.o hid-roccat-konepure.o hid-roccat-kovaplus.o \
|
||||
hid-roccat-lua.o hid-roccat-pyra.o hid-roccat-ryos.o hid-roccat-savu.o
|
||||
obj-$(CONFIG_HID_RMI) += hid-rmi.o
|
||||
obj-$(CONFIG_HID_SAITEK) += hid-saitek.o
|
||||
obj-$(CONFIG_HID_SAMSUNG) += hid-samsung.o
|
||||
obj-$(CONFIG_HID_SMARTJOYPLUS) += hid-sjoy.o
|
||||
|
@ -779,6 +779,14 @@ static int hid_scan_report(struct hid_device *hid)
|
||||
(hid->group == HID_GROUP_MULTITOUCH))
|
||||
hid->group = HID_GROUP_MULTITOUCH_WIN_8;
|
||||
|
||||
/*
|
||||
* Vendor specific handlings
|
||||
*/
|
||||
if ((hid->vendor == USB_VENDOR_ID_SYNAPTICS) &&
|
||||
(hid->group == HID_GROUP_GENERIC))
|
||||
/* hid-rmi should take care of them, not hid-generic */
|
||||
hid->group = HID_GROUP_RMI;
|
||||
|
||||
vfree(parser);
|
||||
return 0;
|
||||
}
|
||||
@ -842,7 +850,17 @@ struct hid_report *hid_validate_values(struct hid_device *hid,
|
||||
* ->numbered being checked, which may not always be the case when
|
||||
* drivers go to access report values.
|
||||
*/
|
||||
report = hid->report_enum[type].report_id_hash[id];
|
||||
if (id == 0) {
|
||||
/*
|
||||
* Validating on id 0 means we should examine the first
|
||||
* report in the list.
|
||||
*/
|
||||
report = list_entry(
|
||||
hid->report_enum[type].report_list.next,
|
||||
struct hid_report, list);
|
||||
} else {
|
||||
report = hid->report_enum[type].report_id_hash[id];
|
||||
}
|
||||
if (!report) {
|
||||
hid_err(hid, "missing %s %u\n", hid_report_names[type], id);
|
||||
return NULL;
|
||||
@ -1868,7 +1886,11 @@ static const struct hid_device_id hid_have_special_driver[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_RYOS_MK_PRO) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_SAVU) },
|
||||
#endif
|
||||
#if IS_ENABLED(CONFIG_HID_SAITEK)
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_PS1000) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RAT7) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_MMO7) },
|
||||
#endif
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD_MOUSE) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SKYCABLE, USB_DEVICE_ID_SKYCABLE_WIRELESS_PRESENTER) },
|
||||
|
@ -165,6 +165,8 @@ static const struct hid_usage_entry hid_usage_table[] = {
|
||||
{0, 0x53, "DeviceIndex"},
|
||||
{0, 0x54, "ContactCount"},
|
||||
{0, 0x55, "ContactMaximumNumber"},
|
||||
{0, 0x5A, "SecondaryBarrelSwitch"},
|
||||
{0, 0x5B, "TransducerSerialNumber"},
|
||||
{ 15, 0, "PhysicalInterfaceDevice" },
|
||||
{0, 0x00, "Undefined"},
|
||||
{0, 0x01, "Physical_Interface_Device"},
|
||||
@ -272,6 +274,85 @@ static const struct hid_usage_entry hid_usage_table[] = {
|
||||
{0, 0xAA, "Shared_Parameter_Blocks"},
|
||||
{0, 0xAB, "Create_New_Effect_Report"},
|
||||
{0, 0xAC, "RAM_Pool_Available"},
|
||||
{ 0x20, 0, "Sensor" },
|
||||
{ 0x20, 0x01, "Sensor" },
|
||||
{ 0x20, 0x10, "Biometric" },
|
||||
{ 0x20, 0x11, "BiometricHumanPresence" },
|
||||
{ 0x20, 0x12, "BiometricHumanProximity" },
|
||||
{ 0x20, 0x13, "BiometricHumanTouch" },
|
||||
{ 0x20, 0x20, "Electrical" },
|
||||
{ 0x20, 0x21, "ElectricalCapacitance" },
|
||||
{ 0x20, 0x22, "ElectricalCurrent" },
|
||||
{ 0x20, 0x23, "ElectricalPower" },
|
||||
{ 0x20, 0x24, "ElectricalInductance" },
|
||||
{ 0x20, 0x25, "ElectricalResistance" },
|
||||
{ 0x20, 0x26, "ElectricalVoltage" },
|
||||
{ 0x20, 0x27, "ElectricalPoteniometer" },
|
||||
{ 0x20, 0x28, "ElectricalFrequency" },
|
||||
{ 0x20, 0x29, "ElectricalPeriod" },
|
||||
{ 0x20, 0x30, "Environmental" },
|
||||
{ 0x20, 0x31, "EnvironmentalAtmosphericPressure" },
|
||||
{ 0x20, 0x32, "EnvironmentalHumidity" },
|
||||
{ 0x20, 0x33, "EnvironmentalTemperature" },
|
||||
{ 0x20, 0x34, "EnvironmentalWindDirection" },
|
||||
{ 0x20, 0x35, "EnvironmentalWindSpeed" },
|
||||
{ 0x20, 0x40, "Light" },
|
||||
{ 0x20, 0x41, "LightAmbientLight" },
|
||||
{ 0x20, 0x42, "LightConsumerInfrared" },
|
||||
{ 0x20, 0x50, "Location" },
|
||||
{ 0x20, 0x51, "LocationBroadcast" },
|
||||
{ 0x20, 0x52, "LocationDeadReckoning" },
|
||||
{ 0x20, 0x53, "LocationGPS" },
|
||||
{ 0x20, 0x54, "LocationLookup" },
|
||||
{ 0x20, 0x55, "LocationOther" },
|
||||
{ 0x20, 0x56, "LocationStatic" },
|
||||
{ 0x20, 0x57, "LocationTriangulation" },
|
||||
{ 0x20, 0x60, "Mechanical" },
|
||||
{ 0x20, 0x61, "MechanicalBooleanSwitch" },
|
||||
{ 0x20, 0x62, "MechanicalBooleanSwitchArray" },
|
||||
{ 0x20, 0x63, "MechanicalMultivalueSwitch" },
|
||||
{ 0x20, 0x64, "MechanicalForce" },
|
||||
{ 0x20, 0x65, "MechanicalPressure" },
|
||||
{ 0x20, 0x66, "MechanicalStrain" },
|
||||
{ 0x20, 0x67, "MechanicalWeight" },
|
||||
{ 0x20, 0x68, "MechanicalHapticVibrator" },
|
||||
{ 0x20, 0x69, "MechanicalHallEffectSwitch" },
|
||||
{ 0x20, 0x70, "Motion" },
|
||||
{ 0x20, 0x71, "MotionAccelerometer1D" },
|
||||
{ 0x20, 0x72, "MotionAccelerometer2D" },
|
||||
{ 0x20, 0x73, "MotionAccelerometer3D" },
|
||||
{ 0x20, 0x74, "MotionGyrometer1D" },
|
||||
{ 0x20, 0x75, "MotionGyrometer2D" },
|
||||
{ 0x20, 0x76, "MotionGyrometer3D" },
|
||||
{ 0x20, 0x77, "MotionMotionDetector" },
|
||||
{ 0x20, 0x78, "MotionSpeedometer" },
|
||||
{ 0x20, 0x79, "MotionAccelerometer" },
|
||||
{ 0x20, 0x7A, "MotionGyrometer" },
|
||||
{ 0x20, 0x80, "Orientation" },
|
||||
{ 0x20, 0x81, "OrientationCompass1D" },
|
||||
{ 0x20, 0x82, "OrientationCompass2D" },
|
||||
{ 0x20, 0x83, "OrientationCompass3D" },
|
||||
{ 0x20, 0x84, "OrientationInclinometer1D" },
|
||||
{ 0x20, 0x85, "OrientationInclinometer2D" },
|
||||
{ 0x20, 0x86, "OrientationInclinometer3D" },
|
||||
{ 0x20, 0x87, "OrientationDistance1D" },
|
||||
{ 0x20, 0x88, "OrientationDistance2D" },
|
||||
{ 0x20, 0x89, "OrientationDistance3D" },
|
||||
{ 0x20, 0x8A, "OrientationDeviceOrientation" },
|
||||
{ 0x20, 0x8B, "OrientationCompass" },
|
||||
{ 0x20, 0x8C, "OrientationInclinometer" },
|
||||
{ 0x20, 0x8D, "OrientationDistance" },
|
||||
{ 0x20, 0x90, "Scanner" },
|
||||
{ 0x20, 0x91, "ScannerBarcode" },
|
||||
{ 0x20, 0x91, "ScannerRFID" },
|
||||
{ 0x20, 0x91, "ScannerNFC" },
|
||||
{ 0x20, 0xA0, "Time" },
|
||||
{ 0x20, 0xA1, "TimeAlarmTimer" },
|
||||
{ 0x20, 0xA2, "TimeRealTimeClock" },
|
||||
{ 0x20, 0xE0, "Other" },
|
||||
{ 0x20, 0xE1, "OtherCustom" },
|
||||
{ 0x20, 0xE2, "OtherGeneric" },
|
||||
{ 0x20, 0xE3, "OtherGenericEnumerator" },
|
||||
{ 0x84, 0, "Power Device" },
|
||||
{ 0x84, 0x02, "PresentStatus" },
|
||||
{ 0x84, 0x03, "ChangeStatus" },
|
||||
@ -855,6 +936,16 @@ static const char *keys[KEY_MAX + 1] = {
|
||||
[KEY_KBDILLUMDOWN] = "KbdIlluminationDown",
|
||||
[KEY_KBDILLUMUP] = "KbdIlluminationUp",
|
||||
[KEY_SWITCHVIDEOMODE] = "SwitchVideoMode",
|
||||
[KEY_BUTTONCONFIG] = "ButtonConfig",
|
||||
[KEY_TASKMANAGER] = "TaskManager",
|
||||
[KEY_JOURNAL] = "Journal",
|
||||
[KEY_CONTROLPANEL] = "ControlPanel",
|
||||
[KEY_APPSELECT] = "AppSelect",
|
||||
[KEY_SCREENSAVER] = "ScreenSaver",
|
||||
[KEY_VOICECOMMAND] = "VoiceCommand",
|
||||
[KEY_BRIGHTNESS_MIN] = "BrightnessMin",
|
||||
[KEY_BRIGHTNESS_MAX] = "BrightnessMax",
|
||||
[KEY_BRIGHTNESS_AUTO] = "BrightnessAuto",
|
||||
};
|
||||
|
||||
static const char *relatives[REL_MAX + 1] = {
|
||||
|
@ -463,6 +463,7 @@
|
||||
|
||||
#define USB_VENDOR_ID_STM_0 0x0483
|
||||
#define USB_DEVICE_ID_STM_HID_SENSOR 0x91d1
|
||||
#define USB_DEVICE_ID_STM_HID_SENSOR_1 0x9100
|
||||
|
||||
#define USB_VENDOR_ID_ION 0x15e4
|
||||
#define USB_DEVICE_ID_ICADE 0x0132
|
||||
@ -633,6 +634,9 @@
|
||||
#define USB_DEVICE_ID_MS_PRESENTER_8K_USB 0x0713
|
||||
#define USB_DEVICE_ID_MS_DIGITAL_MEDIA_3K 0x0730
|
||||
#define USB_DEVICE_ID_MS_COMFORT_MOUSE_4500 0x076c
|
||||
#define USB_DEVICE_ID_MS_SURFACE_PRO_2 0x0799
|
||||
#define USB_DEVICE_ID_MS_TOUCH_COVER_2 0x07a7
|
||||
#define USB_DEVICE_ID_MS_TYPE_COVER_2 0x07a9
|
||||
|
||||
#define USB_VENDOR_ID_MOJO 0x8282
|
||||
#define USB_DEVICE_ID_RETRO_ADAPTER 0x3201
|
||||
@ -764,11 +768,16 @@
|
||||
#define USB_VENDOR_ID_SAITEK 0x06a3
|
||||
#define USB_DEVICE_ID_SAITEK_RUMBLEPAD 0xff17
|
||||
#define USB_DEVICE_ID_SAITEK_PS1000 0x0621
|
||||
#define USB_DEVICE_ID_SAITEK_RAT7 0x0cd7
|
||||
#define USB_DEVICE_ID_SAITEK_MMO7 0x0cd0
|
||||
|
||||
#define USB_VENDOR_ID_SAMSUNG 0x0419
|
||||
#define USB_DEVICE_ID_SAMSUNG_IR_REMOTE 0x0001
|
||||
#define USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD_MOUSE 0x0600
|
||||
|
||||
#define USB_VENDOR_ID_SEMICO 0x1a2c
|
||||
#define USB_DEVICE_ID_SEMICO_USB_KEYKOARD 0x0023
|
||||
|
||||
#define USB_VENDOR_ID_SENNHEISER 0x1395
|
||||
#define USB_DEVICE_ID_SENNHEISER_BTD500USB 0x002c
|
||||
|
||||
|
@ -684,9 +684,14 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
|
||||
break;
|
||||
|
||||
case 0x46: /* TabletPick */
|
||||
case 0x5a: /* SecondaryBarrelSwitch */
|
||||
map_key_clear(BTN_STYLUS2);
|
||||
break;
|
||||
|
||||
case 0x5b: /* TransducerSerialNumber */
|
||||
set_bit(MSC_SERIAL, input->mscbit);
|
||||
break;
|
||||
|
||||
default: goto unknown;
|
||||
}
|
||||
break;
|
||||
@ -721,6 +726,13 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
|
||||
case 0x06c: map_key_clear(KEY_YELLOW); break;
|
||||
case 0x06d: map_key_clear(KEY_ZOOM); break;
|
||||
|
||||
case 0x06f: map_key_clear(KEY_BRIGHTNESSUP); break;
|
||||
case 0x070: map_key_clear(KEY_BRIGHTNESSDOWN); break;
|
||||
case 0x072: map_key_clear(KEY_BRIGHTNESS_TOGGLE); break;
|
||||
case 0x073: map_key_clear(KEY_BRIGHTNESS_MIN); break;
|
||||
case 0x074: map_key_clear(KEY_BRIGHTNESS_MAX); break;
|
||||
case 0x075: map_key_clear(KEY_BRIGHTNESS_AUTO); break;
|
||||
|
||||
case 0x082: map_key_clear(KEY_VIDEO_NEXT); break;
|
||||
case 0x083: map_key_clear(KEY_LAST); break;
|
||||
case 0x084: map_key_clear(KEY_ENTER); break;
|
||||
@ -761,6 +773,7 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
|
||||
case 0x0bf: map_key_clear(KEY_SLOW); break;
|
||||
|
||||
case 0x0cd: map_key_clear(KEY_PLAYPAUSE); break;
|
||||
case 0x0cf: map_key_clear(KEY_VOICECOMMAND); break;
|
||||
case 0x0e0: map_abs_clear(ABS_VOLUME); break;
|
||||
case 0x0e2: map_key_clear(KEY_MUTE); break;
|
||||
case 0x0e5: map_key_clear(KEY_BASSBOOST); break;
|
||||
@ -768,6 +781,7 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
|
||||
case 0x0ea: map_key_clear(KEY_VOLUMEDOWN); break;
|
||||
case 0x0f5: map_key_clear(KEY_SLOW); break;
|
||||
|
||||
case 0x181: map_key_clear(KEY_BUTTONCONFIG); break;
|
||||
case 0x182: map_key_clear(KEY_BOOKMARKS); break;
|
||||
case 0x183: map_key_clear(KEY_CONFIG); break;
|
||||
case 0x184: map_key_clear(KEY_WORDPROCESSOR); break;
|
||||
@ -781,6 +795,8 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
|
||||
case 0x18c: map_key_clear(KEY_VOICEMAIL); break;
|
||||
case 0x18d: map_key_clear(KEY_ADDRESSBOOK); break;
|
||||
case 0x18e: map_key_clear(KEY_CALENDAR); break;
|
||||
case 0x18f: map_key_clear(KEY_TASKMANAGER); break;
|
||||
case 0x190: map_key_clear(KEY_JOURNAL); break;
|
||||
case 0x191: map_key_clear(KEY_FINANCE); break;
|
||||
case 0x192: map_key_clear(KEY_CALC); break;
|
||||
case 0x193: map_key_clear(KEY_PLAYER); break;
|
||||
@ -789,12 +805,15 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
|
||||
case 0x199: map_key_clear(KEY_CHAT); break;
|
||||
case 0x19c: map_key_clear(KEY_LOGOFF); break;
|
||||
case 0x19e: map_key_clear(KEY_COFFEE); break;
|
||||
case 0x19f: map_key_clear(KEY_CONTROLPANEL); break;
|
||||
case 0x1a2: map_key_clear(KEY_APPSELECT); break;
|
||||
case 0x1a3: map_key_clear(KEY_NEXT); break;
|
||||
case 0x1a4: map_key_clear(KEY_PREVIOUS); break;
|
||||
case 0x1a6: map_key_clear(KEY_HELP); break;
|
||||
case 0x1a7: map_key_clear(KEY_DOCUMENTS); break;
|
||||
case 0x1ab: map_key_clear(KEY_SPELLCHECK); break;
|
||||
case 0x1ae: map_key_clear(KEY_KEYBOARD); break;
|
||||
case 0x1b1: map_key_clear(KEY_SCREENSAVER); break;
|
||||
case 0x1b4: map_key_clear(KEY_FILE); break;
|
||||
case 0x1b6: map_key_clear(KEY_IMAGES); break;
|
||||
case 0x1b7: map_key_clear(KEY_AUDIO); break;
|
||||
|
920
drivers/hid/hid-rmi.c
Normal file
920
drivers/hid/hid-rmi.c
Normal file
@ -0,0 +1,920 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Andrew Duggan <aduggan@synaptics.com>
|
||||
* Copyright (c) 2013 Synaptics Incorporated
|
||||
* Copyright (c) 2014 Benjamin Tissoires <benjamin.tissoires@gmail.com>
|
||||
* Copyright (c) 2014 Red Hat, Inc
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/hid.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/input/mt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/sched.h>
|
||||
#include "hid-ids.h"
|
||||
|
||||
#define RMI_MOUSE_REPORT_ID 0x01 /* Mouse emulation Report */
|
||||
#define RMI_WRITE_REPORT_ID 0x09 /* Output Report */
|
||||
#define RMI_READ_ADDR_REPORT_ID 0x0a /* Output Report */
|
||||
#define RMI_READ_DATA_REPORT_ID 0x0b /* Input Report */
|
||||
#define RMI_ATTN_REPORT_ID 0x0c /* Input Report */
|
||||
#define RMI_SET_RMI_MODE_REPORT_ID 0x0f /* Feature Report */
|
||||
|
||||
/* flags */
|
||||
#define RMI_READ_REQUEST_PENDING BIT(0)
|
||||
#define RMI_READ_DATA_PENDING BIT(1)
|
||||
#define RMI_STARTED BIT(2)
|
||||
|
||||
enum rmi_mode_type {
|
||||
RMI_MODE_OFF = 0,
|
||||
RMI_MODE_ATTN_REPORTS = 1,
|
||||
RMI_MODE_NO_PACKED_ATTN_REPORTS = 2,
|
||||
};
|
||||
|
||||
struct rmi_function {
|
||||
unsigned page; /* page of the function */
|
||||
u16 query_base_addr; /* base address for queries */
|
||||
u16 command_base_addr; /* base address for commands */
|
||||
u16 control_base_addr; /* base address for controls */
|
||||
u16 data_base_addr; /* base address for datas */
|
||||
unsigned int interrupt_base; /* cross-function interrupt number
|
||||
* (uniq in the device)*/
|
||||
unsigned int interrupt_count; /* number of interrupts */
|
||||
unsigned int report_size; /* size of a report */
|
||||
unsigned long irq_mask; /* mask of the interrupts
|
||||
* (to be applied against ATTN IRQ) */
|
||||
};
|
||||
|
||||
/**
|
||||
* struct rmi_data - stores information for hid communication
|
||||
*
|
||||
* @page_mutex: Locks current page to avoid changing pages in unexpected ways.
|
||||
* @page: Keeps track of the current virtual page
|
||||
*
|
||||
* @wait: Used for waiting for read data
|
||||
*
|
||||
* @writeReport: output buffer when writing RMI registers
|
||||
* @readReport: input buffer when reading RMI registers
|
||||
*
|
||||
* @input_report_size: size of an input report (advertised by HID)
|
||||
* @output_report_size: size of an output report (advertised by HID)
|
||||
*
|
||||
* @flags: flags for the current device (started, reading, etc...)
|
||||
*
|
||||
* @f11: placeholder of internal RMI function F11 description
|
||||
* @f30: placeholder of internal RMI function F30 description
|
||||
*
|
||||
* @max_fingers: maximum finger count reported by the device
|
||||
* @max_x: maximum x value reported by the device
|
||||
* @max_y: maximum y value reported by the device
|
||||
*
|
||||
* @gpio_led_count: count of GPIOs + LEDs reported by F30
|
||||
* @button_count: actual physical buttons count
|
||||
* @button_mask: button mask used to decode GPIO ATTN reports
|
||||
* @button_state_mask: pull state of the buttons
|
||||
*
|
||||
* @input: pointer to the kernel input device
|
||||
*
|
||||
* @reset_work: worker which will be called in case of a mouse report
|
||||
* @hdev: pointer to the struct hid_device
|
||||
*/
|
||||
struct rmi_data {
|
||||
struct mutex page_mutex;
|
||||
int page;
|
||||
|
||||
wait_queue_head_t wait;
|
||||
|
||||
u8 *writeReport;
|
||||
u8 *readReport;
|
||||
|
||||
int input_report_size;
|
||||
int output_report_size;
|
||||
|
||||
unsigned long flags;
|
||||
|
||||
struct rmi_function f11;
|
||||
struct rmi_function f30;
|
||||
|
||||
unsigned int max_fingers;
|
||||
unsigned int max_x;
|
||||
unsigned int max_y;
|
||||
unsigned int x_size_mm;
|
||||
unsigned int y_size_mm;
|
||||
|
||||
unsigned int gpio_led_count;
|
||||
unsigned int button_count;
|
||||
unsigned long button_mask;
|
||||
unsigned long button_state_mask;
|
||||
|
||||
struct input_dev *input;
|
||||
|
||||
struct work_struct reset_work;
|
||||
struct hid_device *hdev;
|
||||
};
|
||||
|
||||
#define RMI_PAGE(addr) (((addr) >> 8) & 0xff)
|
||||
|
||||
static int rmi_write_report(struct hid_device *hdev, u8 *report, int len);
|
||||
|
||||
/**
|
||||
* rmi_set_page - Set RMI page
|
||||
* @hdev: The pointer to the hid_device struct
|
||||
* @page: The new page address.
|
||||
*
|
||||
* RMI devices have 16-bit addressing, but some of the physical
|
||||
* implementations (like SMBus) only have 8-bit addressing. So RMI implements
|
||||
* a page address at 0xff of every page so we can reliable page addresses
|
||||
* every 256 registers.
|
||||
*
|
||||
* The page_mutex lock must be held when this function is entered.
|
||||
*
|
||||
* Returns zero on success, non-zero on failure.
|
||||
*/
|
||||
static int rmi_set_page(struct hid_device *hdev, u8 page)
|
||||
{
|
||||
struct rmi_data *data = hid_get_drvdata(hdev);
|
||||
int retval;
|
||||
|
||||
data->writeReport[0] = RMI_WRITE_REPORT_ID;
|
||||
data->writeReport[1] = 1;
|
||||
data->writeReport[2] = 0xFF;
|
||||
data->writeReport[4] = page;
|
||||
|
||||
retval = rmi_write_report(hdev, data->writeReport,
|
||||
data->output_report_size);
|
||||
if (retval != data->output_report_size) {
|
||||
dev_err(&hdev->dev,
|
||||
"%s: set page failed: %d.", __func__, retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
data->page = page;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rmi_set_mode(struct hid_device *hdev, u8 mode)
|
||||
{
|
||||
int ret;
|
||||
u8 txbuf[2] = {RMI_SET_RMI_MODE_REPORT_ID, mode};
|
||||
|
||||
ret = hid_hw_raw_request(hdev, RMI_SET_RMI_MODE_REPORT_ID, txbuf,
|
||||
sizeof(txbuf), HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
|
||||
if (ret < 0) {
|
||||
dev_err(&hdev->dev, "unable to set rmi mode to %d (%d)\n", mode,
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rmi_write_report(struct hid_device *hdev, u8 *report, int len)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = hid_hw_output_report(hdev, (void *)report, len);
|
||||
if (ret < 0) {
|
||||
dev_err(&hdev->dev, "failed to write hid report (%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rmi_read_block(struct hid_device *hdev, u16 addr, void *buf,
|
||||
const int len)
|
||||
{
|
||||
struct rmi_data *data = hid_get_drvdata(hdev);
|
||||
int ret;
|
||||
int bytes_read;
|
||||
int bytes_needed;
|
||||
int retries;
|
||||
int read_input_count;
|
||||
|
||||
mutex_lock(&data->page_mutex);
|
||||
|
||||
if (RMI_PAGE(addr) != data->page) {
|
||||
ret = rmi_set_page(hdev, RMI_PAGE(addr));
|
||||
if (ret < 0)
|
||||
goto exit;
|
||||
}
|
||||
|
||||
for (retries = 5; retries > 0; retries--) {
|
||||
data->writeReport[0] = RMI_READ_ADDR_REPORT_ID;
|
||||
data->writeReport[1] = 0; /* old 1 byte read count */
|
||||
data->writeReport[2] = addr & 0xFF;
|
||||
data->writeReport[3] = (addr >> 8) & 0xFF;
|
||||
data->writeReport[4] = len & 0xFF;
|
||||
data->writeReport[5] = (len >> 8) & 0xFF;
|
||||
|
||||
set_bit(RMI_READ_REQUEST_PENDING, &data->flags);
|
||||
|
||||
ret = rmi_write_report(hdev, data->writeReport,
|
||||
data->output_report_size);
|
||||
if (ret != data->output_report_size) {
|
||||
clear_bit(RMI_READ_REQUEST_PENDING, &data->flags);
|
||||
dev_err(&hdev->dev,
|
||||
"failed to write request output report (%d)\n",
|
||||
ret);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
bytes_read = 0;
|
||||
bytes_needed = len;
|
||||
while (bytes_read < len) {
|
||||
if (!wait_event_timeout(data->wait,
|
||||
test_bit(RMI_READ_DATA_PENDING, &data->flags),
|
||||
msecs_to_jiffies(1000))) {
|
||||
hid_warn(hdev, "%s: timeout elapsed\n",
|
||||
__func__);
|
||||
ret = -EAGAIN;
|
||||
break;
|
||||
}
|
||||
|
||||
read_input_count = data->readReport[1];
|
||||
memcpy(buf + bytes_read, &data->readReport[2],
|
||||
read_input_count < bytes_needed ?
|
||||
read_input_count : bytes_needed);
|
||||
|
||||
bytes_read += read_input_count;
|
||||
bytes_needed -= read_input_count;
|
||||
clear_bit(RMI_READ_DATA_PENDING, &data->flags);
|
||||
}
|
||||
|
||||
if (ret >= 0) {
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
clear_bit(RMI_READ_REQUEST_PENDING, &data->flags);
|
||||
mutex_unlock(&data->page_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int rmi_read(struct hid_device *hdev, u16 addr, void *buf)
|
||||
{
|
||||
return rmi_read_block(hdev, addr, buf, 1);
|
||||
}
|
||||
|
||||
static void rmi_f11_process_touch(struct rmi_data *hdata, int slot,
|
||||
u8 finger_state, u8 *touch_data)
|
||||
{
|
||||
int x, y, wx, wy;
|
||||
int wide, major, minor;
|
||||
int z;
|
||||
|
||||
input_mt_slot(hdata->input, slot);
|
||||
input_mt_report_slot_state(hdata->input, MT_TOOL_FINGER,
|
||||
finger_state == 0x01);
|
||||
if (finger_state == 0x01) {
|
||||
x = (touch_data[0] << 4) | (touch_data[2] & 0x0F);
|
||||
y = (touch_data[1] << 4) | (touch_data[2] >> 4);
|
||||
wx = touch_data[3] & 0x0F;
|
||||
wy = touch_data[3] >> 4;
|
||||
wide = (wx > wy);
|
||||
major = max(wx, wy);
|
||||
minor = min(wx, wy);
|
||||
z = touch_data[4];
|
||||
|
||||
/* y is inverted */
|
||||
y = hdata->max_y - y;
|
||||
|
||||
input_event(hdata->input, EV_ABS, ABS_MT_POSITION_X, x);
|
||||
input_event(hdata->input, EV_ABS, ABS_MT_POSITION_Y, y);
|
||||
input_event(hdata->input, EV_ABS, ABS_MT_ORIENTATION, wide);
|
||||
input_event(hdata->input, EV_ABS, ABS_MT_PRESSURE, z);
|
||||
input_event(hdata->input, EV_ABS, ABS_MT_TOUCH_MAJOR, major);
|
||||
input_event(hdata->input, EV_ABS, ABS_MT_TOUCH_MINOR, minor);
|
||||
}
|
||||
}
|
||||
|
||||
static void rmi_reset_work(struct work_struct *work)
|
||||
{
|
||||
struct rmi_data *hdata = container_of(work, struct rmi_data,
|
||||
reset_work);
|
||||
|
||||
/* switch the device to RMI if we receive a generic mouse report */
|
||||
rmi_set_mode(hdata->hdev, RMI_MODE_ATTN_REPORTS);
|
||||
}
|
||||
|
||||
static inline int rmi_schedule_reset(struct hid_device *hdev)
|
||||
{
|
||||
struct rmi_data *hdata = hid_get_drvdata(hdev);
|
||||
return schedule_work(&hdata->reset_work);
|
||||
}
|
||||
|
||||
static int rmi_f11_input_event(struct hid_device *hdev, u8 irq, u8 *data,
|
||||
int size)
|
||||
{
|
||||
struct rmi_data *hdata = hid_get_drvdata(hdev);
|
||||
int offset;
|
||||
int i;
|
||||
|
||||
if (size < hdata->f11.report_size)
|
||||
return 0;
|
||||
|
||||
if (!(irq & hdata->f11.irq_mask))
|
||||
return 0;
|
||||
|
||||
offset = (hdata->max_fingers >> 2) + 1;
|
||||
for (i = 0; i < hdata->max_fingers; i++) {
|
||||
int fs_byte_position = i >> 2;
|
||||
int fs_bit_position = (i & 0x3) << 1;
|
||||
int finger_state = (data[fs_byte_position] >> fs_bit_position) &
|
||||
0x03;
|
||||
|
||||
rmi_f11_process_touch(hdata, i, finger_state,
|
||||
&data[offset + 5 * i]);
|
||||
}
|
||||
input_mt_sync_frame(hdata->input);
|
||||
input_sync(hdata->input);
|
||||
return hdata->f11.report_size;
|
||||
}
|
||||
|
||||
static int rmi_f30_input_event(struct hid_device *hdev, u8 irq, u8 *data,
|
||||
int size)
|
||||
{
|
||||
struct rmi_data *hdata = hid_get_drvdata(hdev);
|
||||
int i;
|
||||
int button = 0;
|
||||
bool value;
|
||||
|
||||
if (!(irq & hdata->f30.irq_mask))
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < hdata->gpio_led_count; i++) {
|
||||
if (test_bit(i, &hdata->button_mask)) {
|
||||
value = (data[i / 8] >> (i & 0x07)) & BIT(0);
|
||||
if (test_bit(i, &hdata->button_state_mask))
|
||||
value = !value;
|
||||
input_event(hdata->input, EV_KEY, BTN_LEFT + button++,
|
||||
value);
|
||||
}
|
||||
}
|
||||
return hdata->f30.report_size;
|
||||
}
|
||||
|
||||
static int rmi_input_event(struct hid_device *hdev, u8 *data, int size)
|
||||
{
|
||||
struct rmi_data *hdata = hid_get_drvdata(hdev);
|
||||
unsigned long irq_mask = 0;
|
||||
unsigned index = 2;
|
||||
|
||||
if (!(test_bit(RMI_STARTED, &hdata->flags)))
|
||||
return 0;
|
||||
|
||||
irq_mask |= hdata->f11.irq_mask;
|
||||
irq_mask |= hdata->f30.irq_mask;
|
||||
|
||||
if (data[1] & ~irq_mask)
|
||||
hid_warn(hdev, "unknown intr source:%02lx %s:%d\n",
|
||||
data[1] & ~irq_mask, __FILE__, __LINE__);
|
||||
|
||||
if (hdata->f11.interrupt_base < hdata->f30.interrupt_base) {
|
||||
index += rmi_f11_input_event(hdev, data[1], &data[index],
|
||||
size - index);
|
||||
index += rmi_f30_input_event(hdev, data[1], &data[index],
|
||||
size - index);
|
||||
} else {
|
||||
index += rmi_f30_input_event(hdev, data[1], &data[index],
|
||||
size - index);
|
||||
index += rmi_f11_input_event(hdev, data[1], &data[index],
|
||||
size - index);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int rmi_read_data_event(struct hid_device *hdev, u8 *data, int size)
|
||||
{
|
||||
struct rmi_data *hdata = hid_get_drvdata(hdev);
|
||||
|
||||
if (!test_bit(RMI_READ_REQUEST_PENDING, &hdata->flags)) {
|
||||
hid_err(hdev, "no read request pending\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(hdata->readReport, data, size < hdata->input_report_size ?
|
||||
size : hdata->input_report_size);
|
||||
set_bit(RMI_READ_DATA_PENDING, &hdata->flags);
|
||||
wake_up(&hdata->wait);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int rmi_raw_event(struct hid_device *hdev,
|
||||
struct hid_report *report, u8 *data, int size)
|
||||
{
|
||||
switch (data[0]) {
|
||||
case RMI_READ_DATA_REPORT_ID:
|
||||
return rmi_read_data_event(hdev, data, size);
|
||||
case RMI_ATTN_REPORT_ID:
|
||||
return rmi_input_event(hdev, data, size);
|
||||
case RMI_MOUSE_REPORT_ID:
|
||||
rmi_schedule_reset(hdev);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rmi_post_reset(struct hid_device *hdev)
|
||||
{
|
||||
return rmi_set_mode(hdev, RMI_MODE_ATTN_REPORTS);
|
||||
}
|
||||
|
||||
static int rmi_post_resume(struct hid_device *hdev)
|
||||
{
|
||||
return rmi_set_mode(hdev, RMI_MODE_ATTN_REPORTS);
|
||||
}
|
||||
|
||||
#define RMI4_MAX_PAGE 0xff
|
||||
#define RMI4_PAGE_SIZE 0x0100
|
||||
|
||||
#define PDT_START_SCAN_LOCATION 0x00e9
|
||||
#define PDT_END_SCAN_LOCATION 0x0005
|
||||
#define RMI4_END_OF_PDT(id) ((id) == 0x00 || (id) == 0xff)
|
||||
|
||||
struct pdt_entry {
|
||||
u8 query_base_addr:8;
|
||||
u8 command_base_addr:8;
|
||||
u8 control_base_addr:8;
|
||||
u8 data_base_addr:8;
|
||||
u8 interrupt_source_count:3;
|
||||
u8 bits3and4:2;
|
||||
u8 function_version:2;
|
||||
u8 bit7:1;
|
||||
u8 function_number:8;
|
||||
} __attribute__((__packed__));
|
||||
|
||||
static inline unsigned long rmi_gen_mask(unsigned irq_base, unsigned irq_count)
|
||||
{
|
||||
return GENMASK(irq_count + irq_base - 1, irq_base);
|
||||
}
|
||||
|
||||
static void rmi_register_function(struct rmi_data *data,
|
||||
struct pdt_entry *pdt_entry, int page, unsigned interrupt_count)
|
||||
{
|
||||
struct rmi_function *f = NULL;
|
||||
u16 page_base = page << 8;
|
||||
|
||||
switch (pdt_entry->function_number) {
|
||||
case 0x11:
|
||||
f = &data->f11;
|
||||
break;
|
||||
case 0x30:
|
||||
f = &data->f30;
|
||||
break;
|
||||
}
|
||||
|
||||
if (f) {
|
||||
f->page = page;
|
||||
f->query_base_addr = page_base | pdt_entry->query_base_addr;
|
||||
f->command_base_addr = page_base | pdt_entry->command_base_addr;
|
||||
f->control_base_addr = page_base | pdt_entry->control_base_addr;
|
||||
f->data_base_addr = page_base | pdt_entry->data_base_addr;
|
||||
f->interrupt_base = interrupt_count;
|
||||
f->interrupt_count = pdt_entry->interrupt_source_count;
|
||||
f->irq_mask = rmi_gen_mask(f->interrupt_base,
|
||||
f->interrupt_count);
|
||||
}
|
||||
}
|
||||
|
||||
static int rmi_scan_pdt(struct hid_device *hdev)
|
||||
{
|
||||
struct rmi_data *data = hid_get_drvdata(hdev);
|
||||
struct pdt_entry entry;
|
||||
int page;
|
||||
bool page_has_function;
|
||||
int i;
|
||||
int retval;
|
||||
int interrupt = 0;
|
||||
u16 page_start, pdt_start , pdt_end;
|
||||
|
||||
hid_info(hdev, "Scanning PDT...\n");
|
||||
|
||||
for (page = 0; (page <= RMI4_MAX_PAGE); page++) {
|
||||
page_start = RMI4_PAGE_SIZE * page;
|
||||
pdt_start = page_start + PDT_START_SCAN_LOCATION;
|
||||
pdt_end = page_start + PDT_END_SCAN_LOCATION;
|
||||
|
||||
page_has_function = false;
|
||||
for (i = pdt_start; i >= pdt_end; i -= sizeof(entry)) {
|
||||
retval = rmi_read_block(hdev, i, &entry, sizeof(entry));
|
||||
if (retval) {
|
||||
hid_err(hdev,
|
||||
"Read of PDT entry at %#06x failed.\n",
|
||||
i);
|
||||
goto error_exit;
|
||||
}
|
||||
|
||||
if (RMI4_END_OF_PDT(entry.function_number))
|
||||
break;
|
||||
|
||||
page_has_function = true;
|
||||
|
||||
hid_info(hdev, "Found F%02X on page %#04x\n",
|
||||
entry.function_number, page);
|
||||
|
||||
rmi_register_function(data, &entry, page, interrupt);
|
||||
interrupt += entry.interrupt_source_count;
|
||||
}
|
||||
|
||||
if (!page_has_function)
|
||||
break;
|
||||
}
|
||||
|
||||
hid_info(hdev, "%s: Done with PDT scan.\n", __func__);
|
||||
retval = 0;
|
||||
|
||||
error_exit:
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int rmi_populate_f11(struct hid_device *hdev)
|
||||
{
|
||||
struct rmi_data *data = hid_get_drvdata(hdev);
|
||||
u8 buf[20];
|
||||
int ret;
|
||||
bool has_query9;
|
||||
bool has_query10;
|
||||
bool has_query11;
|
||||
bool has_query12;
|
||||
bool has_physical_props;
|
||||
unsigned x_size, y_size;
|
||||
u16 query12_offset;
|
||||
|
||||
if (!data->f11.query_base_addr) {
|
||||
hid_err(hdev, "No 2D sensor found, giving up.\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* query 0 contains some useful information */
|
||||
ret = rmi_read(hdev, data->f11.query_base_addr, buf);
|
||||
if (ret) {
|
||||
hid_err(hdev, "can not get query 0: %d.\n", ret);
|
||||
return ret;
|
||||
}
|
||||
has_query9 = !!(buf[0] & BIT(3));
|
||||
has_query11 = !!(buf[0] & BIT(4));
|
||||
has_query12 = !!(buf[0] & BIT(5));
|
||||
|
||||
/* query 1 to get the max number of fingers */
|
||||
ret = rmi_read(hdev, data->f11.query_base_addr + 1, buf);
|
||||
if (ret) {
|
||||
hid_err(hdev, "can not get NumberOfFingers: %d.\n", ret);
|
||||
return ret;
|
||||
}
|
||||
data->max_fingers = (buf[0] & 0x07) + 1;
|
||||
if (data->max_fingers > 5)
|
||||
data->max_fingers = 10;
|
||||
|
||||
data->f11.report_size = data->max_fingers * 5 +
|
||||
DIV_ROUND_UP(data->max_fingers, 4);
|
||||
|
||||
if (!(buf[0] & BIT(4))) {
|
||||
hid_err(hdev, "No absolute events, giving up.\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* query 8 to find out if query 10 exists */
|
||||
ret = rmi_read(hdev, data->f11.query_base_addr + 8, buf);
|
||||
if (ret) {
|
||||
hid_err(hdev, "can not read gesture information: %d.\n", ret);
|
||||
return ret;
|
||||
}
|
||||
has_query10 = !!(buf[0] & BIT(2));
|
||||
|
||||
/*
|
||||
* At least 8 queries are guaranteed to be present in F11
|
||||
* +1 for query12.
|
||||
*/
|
||||
query12_offset = 9;
|
||||
|
||||
if (has_query9)
|
||||
++query12_offset;
|
||||
|
||||
if (has_query10)
|
||||
++query12_offset;
|
||||
|
||||
if (has_query11)
|
||||
++query12_offset;
|
||||
|
||||
/* query 12 to know if the physical properties are reported */
|
||||
if (has_query12) {
|
||||
ret = rmi_read(hdev, data->f11.query_base_addr
|
||||
+ query12_offset, buf);
|
||||
if (ret) {
|
||||
hid_err(hdev, "can not get query 12: %d.\n", ret);
|
||||
return ret;
|
||||
}
|
||||
has_physical_props = !!(buf[0] & BIT(5));
|
||||
|
||||
if (has_physical_props) {
|
||||
ret = rmi_read_block(hdev,
|
||||
data->f11.query_base_addr
|
||||
+ query12_offset + 1, buf, 4);
|
||||
if (ret) {
|
||||
hid_err(hdev, "can not read query 15-18: %d.\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
x_size = buf[0] | (buf[1] << 8);
|
||||
y_size = buf[2] | (buf[3] << 8);
|
||||
|
||||
data->x_size_mm = DIV_ROUND_CLOSEST(x_size, 10);
|
||||
data->y_size_mm = DIV_ROUND_CLOSEST(y_size, 10);
|
||||
|
||||
hid_info(hdev, "%s: size in mm: %d x %d\n",
|
||||
__func__, data->x_size_mm, data->y_size_mm);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* retrieve the ctrl registers
|
||||
* the ctrl register has a size of 20 but a fw bug split it into 16 + 4,
|
||||
* and there is no way to know if the first 20 bytes are here or not.
|
||||
* We use only the first 10 bytes, so get only them.
|
||||
*/
|
||||
ret = rmi_read_block(hdev, data->f11.control_base_addr, buf, 10);
|
||||
if (ret) {
|
||||
hid_err(hdev, "can not read ctrl block of size 10: %d.\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
data->max_x = buf[6] | (buf[7] << 8);
|
||||
data->max_y = buf[8] | (buf[9] << 8);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rmi_populate_f30(struct hid_device *hdev)
|
||||
{
|
||||
struct rmi_data *data = hid_get_drvdata(hdev);
|
||||
u8 buf[20];
|
||||
int ret;
|
||||
bool has_gpio, has_led;
|
||||
unsigned bytes_per_ctrl;
|
||||
u8 ctrl2_addr;
|
||||
int ctrl2_3_length;
|
||||
int i;
|
||||
|
||||
/* function F30 is for physical buttons */
|
||||
if (!data->f30.query_base_addr) {
|
||||
hid_err(hdev, "No GPIO/LEDs found, giving up.\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ret = rmi_read_block(hdev, data->f30.query_base_addr, buf, 2);
|
||||
if (ret) {
|
||||
hid_err(hdev, "can not get F30 query registers: %d.\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
has_gpio = !!(buf[0] & BIT(3));
|
||||
has_led = !!(buf[0] & BIT(2));
|
||||
data->gpio_led_count = buf[1] & 0x1f;
|
||||
|
||||
/* retrieve ctrl 2 & 3 registers */
|
||||
bytes_per_ctrl = (data->gpio_led_count + 7) / 8;
|
||||
/* Ctrl0 is present only if both has_gpio and has_led are set*/
|
||||
ctrl2_addr = (has_gpio && has_led) ? bytes_per_ctrl : 0;
|
||||
/* Ctrl1 is always be present */
|
||||
ctrl2_addr += bytes_per_ctrl;
|
||||
ctrl2_3_length = 2 * bytes_per_ctrl;
|
||||
|
||||
data->f30.report_size = bytes_per_ctrl;
|
||||
|
||||
ret = rmi_read_block(hdev, data->f30.control_base_addr + ctrl2_addr,
|
||||
buf, ctrl2_3_length);
|
||||
if (ret) {
|
||||
hid_err(hdev, "can not read ctrl 2&3 block of size %d: %d.\n",
|
||||
ctrl2_3_length, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (i = 0; i < data->gpio_led_count; i++) {
|
||||
int byte_position = i >> 3;
|
||||
int bit_position = i & 0x07;
|
||||
u8 dir_byte = buf[byte_position];
|
||||
u8 data_byte = buf[byte_position + bytes_per_ctrl];
|
||||
bool dir = (dir_byte >> bit_position) & BIT(0);
|
||||
bool dat = (data_byte >> bit_position) & BIT(0);
|
||||
|
||||
if (dir == 0) {
|
||||
/* input mode */
|
||||
if (dat) {
|
||||
/* actual buttons have pull up resistor */
|
||||
data->button_count++;
|
||||
set_bit(i, &data->button_mask);
|
||||
set_bit(i, &data->button_state_mask);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rmi_populate(struct hid_device *hdev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = rmi_scan_pdt(hdev);
|
||||
if (ret) {
|
||||
hid_err(hdev, "PDT scan failed with code %d.\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = rmi_populate_f11(hdev);
|
||||
if (ret) {
|
||||
hid_err(hdev, "Error while initializing F11 (%d).\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = rmi_populate_f30(hdev);
|
||||
if (ret)
|
||||
hid_warn(hdev, "Error while initializing F30 (%d).\n", ret);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rmi_input_configured(struct hid_device *hdev, struct hid_input *hi)
|
||||
{
|
||||
struct rmi_data *data = hid_get_drvdata(hdev);
|
||||
struct input_dev *input = hi->input;
|
||||
int ret;
|
||||
int res_x, res_y, i;
|
||||
|
||||
data->input = input;
|
||||
|
||||
hid_dbg(hdev, "Opening low level driver\n");
|
||||
ret = hid_hw_open(hdev);
|
||||
if (ret)
|
||||
return;
|
||||
|
||||
/* Allow incoming hid reports */
|
||||
hid_device_io_start(hdev);
|
||||
|
||||
ret = rmi_set_mode(hdev, RMI_MODE_ATTN_REPORTS);
|
||||
if (ret < 0) {
|
||||
dev_err(&hdev->dev, "failed to set rmi mode\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ret = rmi_set_page(hdev, 0);
|
||||
if (ret < 0) {
|
||||
dev_err(&hdev->dev, "failed to set page select to 0.\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ret = rmi_populate(hdev);
|
||||
if (ret)
|
||||
goto exit;
|
||||
|
||||
__set_bit(EV_ABS, input->evbit);
|
||||
input_set_abs_params(input, ABS_MT_POSITION_X, 1, data->max_x, 0, 0);
|
||||
input_set_abs_params(input, ABS_MT_POSITION_Y, 1, data->max_y, 0, 0);
|
||||
|
||||
if (data->x_size_mm && data->y_size_mm) {
|
||||
res_x = (data->max_x - 1) / data->x_size_mm;
|
||||
res_y = (data->max_y - 1) / data->y_size_mm;
|
||||
|
||||
input_abs_set_res(input, ABS_MT_POSITION_X, res_x);
|
||||
input_abs_set_res(input, ABS_MT_POSITION_Y, res_y);
|
||||
}
|
||||
|
||||
input_set_abs_params(input, ABS_MT_ORIENTATION, 0, 1, 0, 0);
|
||||
input_set_abs_params(input, ABS_MT_PRESSURE, 0, 0xff, 0, 0);
|
||||
input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 0x0f, 0, 0);
|
||||
input_set_abs_params(input, ABS_MT_TOUCH_MINOR, 0, 0x0f, 0, 0);
|
||||
|
||||
input_mt_init_slots(input, data->max_fingers, INPUT_MT_POINTER);
|
||||
|
||||
if (data->button_count) {
|
||||
__set_bit(EV_KEY, input->evbit);
|
||||
for (i = 0; i < data->button_count; i++)
|
||||
__set_bit(BTN_LEFT + i, input->keybit);
|
||||
|
||||
if (data->button_count == 1)
|
||||
__set_bit(INPUT_PROP_BUTTONPAD, input->propbit);
|
||||
}
|
||||
|
||||
set_bit(RMI_STARTED, &data->flags);
|
||||
|
||||
exit:
|
||||
hid_device_io_stop(hdev);
|
||||
hid_hw_close(hdev);
|
||||
}
|
||||
|
||||
static int rmi_input_mapping(struct hid_device *hdev,
|
||||
struct hid_input *hi, struct hid_field *field,
|
||||
struct hid_usage *usage, unsigned long **bit, int *max)
|
||||
{
|
||||
/* we want to make HID ignore the advertised HID collection */
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int rmi_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
||||
{
|
||||
struct rmi_data *data = NULL;
|
||||
int ret;
|
||||
size_t alloc_size;
|
||||
|
||||
data = devm_kzalloc(&hdev->dev, sizeof(struct rmi_data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
INIT_WORK(&data->reset_work, rmi_reset_work);
|
||||
data->hdev = hdev;
|
||||
|
||||
hid_set_drvdata(hdev, data);
|
||||
|
||||
hdev->quirks |= HID_QUIRK_NO_INIT_REPORTS;
|
||||
|
||||
ret = hid_parse(hdev);
|
||||
if (ret) {
|
||||
hid_err(hdev, "parse failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
data->input_report_size = (hdev->report_enum[HID_INPUT_REPORT]
|
||||
.report_id_hash[RMI_ATTN_REPORT_ID]->size >> 3)
|
||||
+ 1 /* report id */;
|
||||
data->output_report_size = (hdev->report_enum[HID_OUTPUT_REPORT]
|
||||
.report_id_hash[RMI_WRITE_REPORT_ID]->size >> 3)
|
||||
+ 1 /* report id */;
|
||||
|
||||
alloc_size = data->output_report_size + data->input_report_size;
|
||||
|
||||
data->writeReport = devm_kzalloc(&hdev->dev, alloc_size, GFP_KERNEL);
|
||||
if (!data->writeReport) {
|
||||
ret = -ENOMEM;
|
||||
return ret;
|
||||
}
|
||||
|
||||
data->readReport = data->writeReport + data->output_report_size;
|
||||
|
||||
init_waitqueue_head(&data->wait);
|
||||
|
||||
mutex_init(&data->page_mutex);
|
||||
|
||||
ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
|
||||
if (ret) {
|
||||
hid_err(hdev, "hw start failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!test_bit(RMI_STARTED, &data->flags)) {
|
||||
hid_hw_stop(hdev);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rmi_remove(struct hid_device *hdev)
|
||||
{
|
||||
struct rmi_data *hdata = hid_get_drvdata(hdev);
|
||||
|
||||
clear_bit(RMI_STARTED, &hdata->flags);
|
||||
|
||||
hid_hw_stop(hdev);
|
||||
}
|
||||
|
||||
static const struct hid_device_id rmi_id[] = {
|
||||
{ HID_DEVICE(HID_BUS_ANY, HID_GROUP_RMI, HID_ANY_ID, HID_ANY_ID) },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(hid, rmi_id);
|
||||
|
||||
static struct hid_driver rmi_driver = {
|
||||
.name = "hid-rmi",
|
||||
.id_table = rmi_id,
|
||||
.probe = rmi_probe,
|
||||
.remove = rmi_remove,
|
||||
.raw_event = rmi_raw_event,
|
||||
.input_mapping = rmi_input_mapping,
|
||||
.input_configured = rmi_input_configured,
|
||||
#ifdef CONFIG_PM
|
||||
.resume = rmi_post_resume,
|
||||
.reset_resume = rmi_post_reset,
|
||||
#endif
|
||||
};
|
||||
|
||||
module_hid_driver(rmi_driver);
|
||||
|
||||
MODULE_AUTHOR("Andrew Duggan <aduggan@synaptics.com>");
|
||||
MODULE_DESCRIPTION("RMI HID driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -1,10 +1,17 @@
|
||||
/*
|
||||
* HID driver for Saitek devices, currently only the PS1000 (USB gamepad).
|
||||
* HID driver for Saitek devices.
|
||||
*
|
||||
* PS1000 (USB gamepad):
|
||||
* Fixes the HID report descriptor by removing a non-existent axis and
|
||||
* clearing the constant bit on the input reports for buttons and d-pad.
|
||||
* (This module is based on "hid-ortek".)
|
||||
*
|
||||
* Copyright (c) 2012 Andreas Hübner
|
||||
*
|
||||
* R.A.T.7, M.M.O.7 (USB gaming mice):
|
||||
* Fixes the mode button which cycles through three constantly pressed
|
||||
* buttons. All three press events are mapped to one button and the
|
||||
* missing release event is generated immediately.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -21,12 +28,57 @@
|
||||
|
||||
#include "hid-ids.h"
|
||||
|
||||
#define SAITEK_FIX_PS1000 0x0001
|
||||
#define SAITEK_RELEASE_MODE_RAT7 0x0002
|
||||
#define SAITEK_RELEASE_MODE_MMO7 0x0004
|
||||
|
||||
struct saitek_sc {
|
||||
unsigned long quirks;
|
||||
int mode;
|
||||
};
|
||||
|
||||
static int saitek_probe(struct hid_device *hdev,
|
||||
const struct hid_device_id *id)
|
||||
{
|
||||
unsigned long quirks = id->driver_data;
|
||||
struct saitek_sc *ssc;
|
||||
int ret;
|
||||
|
||||
ssc = devm_kzalloc(&hdev->dev, sizeof(*ssc), GFP_KERNEL);
|
||||
if (ssc == NULL) {
|
||||
hid_err(hdev, "can't alloc saitek descriptor\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ssc->quirks = quirks;
|
||||
ssc->mode = -1;
|
||||
|
||||
hid_set_drvdata(hdev, ssc);
|
||||
|
||||
ret = hid_parse(hdev);
|
||||
if (ret) {
|
||||
hid_err(hdev, "parse failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
|
||||
if (ret) {
|
||||
hid_err(hdev, "hw start failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __u8 *saitek_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
if (*rsize == 137 && rdesc[20] == 0x09 && rdesc[21] == 0x33
|
||||
&& rdesc[94] == 0x81 && rdesc[95] == 0x03
|
||||
&& rdesc[110] == 0x81 && rdesc[111] == 0x03) {
|
||||
struct saitek_sc *ssc = hid_get_drvdata(hdev);
|
||||
|
||||
if ((ssc->quirks & SAITEK_FIX_PS1000) && *rsize == 137 &&
|
||||
rdesc[20] == 0x09 && rdesc[21] == 0x33 &&
|
||||
rdesc[94] == 0x81 && rdesc[95] == 0x03 &&
|
||||
rdesc[110] == 0x81 && rdesc[111] == 0x03) {
|
||||
|
||||
hid_info(hdev, "Fixing up Saitek PS1000 report descriptor\n");
|
||||
|
||||
@ -42,8 +94,93 @@ static __u8 *saitek_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
return rdesc;
|
||||
}
|
||||
|
||||
static int saitek_raw_event(struct hid_device *hdev,
|
||||
struct hid_report *report, u8 *raw_data, int size)
|
||||
{
|
||||
struct saitek_sc *ssc = hid_get_drvdata(hdev);
|
||||
|
||||
if (ssc->quirks & SAITEK_RELEASE_MODE_RAT7 && size == 7) {
|
||||
/* R.A.T.7 uses bits 13, 14, 15 for the mode */
|
||||
int mode = -1;
|
||||
if (raw_data[1] & 0x01)
|
||||
mode = 0;
|
||||
else if (raw_data[1] & 0x02)
|
||||
mode = 1;
|
||||
else if (raw_data[1] & 0x04)
|
||||
mode = 2;
|
||||
|
||||
/* clear mode bits */
|
||||
raw_data[1] &= ~0x07;
|
||||
|
||||
if (mode != ssc->mode) {
|
||||
hid_dbg(hdev, "entered mode %d\n", mode);
|
||||
if (ssc->mode != -1) {
|
||||
/* use bit 13 as the mode button */
|
||||
raw_data[1] |= 0x04;
|
||||
}
|
||||
ssc->mode = mode;
|
||||
}
|
||||
} else if (ssc->quirks & SAITEK_RELEASE_MODE_MMO7 && size == 8) {
|
||||
|
||||
/* M.M.O.7 uses bits 8, 22, 23 for the mode */
|
||||
int mode = -1;
|
||||
if (raw_data[1] & 0x80)
|
||||
mode = 0;
|
||||
else if (raw_data[2] & 0x01)
|
||||
mode = 1;
|
||||
else if (raw_data[2] & 0x02)
|
||||
mode = 2;
|
||||
|
||||
/* clear mode bits */
|
||||
raw_data[1] &= ~0x80;
|
||||
raw_data[2] &= ~0x03;
|
||||
|
||||
if (mode != ssc->mode) {
|
||||
hid_dbg(hdev, "entered mode %d\n", mode);
|
||||
if (ssc->mode != -1) {
|
||||
/* use bit 8 as the mode button, bits 22
|
||||
* and 23 do not represent buttons
|
||||
* according to the HID report descriptor
|
||||
*/
|
||||
raw_data[1] |= 0x80;
|
||||
}
|
||||
ssc->mode = mode;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int saitek_event(struct hid_device *hdev, struct hid_field *field,
|
||||
struct hid_usage *usage, __s32 value)
|
||||
{
|
||||
struct saitek_sc *ssc = hid_get_drvdata(hdev);
|
||||
struct input_dev *input = field->hidinput->input;
|
||||
|
||||
if (usage->type == EV_KEY && value &&
|
||||
(((ssc->quirks & SAITEK_RELEASE_MODE_RAT7) &&
|
||||
usage->code - BTN_MOUSE == 10) ||
|
||||
((ssc->quirks & SAITEK_RELEASE_MODE_MMO7) &&
|
||||
usage->code - BTN_MOUSE == 15))) {
|
||||
|
||||
input_report_key(input, usage->code, 1);
|
||||
|
||||
/* report missing release event */
|
||||
input_report_key(input, usage->code, 0);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct hid_device_id saitek_devices[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_PS1000)},
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_PS1000),
|
||||
.driver_data = SAITEK_FIX_PS1000 },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RAT7),
|
||||
.driver_data = SAITEK_RELEASE_MODE_RAT7 },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_MMO7),
|
||||
.driver_data = SAITEK_RELEASE_MODE_MMO7 },
|
||||
{ }
|
||||
};
|
||||
|
||||
@ -52,7 +189,10 @@ MODULE_DEVICE_TABLE(hid, saitek_devices);
|
||||
static struct hid_driver saitek_driver = {
|
||||
.name = "saitek",
|
||||
.id_table = saitek_devices,
|
||||
.report_fixup = saitek_report_fixup
|
||||
.probe = saitek_probe,
|
||||
.report_fixup = saitek_report_fixup,
|
||||
.raw_event = saitek_raw_event,
|
||||
.event = saitek_event,
|
||||
};
|
||||
module_hid_driver(saitek_driver);
|
||||
|
||||
|
@ -705,8 +705,17 @@ static const struct hid_device_id sensor_hub_devices[] = {
|
||||
{ HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_INTEL_1,
|
||||
USB_DEVICE_ID_INTEL_HID_SENSOR_1),
|
||||
.driver_data = HID_SENSOR_HUB_ENUM_QUIRK},
|
||||
{ HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_MICROSOFT,
|
||||
USB_DEVICE_ID_MS_SURFACE_PRO_2),
|
||||
.driver_data = HID_SENSOR_HUB_ENUM_QUIRK},
|
||||
{ HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_MICROSOFT,
|
||||
USB_DEVICE_ID_MS_TOUCH_COVER_2),
|
||||
.driver_data = HID_SENSOR_HUB_ENUM_QUIRK},
|
||||
{ HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_MICROSOFT,
|
||||
USB_DEVICE_ID_MS_TYPE_COVER_2),
|
||||
.driver_data = HID_SENSOR_HUB_ENUM_QUIRK},
|
||||
{ HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_STM_0,
|
||||
USB_DEVICE_ID_STM_HID_SENSOR),
|
||||
USB_DEVICE_ID_STM_HID_SENSOR_1),
|
||||
.driver_data = HID_SENSOR_HUB_ENUM_QUIRK},
|
||||
{ HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_TEXAS_INSTRUMENTS,
|
||||
USB_DEVICE_ID_TEXAS_INSTRUMENTS_LENOVO_YOGA),
|
||||
|
@ -33,6 +33,7 @@
|
||||
#include <linux/power_supply.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/input/mt.h>
|
||||
|
||||
#include "hid-ids.h"
|
||||
@ -717,8 +718,39 @@ static enum power_supply_property sony_battery_props[] = {
|
||||
POWER_SUPPLY_PROP_STATUS,
|
||||
};
|
||||
|
||||
struct sixaxis_led {
|
||||
__u8 time_enabled; /* the total time the led is active (0xff means forever) */
|
||||
__u8 duty_length; /* how long a cycle is in deciseconds (0 means "really fast") */
|
||||
__u8 enabled;
|
||||
__u8 duty_off; /* % of duty_length the led is off (0xff means 100%) */
|
||||
__u8 duty_on; /* % of duty_length the led is on (0xff mean 100%) */
|
||||
} __packed;
|
||||
|
||||
struct sixaxis_rumble {
|
||||
__u8 padding;
|
||||
__u8 right_duration; /* Right motor duration (0xff means forever) */
|
||||
__u8 right_motor_on; /* Right (small) motor on/off, only supports values of 0 or 1 (off/on) */
|
||||
__u8 left_duration; /* Left motor duration (0xff means forever) */
|
||||
__u8 left_motor_force; /* left (large) motor, supports force values from 0 to 255 */
|
||||
} __packed;
|
||||
|
||||
struct sixaxis_output_report {
|
||||
__u8 report_id;
|
||||
struct sixaxis_rumble rumble;
|
||||
__u8 padding[4];
|
||||
__u8 leds_bitmap; /* bitmap of enabled LEDs: LED_1 = 0x02, LED_2 = 0x04, ... */
|
||||
struct sixaxis_led led[4]; /* LEDx at (4 - x) */
|
||||
struct sixaxis_led _reserved; /* LED5, not actually soldered */
|
||||
} __packed;
|
||||
|
||||
union sixaxis_output_report_01 {
|
||||
struct sixaxis_output_report data;
|
||||
__u8 buf[36];
|
||||
};
|
||||
|
||||
static spinlock_t sony_dev_list_lock;
|
||||
static LIST_HEAD(sony_device_list);
|
||||
static DEFINE_IDA(sony_device_id_allocator);
|
||||
|
||||
struct sony_sc {
|
||||
spinlock_t lock;
|
||||
@ -728,6 +760,7 @@ struct sony_sc {
|
||||
unsigned long quirks;
|
||||
struct work_struct state_worker;
|
||||
struct power_supply battery;
|
||||
int device_id;
|
||||
|
||||
#ifdef CONFIG_SONY_FF
|
||||
__u8 left;
|
||||
@ -740,6 +773,8 @@ struct sony_sc {
|
||||
__u8 battery_charging;
|
||||
__u8 battery_capacity;
|
||||
__u8 led_state[MAX_LEDS];
|
||||
__u8 led_delay_on[MAX_LEDS];
|
||||
__u8 led_delay_off[MAX_LEDS];
|
||||
__u8 led_count;
|
||||
};
|
||||
|
||||
@ -1048,6 +1083,52 @@ static int dualshock4_set_operational_bt(struct hid_device *hdev)
|
||||
HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
|
||||
}
|
||||
|
||||
static void sixaxis_set_leds_from_id(int id, __u8 values[MAX_LEDS])
|
||||
{
|
||||
static const __u8 sixaxis_leds[10][4] = {
|
||||
{ 0x01, 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x01, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x01, 0x00 },
|
||||
{ 0x00, 0x00, 0x00, 0x01 },
|
||||
{ 0x01, 0x00, 0x00, 0x01 },
|
||||
{ 0x00, 0x01, 0x00, 0x01 },
|
||||
{ 0x00, 0x00, 0x01, 0x01 },
|
||||
{ 0x01, 0x00, 0x01, 0x01 },
|
||||
{ 0x00, 0x01, 0x01, 0x01 },
|
||||
{ 0x01, 0x01, 0x01, 0x01 }
|
||||
};
|
||||
|
||||
BUG_ON(MAX_LEDS < ARRAY_SIZE(sixaxis_leds[0]));
|
||||
|
||||
if (id < 0)
|
||||
return;
|
||||
|
||||
id %= 10;
|
||||
memcpy(values, sixaxis_leds[id], sizeof(sixaxis_leds[id]));
|
||||
}
|
||||
|
||||
static void dualshock4_set_leds_from_id(int id, __u8 values[MAX_LEDS])
|
||||
{
|
||||
/* The first 4 color/index entries match what the PS4 assigns */
|
||||
static const __u8 color_code[7][3] = {
|
||||
/* Blue */ { 0x00, 0x00, 0x01 },
|
||||
/* Red */ { 0x01, 0x00, 0x00 },
|
||||
/* Green */ { 0x00, 0x01, 0x00 },
|
||||
/* Pink */ { 0x02, 0x00, 0x01 },
|
||||
/* Orange */ { 0x02, 0x01, 0x00 },
|
||||
/* Teal */ { 0x00, 0x01, 0x01 },
|
||||
/* White */ { 0x01, 0x01, 0x01 }
|
||||
};
|
||||
|
||||
BUG_ON(MAX_LEDS < ARRAY_SIZE(color_code[0]));
|
||||
|
||||
if (id < 0)
|
||||
return;
|
||||
|
||||
id %= 7;
|
||||
memcpy(values, color_code[id], sizeof(color_code[id]));
|
||||
}
|
||||
|
||||
static void buzz_set_leds(struct hid_device *hdev, const __u8 *leds)
|
||||
{
|
||||
struct list_head *report_list =
|
||||
@ -1066,19 +1147,18 @@ static void buzz_set_leds(struct hid_device *hdev, const __u8 *leds)
|
||||
hid_hw_request(hdev, report, HID_REQ_SET_REPORT);
|
||||
}
|
||||
|
||||
static void sony_set_leds(struct hid_device *hdev, const __u8 *leds, int count)
|
||||
static void sony_set_leds(struct sony_sc *sc, const __u8 *leds, int count)
|
||||
{
|
||||
struct sony_sc *drv_data = hid_get_drvdata(hdev);
|
||||
int n;
|
||||
|
||||
BUG_ON(count > MAX_LEDS);
|
||||
|
||||
if (drv_data->quirks & BUZZ_CONTROLLER && count == 4) {
|
||||
buzz_set_leds(hdev, leds);
|
||||
if (sc->quirks & BUZZ_CONTROLLER && count == 4) {
|
||||
buzz_set_leds(sc->hdev, leds);
|
||||
} else {
|
||||
for (n = 0; n < count; n++)
|
||||
drv_data->led_state[n] = leds[n];
|
||||
schedule_work(&drv_data->state_worker);
|
||||
sc->led_state[n] = leds[n];
|
||||
schedule_work(&sc->state_worker);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1090,6 +1170,7 @@ static void sony_led_set_brightness(struct led_classdev *led,
|
||||
struct sony_sc *drv_data;
|
||||
|
||||
int n;
|
||||
int force_update;
|
||||
|
||||
drv_data = hid_get_drvdata(hdev);
|
||||
if (!drv_data) {
|
||||
@ -1097,12 +1178,29 @@ static void sony_led_set_brightness(struct led_classdev *led,
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* The Sixaxis on USB will override any LED settings sent to it
|
||||
* and keep flashing all of the LEDs until the PS button is pressed.
|
||||
* Updates, even if redundant, must be always be sent to the
|
||||
* controller to avoid having to toggle the state of an LED just to
|
||||
* stop the flashing later on.
|
||||
*/
|
||||
force_update = !!(drv_data->quirks & SIXAXIS_CONTROLLER_USB);
|
||||
|
||||
for (n = 0; n < drv_data->led_count; n++) {
|
||||
if (led == drv_data->leds[n]) {
|
||||
if (value != drv_data->led_state[n]) {
|
||||
drv_data->led_state[n] = value;
|
||||
sony_set_leds(hdev, drv_data->led_state, drv_data->led_count);
|
||||
}
|
||||
if (led == drv_data->leds[n] && (force_update ||
|
||||
(value != drv_data->led_state[n] ||
|
||||
drv_data->led_delay_on[n] ||
|
||||
drv_data->led_delay_off[n]))) {
|
||||
|
||||
drv_data->led_state[n] = value;
|
||||
|
||||
/* Setting the brightness stops the blinking */
|
||||
drv_data->led_delay_on[n] = 0;
|
||||
drv_data->led_delay_off[n] = 0;
|
||||
|
||||
sony_set_leds(drv_data, drv_data->led_state,
|
||||
drv_data->led_count);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -1130,63 +1228,112 @@ static enum led_brightness sony_led_get_brightness(struct led_classdev *led)
|
||||
return LED_OFF;
|
||||
}
|
||||
|
||||
static void sony_leds_remove(struct hid_device *hdev)
|
||||
static int sony_led_blink_set(struct led_classdev *led, unsigned long *delay_on,
|
||||
unsigned long *delay_off)
|
||||
{
|
||||
struct device *dev = led->dev->parent;
|
||||
struct hid_device *hdev = container_of(dev, struct hid_device, dev);
|
||||
struct sony_sc *drv_data = hid_get_drvdata(hdev);
|
||||
int n;
|
||||
__u8 new_on, new_off;
|
||||
|
||||
if (!drv_data) {
|
||||
hid_err(hdev, "No device data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Max delay is 255 deciseconds or 2550 milliseconds */
|
||||
if (*delay_on > 2550)
|
||||
*delay_on = 2550;
|
||||
if (*delay_off > 2550)
|
||||
*delay_off = 2550;
|
||||
|
||||
/* Blink at 1 Hz if both values are zero */
|
||||
if (!*delay_on && !*delay_off)
|
||||
*delay_on = *delay_off = 500;
|
||||
|
||||
new_on = *delay_on / 10;
|
||||
new_off = *delay_off / 10;
|
||||
|
||||
for (n = 0; n < drv_data->led_count; n++) {
|
||||
if (led == drv_data->leds[n])
|
||||
break;
|
||||
}
|
||||
|
||||
/* This LED is not registered on this device */
|
||||
if (n >= drv_data->led_count)
|
||||
return -EINVAL;
|
||||
|
||||
/* Don't schedule work if the values didn't change */
|
||||
if (new_on != drv_data->led_delay_on[n] ||
|
||||
new_off != drv_data->led_delay_off[n]) {
|
||||
drv_data->led_delay_on[n] = new_on;
|
||||
drv_data->led_delay_off[n] = new_off;
|
||||
schedule_work(&drv_data->state_worker);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sony_leds_remove(struct sony_sc *sc)
|
||||
{
|
||||
struct sony_sc *drv_data;
|
||||
struct led_classdev *led;
|
||||
int n;
|
||||
|
||||
drv_data = hid_get_drvdata(hdev);
|
||||
BUG_ON(!(drv_data->quirks & SONY_LED_SUPPORT));
|
||||
BUG_ON(!(sc->quirks & SONY_LED_SUPPORT));
|
||||
|
||||
for (n = 0; n < drv_data->led_count; n++) {
|
||||
led = drv_data->leds[n];
|
||||
drv_data->leds[n] = NULL;
|
||||
for (n = 0; n < sc->led_count; n++) {
|
||||
led = sc->leds[n];
|
||||
sc->leds[n] = NULL;
|
||||
if (!led)
|
||||
continue;
|
||||
led_classdev_unregister(led);
|
||||
kfree(led);
|
||||
}
|
||||
|
||||
drv_data->led_count = 0;
|
||||
sc->led_count = 0;
|
||||
}
|
||||
|
||||
static int sony_leds_init(struct hid_device *hdev)
|
||||
static int sony_leds_init(struct sony_sc *sc)
|
||||
{
|
||||
struct sony_sc *drv_data;
|
||||
struct hid_device *hdev = sc->hdev;
|
||||
int n, ret = 0;
|
||||
int max_brightness;
|
||||
int use_colors;
|
||||
int use_ds4_names;
|
||||
struct led_classdev *led;
|
||||
size_t name_sz;
|
||||
char *name;
|
||||
size_t name_len;
|
||||
const char *name_fmt;
|
||||
static const char * const color_str[] = { "red", "green", "blue" };
|
||||
static const __u8 initial_values[MAX_LEDS] = { 0x00, 0x00, 0x00, 0x00 };
|
||||
static const char * const ds4_name_str[] = { "red", "green", "blue",
|
||||
"global" };
|
||||
__u8 initial_values[MAX_LEDS] = { 0 };
|
||||
__u8 max_brightness[MAX_LEDS] = { 1 };
|
||||
__u8 use_hw_blink[MAX_LEDS] = { 0 };
|
||||
|
||||
drv_data = hid_get_drvdata(hdev);
|
||||
BUG_ON(!(drv_data->quirks & SONY_LED_SUPPORT));
|
||||
BUG_ON(!(sc->quirks & SONY_LED_SUPPORT));
|
||||
|
||||
if (drv_data->quirks & BUZZ_CONTROLLER) {
|
||||
drv_data->led_count = 4;
|
||||
max_brightness = 1;
|
||||
use_colors = 0;
|
||||
if (sc->quirks & BUZZ_CONTROLLER) {
|
||||
sc->led_count = 4;
|
||||
use_ds4_names = 0;
|
||||
name_len = strlen("::buzz#");
|
||||
name_fmt = "%s::buzz%d";
|
||||
/* Validate expected report characteristics. */
|
||||
if (!hid_validate_values(hdev, HID_OUTPUT_REPORT, 0, 0, 7))
|
||||
return -ENODEV;
|
||||
} else if (drv_data->quirks & DUALSHOCK4_CONTROLLER) {
|
||||
drv_data->led_count = 3;
|
||||
max_brightness = 255;
|
||||
use_colors = 1;
|
||||
} else if (sc->quirks & DUALSHOCK4_CONTROLLER) {
|
||||
dualshock4_set_leds_from_id(sc->device_id, initial_values);
|
||||
initial_values[3] = 1;
|
||||
sc->led_count = 4;
|
||||
memset(max_brightness, 255, 3);
|
||||
use_hw_blink[3] = 1;
|
||||
use_ds4_names = 1;
|
||||
name_len = 0;
|
||||
name_fmt = "%s:%s";
|
||||
} else {
|
||||
drv_data->led_count = 4;
|
||||
max_brightness = 1;
|
||||
use_colors = 0;
|
||||
sixaxis_set_leds_from_id(sc->device_id, initial_values);
|
||||
sc->led_count = 4;
|
||||
memset(use_hw_blink, 1, 4);
|
||||
use_ds4_names = 0;
|
||||
name_len = strlen("::sony#");
|
||||
name_fmt = "%s::sony%d";
|
||||
}
|
||||
@ -1196,14 +1343,14 @@ static int sony_leds_init(struct hid_device *hdev)
|
||||
* only relevant if the driver is loaded after somebody actively set the
|
||||
* LEDs to on
|
||||
*/
|
||||
sony_set_leds(hdev, initial_values, drv_data->led_count);
|
||||
sony_set_leds(sc, initial_values, sc->led_count);
|
||||
|
||||
name_sz = strlen(dev_name(&hdev->dev)) + name_len + 1;
|
||||
|
||||
for (n = 0; n < drv_data->led_count; n++) {
|
||||
for (n = 0; n < sc->led_count; n++) {
|
||||
|
||||
if (use_colors)
|
||||
name_sz = strlen(dev_name(&hdev->dev)) + strlen(color_str[n]) + 2;
|
||||
if (use_ds4_names)
|
||||
name_sz = strlen(dev_name(&hdev->dev)) + strlen(ds4_name_str[n]) + 2;
|
||||
|
||||
led = kzalloc(sizeof(struct led_classdev) + name_sz, GFP_KERNEL);
|
||||
if (!led) {
|
||||
@ -1213,30 +1360,35 @@ static int sony_leds_init(struct hid_device *hdev)
|
||||
}
|
||||
|
||||
name = (void *)(&led[1]);
|
||||
if (use_colors)
|
||||
snprintf(name, name_sz, name_fmt, dev_name(&hdev->dev), color_str[n]);
|
||||
if (use_ds4_names)
|
||||
snprintf(name, name_sz, name_fmt, dev_name(&hdev->dev),
|
||||
ds4_name_str[n]);
|
||||
else
|
||||
snprintf(name, name_sz, name_fmt, dev_name(&hdev->dev), n + 1);
|
||||
led->name = name;
|
||||
led->brightness = 0;
|
||||
led->max_brightness = max_brightness;
|
||||
led->brightness = initial_values[n];
|
||||
led->max_brightness = max_brightness[n];
|
||||
led->brightness_get = sony_led_get_brightness;
|
||||
led->brightness_set = sony_led_set_brightness;
|
||||
|
||||
if (use_hw_blink[n])
|
||||
led->blink_set = sony_led_blink_set;
|
||||
|
||||
sc->leds[n] = led;
|
||||
|
||||
ret = led_classdev_register(&hdev->dev, led);
|
||||
if (ret) {
|
||||
hid_err(hdev, "Failed to register LED %d\n", n);
|
||||
sc->leds[n] = NULL;
|
||||
kfree(led);
|
||||
goto error_leds;
|
||||
}
|
||||
|
||||
drv_data->leds[n] = led;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
error_leds:
|
||||
sony_leds_remove(hdev);
|
||||
sony_leds_remove(sc);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -1244,29 +1396,52 @@ error_leds:
|
||||
static void sixaxis_state_worker(struct work_struct *work)
|
||||
{
|
||||
struct sony_sc *sc = container_of(work, struct sony_sc, state_worker);
|
||||
unsigned char buf[] = {
|
||||
0x01,
|
||||
0x00, 0xff, 0x00, 0xff, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xff, 0x27, 0x10, 0x00, 0x32,
|
||||
0xff, 0x27, 0x10, 0x00, 0x32,
|
||||
0xff, 0x27, 0x10, 0x00, 0x32,
|
||||
0xff, 0x27, 0x10, 0x00, 0x32,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00
|
||||
int n;
|
||||
union sixaxis_output_report_01 report = {
|
||||
.buf = {
|
||||
0x01,
|
||||
0x00, 0xff, 0x00, 0xff, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xff, 0x27, 0x10, 0x00, 0x32,
|
||||
0xff, 0x27, 0x10, 0x00, 0x32,
|
||||
0xff, 0x27, 0x10, 0x00, 0x32,
|
||||
0xff, 0x27, 0x10, 0x00, 0x32,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef CONFIG_SONY_FF
|
||||
buf[3] = sc->right ? 1 : 0;
|
||||
buf[5] = sc->left;
|
||||
report.data.rumble.right_motor_on = sc->right ? 1 : 0;
|
||||
report.data.rumble.left_motor_force = sc->left;
|
||||
#endif
|
||||
|
||||
buf[10] |= sc->led_state[0] << 1;
|
||||
buf[10] |= sc->led_state[1] << 2;
|
||||
buf[10] |= sc->led_state[2] << 3;
|
||||
buf[10] |= sc->led_state[3] << 4;
|
||||
report.data.leds_bitmap |= sc->led_state[0] << 1;
|
||||
report.data.leds_bitmap |= sc->led_state[1] << 2;
|
||||
report.data.leds_bitmap |= sc->led_state[2] << 3;
|
||||
report.data.leds_bitmap |= sc->led_state[3] << 4;
|
||||
|
||||
hid_hw_raw_request(sc->hdev, 0x01, buf, sizeof(buf), HID_OUTPUT_REPORT,
|
||||
HID_REQ_SET_REPORT);
|
||||
/* Set flag for all leds off, required for 3rd party INTEC controller */
|
||||
if ((report.data.leds_bitmap & 0x1E) == 0)
|
||||
report.data.leds_bitmap |= 0x20;
|
||||
|
||||
/*
|
||||
* The LEDs in the report are indexed in reverse order to their
|
||||
* corresponding light on the controller.
|
||||
* Index 0 = LED 4, index 1 = LED 3, etc...
|
||||
*
|
||||
* In the case of both delay values being zero (blinking disabled) the
|
||||
* default report values should be used or the controller LED will be
|
||||
* always off.
|
||||
*/
|
||||
for (n = 0; n < 4; n++) {
|
||||
if (sc->led_delay_on[n] || sc->led_delay_off[n]) {
|
||||
report.data.led[3 - n].duty_off = sc->led_delay_off[n];
|
||||
report.data.led[3 - n].duty_on = sc->led_delay_on[n];
|
||||
}
|
||||
}
|
||||
|
||||
hid_hw_raw_request(sc->hdev, report.data.report_id, report.buf,
|
||||
sizeof(report), HID_OUTPUT_REPORT, HID_REQ_SET_REPORT);
|
||||
}
|
||||
|
||||
static void dualshock4_state_worker(struct work_struct *work)
|
||||
@ -1279,7 +1454,7 @@ static void dualshock4_state_worker(struct work_struct *work)
|
||||
|
||||
if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) {
|
||||
buf[0] = 0x05;
|
||||
buf[1] = 0x03;
|
||||
buf[1] = 0xFF;
|
||||
offset = 4;
|
||||
} else {
|
||||
buf[0] = 0x11;
|
||||
@ -1295,9 +1470,18 @@ static void dualshock4_state_worker(struct work_struct *work)
|
||||
offset += 2;
|
||||
#endif
|
||||
|
||||
buf[offset++] = sc->led_state[0];
|
||||
buf[offset++] = sc->led_state[1];
|
||||
buf[offset++] = sc->led_state[2];
|
||||
/* LED 3 is the global control */
|
||||
if (sc->led_state[3]) {
|
||||
buf[offset++] = sc->led_state[0];
|
||||
buf[offset++] = sc->led_state[1];
|
||||
buf[offset++] = sc->led_state[2];
|
||||
} else {
|
||||
offset += 3;
|
||||
}
|
||||
|
||||
/* If both delay values are zero the DualShock 4 disables blinking. */
|
||||
buf[offset++] = sc->led_delay_on[3];
|
||||
buf[offset++] = sc->led_delay_off[3];
|
||||
|
||||
if (sc->quirks & DUALSHOCK4_CONTROLLER_USB)
|
||||
hid_hw_output_report(hdev, buf, 32);
|
||||
@ -1323,9 +1507,9 @@ static int sony_play_effect(struct input_dev *dev, void *data,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sony_init_ff(struct hid_device *hdev)
|
||||
static int sony_init_ff(struct sony_sc *sc)
|
||||
{
|
||||
struct hid_input *hidinput = list_entry(hdev->inputs.next,
|
||||
struct hid_input *hidinput = list_entry(sc->hdev->inputs.next,
|
||||
struct hid_input, list);
|
||||
struct input_dev *input_dev = hidinput->input;
|
||||
|
||||
@ -1334,7 +1518,7 @@ static int sony_init_ff(struct hid_device *hdev)
|
||||
}
|
||||
|
||||
#else
|
||||
static int sony_init_ff(struct hid_device *hdev)
|
||||
static int sony_init_ff(struct sony_sc *sc)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@ -1384,8 +1568,6 @@ static int sony_battery_get_property(struct power_supply *psy,
|
||||
|
||||
static int sony_battery_probe(struct sony_sc *sc)
|
||||
{
|
||||
static atomic_t power_id_seq = ATOMIC_INIT(0);
|
||||
unsigned long power_id;
|
||||
struct hid_device *hdev = sc->hdev;
|
||||
int ret;
|
||||
|
||||
@ -1395,15 +1577,13 @@ static int sony_battery_probe(struct sony_sc *sc)
|
||||
*/
|
||||
sc->battery_capacity = 100;
|
||||
|
||||
power_id = (unsigned long)atomic_inc_return(&power_id_seq);
|
||||
|
||||
sc->battery.properties = sony_battery_props;
|
||||
sc->battery.num_properties = ARRAY_SIZE(sony_battery_props);
|
||||
sc->battery.get_property = sony_battery_get_property;
|
||||
sc->battery.type = POWER_SUPPLY_TYPE_BATTERY;
|
||||
sc->battery.use_for_apm = 0;
|
||||
sc->battery.name = kasprintf(GFP_KERNEL, "sony_controller_battery_%lu",
|
||||
power_id);
|
||||
sc->battery.name = kasprintf(GFP_KERNEL, "sony_controller_battery_%pMR",
|
||||
sc->mac_address);
|
||||
if (!sc->battery.name)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -1578,6 +1758,52 @@ static int sony_check_add(struct sony_sc *sc)
|
||||
return sony_check_add_dev_list(sc);
|
||||
}
|
||||
|
||||
static int sony_set_device_id(struct sony_sc *sc)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Only DualShock 4 or Sixaxis controllers get an id.
|
||||
* All others are set to -1.
|
||||
*/
|
||||
if ((sc->quirks & SIXAXIS_CONTROLLER) ||
|
||||
(sc->quirks & DUALSHOCK4_CONTROLLER)) {
|
||||
ret = ida_simple_get(&sony_device_id_allocator, 0, 0,
|
||||
GFP_KERNEL);
|
||||
if (ret < 0) {
|
||||
sc->device_id = -1;
|
||||
return ret;
|
||||
}
|
||||
sc->device_id = ret;
|
||||
} else {
|
||||
sc->device_id = -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sony_release_device_id(struct sony_sc *sc)
|
||||
{
|
||||
if (sc->device_id >= 0) {
|
||||
ida_simple_remove(&sony_device_id_allocator, sc->device_id);
|
||||
sc->device_id = -1;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void sony_init_work(struct sony_sc *sc,
|
||||
void (*worker)(struct work_struct *))
|
||||
{
|
||||
if (!sc->worker_initialized)
|
||||
INIT_WORK(&sc->state_worker, worker);
|
||||
|
||||
sc->worker_initialized = 1;
|
||||
}
|
||||
|
||||
static inline void sony_cancel_work_sync(struct sony_sc *sc)
|
||||
{
|
||||
if (sc->worker_initialized)
|
||||
cancel_work_sync(&sc->state_worker);
|
||||
}
|
||||
|
||||
static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
||||
{
|
||||
@ -1615,6 +1841,12 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = sony_set_device_id(sc);
|
||||
if (ret < 0) {
|
||||
hid_err(hdev, "failed to allocate the device id\n");
|
||||
goto err_stop;
|
||||
}
|
||||
|
||||
if (sc->quirks & SIXAXIS_CONTROLLER_USB) {
|
||||
/*
|
||||
* The Sony Sixaxis does not handle HID Output Reports on the
|
||||
@ -1629,8 +1861,7 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
||||
hdev->quirks |= HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP;
|
||||
hdev->quirks |= HID_QUIRK_SKIP_OUTPUT_REPORT_ID;
|
||||
ret = sixaxis_set_operational_usb(hdev);
|
||||
sc->worker_initialized = 1;
|
||||
INIT_WORK(&sc->state_worker, sixaxis_state_worker);
|
||||
sony_init_work(sc, sixaxis_state_worker);
|
||||
} else if (sc->quirks & SIXAXIS_CONTROLLER_BT) {
|
||||
/*
|
||||
* The Sixaxis wants output reports sent on the ctrl endpoint
|
||||
@ -1638,8 +1869,7 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
||||
*/
|
||||
hdev->quirks |= HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP;
|
||||
ret = sixaxis_set_operational_bt(hdev);
|
||||
sc->worker_initialized = 1;
|
||||
INIT_WORK(&sc->state_worker, sixaxis_state_worker);
|
||||
sony_init_work(sc, sixaxis_state_worker);
|
||||
} else if (sc->quirks & DUALSHOCK4_CONTROLLER) {
|
||||
if (sc->quirks & DUALSHOCK4_CONTROLLER_BT) {
|
||||
/*
|
||||
@ -1661,8 +1891,7 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
||||
if (ret < 0)
|
||||
goto err_stop;
|
||||
|
||||
sc->worker_initialized = 1;
|
||||
INIT_WORK(&sc->state_worker, dualshock4_state_worker);
|
||||
sony_init_work(sc, dualshock4_state_worker);
|
||||
} else {
|
||||
ret = 0;
|
||||
}
|
||||
@ -1675,7 +1904,7 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
||||
goto err_stop;
|
||||
|
||||
if (sc->quirks & SONY_LED_SUPPORT) {
|
||||
ret = sony_leds_init(hdev);
|
||||
ret = sony_leds_init(sc);
|
||||
if (ret < 0)
|
||||
goto err_stop;
|
||||
}
|
||||
@ -1694,7 +1923,7 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
||||
}
|
||||
|
||||
if (sc->quirks & SONY_FF_SUPPORT) {
|
||||
ret = sony_init_ff(hdev);
|
||||
ret = sony_init_ff(sc);
|
||||
if (ret < 0)
|
||||
goto err_close;
|
||||
}
|
||||
@ -1704,12 +1933,12 @@ err_close:
|
||||
hid_hw_close(hdev);
|
||||
err_stop:
|
||||
if (sc->quirks & SONY_LED_SUPPORT)
|
||||
sony_leds_remove(hdev);
|
||||
sony_leds_remove(sc);
|
||||
if (sc->quirks & SONY_BATTERY_SUPPORT)
|
||||
sony_battery_remove(sc);
|
||||
if (sc->worker_initialized)
|
||||
cancel_work_sync(&sc->state_worker);
|
||||
sony_cancel_work_sync(sc);
|
||||
sony_remove_dev_list(sc);
|
||||
sony_release_device_id(sc);
|
||||
hid_hw_stop(hdev);
|
||||
return ret;
|
||||
}
|
||||
@ -1719,18 +1948,19 @@ static void sony_remove(struct hid_device *hdev)
|
||||
struct sony_sc *sc = hid_get_drvdata(hdev);
|
||||
|
||||
if (sc->quirks & SONY_LED_SUPPORT)
|
||||
sony_leds_remove(hdev);
|
||||
sony_leds_remove(sc);
|
||||
|
||||
if (sc->quirks & SONY_BATTERY_SUPPORT) {
|
||||
hid_hw_close(hdev);
|
||||
sony_battery_remove(sc);
|
||||
}
|
||||
|
||||
if (sc->worker_initialized)
|
||||
cancel_work_sync(&sc->state_worker);
|
||||
sony_cancel_work_sync(sc);
|
||||
|
||||
sony_remove_dev_list(sc);
|
||||
|
||||
sony_release_device_id(sc);
|
||||
|
||||
hid_hw_stop(hdev);
|
||||
}
|
||||
|
||||
@ -1775,6 +2005,22 @@ static struct hid_driver sony_driver = {
|
||||
.report_fixup = sony_report_fixup,
|
||||
.raw_event = sony_raw_event
|
||||
};
|
||||
module_hid_driver(sony_driver);
|
||||
|
||||
static int __init sony_init(void)
|
||||
{
|
||||
dbg_hid("Sony:%s\n", __func__);
|
||||
|
||||
return hid_register_driver(&sony_driver);
|
||||
}
|
||||
|
||||
static void __exit sony_exit(void)
|
||||
{
|
||||
dbg_hid("Sony:%s\n", __func__);
|
||||
|
||||
ida_destroy(&sony_device_id_allocator);
|
||||
hid_unregister_driver(&sony_driver);
|
||||
}
|
||||
module_init(sony_init);
|
||||
module_exit(sony_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* ThingM blink(1) USB RGB LED driver
|
||||
*
|
||||
* Copyright 2013 Savoir-faire Linux Inc.
|
||||
* Copyright 2013-2014 Savoir-faire Linux Inc.
|
||||
* Vivien Didelot <vivien.didelot@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
@ -10,244 +10,285 @@
|
||||
*/
|
||||
|
||||
#include <linux/hid.h>
|
||||
#include <linux/hidraw.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#include "hid-ids.h"
|
||||
|
||||
#define BLINK1_CMD_SIZE 9
|
||||
#define REPORT_ID 1
|
||||
#define REPORT_SIZE 9
|
||||
|
||||
#define blink1_rgb_to_r(rgb) ((rgb & 0xFF0000) >> 16)
|
||||
#define blink1_rgb_to_g(rgb) ((rgb & 0x00FF00) >> 8)
|
||||
#define blink1_rgb_to_b(rgb) ((rgb & 0x0000FF) >> 0)
|
||||
/* Firmware major number of supported devices */
|
||||
#define THINGM_MAJOR_MK1 '1'
|
||||
#define THINGM_MAJOR_MK2 '2'
|
||||
|
||||
/**
|
||||
* struct blink1_data - blink(1) device specific data
|
||||
* @hdev: HID device.
|
||||
* @led_cdev: LED class instance.
|
||||
* @rgb: 8-bit per channel RGB notation.
|
||||
* @fade: fade time in hundredths of a second.
|
||||
* @brightness: brightness coefficient.
|
||||
* @play: play/pause in-memory patterns.
|
||||
*/
|
||||
struct blink1_data {
|
||||
struct hid_device *hdev;
|
||||
struct led_classdev led_cdev;
|
||||
u32 rgb;
|
||||
u16 fade;
|
||||
u8 brightness;
|
||||
bool play;
|
||||
struct thingm_fwinfo {
|
||||
char major;
|
||||
unsigned numrgb;
|
||||
unsigned first;
|
||||
};
|
||||
|
||||
static int blink1_send_command(struct blink1_data *data,
|
||||
u8 buf[BLINK1_CMD_SIZE])
|
||||
static const struct thingm_fwinfo thingm_fwinfo[] = {
|
||||
{
|
||||
.major = THINGM_MAJOR_MK1,
|
||||
.numrgb = 1,
|
||||
.first = 0,
|
||||
}, {
|
||||
.major = THINGM_MAJOR_MK2,
|
||||
.numrgb = 2,
|
||||
.first = 1,
|
||||
}
|
||||
};
|
||||
|
||||
/* A red, green or blue channel, part of an RGB chip */
|
||||
struct thingm_led {
|
||||
struct thingm_rgb *rgb;
|
||||
struct led_classdev ldev;
|
||||
char name[32];
|
||||
};
|
||||
|
||||
/* Basically a WS2812 5050 RGB LED chip */
|
||||
struct thingm_rgb {
|
||||
struct thingm_device *tdev;
|
||||
struct thingm_led red;
|
||||
struct thingm_led green;
|
||||
struct thingm_led blue;
|
||||
struct work_struct work;
|
||||
u8 num;
|
||||
};
|
||||
|
||||
struct thingm_device {
|
||||
struct hid_device *hdev;
|
||||
struct {
|
||||
char major;
|
||||
char minor;
|
||||
} version;
|
||||
const struct thingm_fwinfo *fwinfo;
|
||||
struct mutex lock;
|
||||
struct thingm_rgb *rgb;
|
||||
};
|
||||
|
||||
static int thingm_send(struct thingm_device *tdev, u8 buf[REPORT_SIZE])
|
||||
{
|
||||
int ret;
|
||||
|
||||
hid_dbg(data->hdev, "command: %d%c%.2x%.2x%.2x%.2x%.2x%.2x%.2x\n",
|
||||
hid_dbg(tdev->hdev, "-> %d %c %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx\n",
|
||||
buf[0], buf[1], buf[2], buf[3], buf[4],
|
||||
buf[5], buf[6], buf[7], buf[8]);
|
||||
|
||||
ret = hid_hw_raw_request(data->hdev, buf[0], buf, BLINK1_CMD_SIZE,
|
||||
HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
|
||||
ret = hid_hw_raw_request(tdev->hdev, buf[0], buf, REPORT_SIZE,
|
||||
HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
|
||||
|
||||
return ret < 0 ? ret : 0;
|
||||
}
|
||||
|
||||
static int blink1_update_color(struct blink1_data *data)
|
||||
static int thingm_recv(struct thingm_device *tdev, u8 buf[REPORT_SIZE])
|
||||
{
|
||||
u8 buf[BLINK1_CMD_SIZE] = { 1, 'n', 0, 0, 0, 0, 0, 0, 0 };
|
||||
int ret;
|
||||
|
||||
if (data->brightness) {
|
||||
unsigned int coef = DIV_ROUND_CLOSEST(255, data->brightness);
|
||||
ret = hid_hw_raw_request(tdev->hdev, buf[0], buf, REPORT_SIZE,
|
||||
HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
buf[2] = DIV_ROUND_CLOSEST(blink1_rgb_to_r(data->rgb), coef);
|
||||
buf[3] = DIV_ROUND_CLOSEST(blink1_rgb_to_g(data->rgb), coef);
|
||||
buf[4] = DIV_ROUND_CLOSEST(blink1_rgb_to_b(data->rgb), coef);
|
||||
}
|
||||
hid_dbg(tdev->hdev, "<- %d %c %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx\n",
|
||||
buf[0], buf[1], buf[2], buf[3], buf[4],
|
||||
buf[5], buf[6], buf[7], buf[8]);
|
||||
|
||||
if (data->fade) {
|
||||
buf[1] = 'c';
|
||||
buf[5] = (data->fade & 0xFF00) >> 8;
|
||||
buf[6] = (data->fade & 0x00FF);
|
||||
}
|
||||
|
||||
return blink1_send_command(data, buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void blink1_led_set(struct led_classdev *led_cdev,
|
||||
static int thingm_version(struct thingm_device *tdev)
|
||||
{
|
||||
u8 buf[REPORT_SIZE] = { REPORT_ID, 'v', 0, 0, 0, 0, 0, 0, 0 };
|
||||
int err;
|
||||
|
||||
err = thingm_send(tdev, buf);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = thingm_recv(tdev, buf);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
tdev->version.major = buf[3];
|
||||
tdev->version.minor = buf[4];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int thingm_write_color(struct thingm_rgb *rgb)
|
||||
{
|
||||
u8 buf[REPORT_SIZE] = { REPORT_ID, 'c', 0, 0, 0, 0, 0, rgb->num, 0 };
|
||||
|
||||
buf[2] = rgb->red.ldev.brightness;
|
||||
buf[3] = rgb->green.ldev.brightness;
|
||||
buf[4] = rgb->blue.ldev.brightness;
|
||||
|
||||
return thingm_send(rgb->tdev, buf);
|
||||
}
|
||||
|
||||
static void thingm_work(struct work_struct *work)
|
||||
{
|
||||
struct thingm_rgb *rgb = container_of(work, struct thingm_rgb, work);
|
||||
|
||||
mutex_lock(&rgb->tdev->lock);
|
||||
|
||||
if (thingm_write_color(rgb))
|
||||
hid_err(rgb->tdev->hdev, "failed to write color\n");
|
||||
|
||||
mutex_unlock(&rgb->tdev->lock);
|
||||
}
|
||||
|
||||
static void thingm_led_set(struct led_classdev *ldev,
|
||||
enum led_brightness brightness)
|
||||
{
|
||||
struct blink1_data *data = dev_get_drvdata(led_cdev->dev->parent);
|
||||
struct thingm_led *led = container_of(ldev, struct thingm_led, ldev);
|
||||
|
||||
data->brightness = brightness;
|
||||
if (blink1_update_color(data))
|
||||
hid_err(data->hdev, "failed to update color\n");
|
||||
/* the ledclass has already stored the brightness value */
|
||||
schedule_work(&led->rgb->work);
|
||||
}
|
||||
|
||||
static enum led_brightness blink1_led_get(struct led_classdev *led_cdev)
|
||||
static int thingm_init_rgb(struct thingm_rgb *rgb)
|
||||
{
|
||||
struct blink1_data *data = dev_get_drvdata(led_cdev->dev->parent);
|
||||
const int minor = ((struct hidraw *) rgb->tdev->hdev->hidraw)->minor;
|
||||
int err;
|
||||
|
||||
return data->brightness;
|
||||
}
|
||||
/* Register the red diode */
|
||||
snprintf(rgb->red.name, sizeof(rgb->red.name),
|
||||
"thingm%d:red:led%d", minor, rgb->num);
|
||||
rgb->red.ldev.name = rgb->red.name;
|
||||
rgb->red.ldev.max_brightness = 255;
|
||||
rgb->red.ldev.brightness_set = thingm_led_set;
|
||||
rgb->red.rgb = rgb;
|
||||
|
||||
static ssize_t blink1_show_rgb(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct blink1_data *data = dev_get_drvdata(dev->parent);
|
||||
err = led_classdev_register(&rgb->tdev->hdev->dev, &rgb->red.ldev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return sprintf(buf, "%.6X\n", data->rgb);
|
||||
}
|
||||
/* Register the green diode */
|
||||
snprintf(rgb->green.name, sizeof(rgb->green.name),
|
||||
"thingm%d:green:led%d", minor, rgb->num);
|
||||
rgb->green.ldev.name = rgb->green.name;
|
||||
rgb->green.ldev.max_brightness = 255;
|
||||
rgb->green.ldev.brightness_set = thingm_led_set;
|
||||
rgb->green.rgb = rgb;
|
||||
|
||||
static ssize_t blink1_store_rgb(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
struct blink1_data *data = dev_get_drvdata(dev->parent);
|
||||
long unsigned int rgb;
|
||||
int ret;
|
||||
err = led_classdev_register(&rgb->tdev->hdev->dev, &rgb->green.ldev);
|
||||
if (err)
|
||||
goto unregister_red;
|
||||
|
||||
ret = kstrtoul(buf, 16, &rgb);
|
||||
if (ret)
|
||||
return ret;
|
||||
/* Register the blue diode */
|
||||
snprintf(rgb->blue.name, sizeof(rgb->blue.name),
|
||||
"thingm%d:blue:led%d", minor, rgb->num);
|
||||
rgb->blue.ldev.name = rgb->blue.name;
|
||||
rgb->blue.ldev.max_brightness = 255;
|
||||
rgb->blue.ldev.brightness_set = thingm_led_set;
|
||||
rgb->blue.rgb = rgb;
|
||||
|
||||
/* RGB triplet notation is 24-bit hexadecimal */
|
||||
if (rgb > 0xFFFFFF)
|
||||
return -EINVAL;
|
||||
err = led_classdev_register(&rgb->tdev->hdev->dev, &rgb->blue.ldev);
|
||||
if (err)
|
||||
goto unregister_green;
|
||||
|
||||
data->rgb = rgb;
|
||||
ret = blink1_update_color(data);
|
||||
|
||||
return ret ? ret : count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(rgb, S_IRUGO | S_IWUSR, blink1_show_rgb, blink1_store_rgb);
|
||||
|
||||
static ssize_t blink1_show_fade(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct blink1_data *data = dev_get_drvdata(dev->parent);
|
||||
|
||||
return sprintf(buf, "%d\n", data->fade * 10);
|
||||
}
|
||||
|
||||
static ssize_t blink1_store_fade(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
struct blink1_data *data = dev_get_drvdata(dev->parent);
|
||||
long unsigned int fade;
|
||||
int ret;
|
||||
|
||||
ret = kstrtoul(buf, 10, &fade);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* blink(1) accepts 16-bit fade time, number of 10ms ticks */
|
||||
fade = DIV_ROUND_CLOSEST(fade, 10);
|
||||
if (fade > 65535)
|
||||
return -EINVAL;
|
||||
|
||||
data->fade = fade;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(fade, S_IRUGO | S_IWUSR,
|
||||
blink1_show_fade, blink1_store_fade);
|
||||
|
||||
static ssize_t blink1_show_play(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct blink1_data *data = dev_get_drvdata(dev->parent);
|
||||
|
||||
return sprintf(buf, "%d\n", data->play);
|
||||
}
|
||||
|
||||
static ssize_t blink1_store_play(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
struct blink1_data *data = dev_get_drvdata(dev->parent);
|
||||
u8 cmd[BLINK1_CMD_SIZE] = { 1, 'p', 0, 0, 0, 0, 0, 0, 0 };
|
||||
long unsigned int play;
|
||||
int ret;
|
||||
|
||||
ret = kstrtoul(buf, 10, &play);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
data->play = !!play;
|
||||
cmd[2] = data->play;
|
||||
ret = blink1_send_command(data, cmd);
|
||||
|
||||
return ret ? ret : count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(play, S_IRUGO | S_IWUSR,
|
||||
blink1_show_play, blink1_store_play);
|
||||
|
||||
static const struct attribute_group blink1_sysfs_group = {
|
||||
.attrs = (struct attribute *[]) {
|
||||
&dev_attr_rgb.attr,
|
||||
&dev_attr_fade.attr,
|
||||
&dev_attr_play.attr,
|
||||
NULL
|
||||
},
|
||||
};
|
||||
|
||||
static int thingm_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
||||
{
|
||||
struct blink1_data *data;
|
||||
struct led_classdev *led;
|
||||
char led_name[13];
|
||||
int ret;
|
||||
|
||||
data = devm_kzalloc(&hdev->dev, sizeof(struct blink1_data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
hid_set_drvdata(hdev, data);
|
||||
data->hdev = hdev;
|
||||
data->rgb = 0xFFFFFF; /* set a default white color */
|
||||
|
||||
ret = hid_parse(hdev);
|
||||
if (ret)
|
||||
goto error;
|
||||
|
||||
ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
|
||||
if (ret)
|
||||
goto error;
|
||||
|
||||
/* blink(1) serial numbers range is 0x1A001000 to 0x1A002FFF */
|
||||
led = &data->led_cdev;
|
||||
snprintf(led_name, sizeof(led_name), "blink1::%s", hdev->uniq + 4);
|
||||
led->name = led_name;
|
||||
led->brightness_set = blink1_led_set;
|
||||
led->brightness_get = blink1_led_get;
|
||||
ret = led_classdev_register(&hdev->dev, led);
|
||||
if (ret)
|
||||
goto stop;
|
||||
|
||||
ret = sysfs_create_group(&led->dev->kobj, &blink1_sysfs_group);
|
||||
if (ret)
|
||||
goto remove_led;
|
||||
INIT_WORK(&rgb->work, thingm_work);
|
||||
|
||||
return 0;
|
||||
|
||||
remove_led:
|
||||
led_classdev_unregister(led);
|
||||
unregister_green:
|
||||
led_classdev_unregister(&rgb->green.ldev);
|
||||
|
||||
unregister_red:
|
||||
led_classdev_unregister(&rgb->red.ldev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void thingm_remove_rgb(struct thingm_rgb *rgb)
|
||||
{
|
||||
flush_work(&rgb->work);
|
||||
led_classdev_unregister(&rgb->red.ldev);
|
||||
led_classdev_unregister(&rgb->green.ldev);
|
||||
led_classdev_unregister(&rgb->blue.ldev);
|
||||
}
|
||||
|
||||
static int thingm_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
||||
{
|
||||
struct thingm_device *tdev;
|
||||
int i, err;
|
||||
|
||||
tdev = devm_kzalloc(&hdev->dev, sizeof(struct thingm_device),
|
||||
GFP_KERNEL);
|
||||
if (!tdev)
|
||||
return -ENOMEM;
|
||||
|
||||
tdev->hdev = hdev;
|
||||
hid_set_drvdata(hdev, tdev);
|
||||
|
||||
err = hid_parse(hdev);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
err = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
mutex_init(&tdev->lock);
|
||||
|
||||
err = thingm_version(tdev);
|
||||
if (err)
|
||||
goto stop;
|
||||
|
||||
hid_dbg(hdev, "firmware version: %c.%c\n",
|
||||
tdev->version.major, tdev->version.minor);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(thingm_fwinfo) && !tdev->fwinfo; ++i)
|
||||
if (thingm_fwinfo[i].major == tdev->version.major)
|
||||
tdev->fwinfo = &thingm_fwinfo[i];
|
||||
|
||||
if (!tdev->fwinfo) {
|
||||
hid_err(hdev, "unsupported firmware %c\n", tdev->version.major);
|
||||
goto stop;
|
||||
}
|
||||
|
||||
tdev->rgb = devm_kzalloc(&hdev->dev,
|
||||
sizeof(struct thingm_rgb) * tdev->fwinfo->numrgb,
|
||||
GFP_KERNEL);
|
||||
if (!tdev->rgb) {
|
||||
err = -ENOMEM;
|
||||
goto stop;
|
||||
}
|
||||
|
||||
for (i = 0; i < tdev->fwinfo->numrgb; ++i) {
|
||||
struct thingm_rgb *rgb = tdev->rgb + i;
|
||||
|
||||
rgb->tdev = tdev;
|
||||
rgb->num = tdev->fwinfo->first + i;
|
||||
err = thingm_init_rgb(rgb);
|
||||
if (err) {
|
||||
while (--i >= 0)
|
||||
thingm_remove_rgb(tdev->rgb + i);
|
||||
goto stop;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
stop:
|
||||
hid_hw_stop(hdev);
|
||||
error:
|
||||
return ret;
|
||||
return err;
|
||||
}
|
||||
|
||||
static void thingm_remove(struct hid_device *hdev)
|
||||
{
|
||||
struct blink1_data *data = hid_get_drvdata(hdev);
|
||||
struct led_classdev *led = &data->led_cdev;
|
||||
struct thingm_device *tdev = hid_get_drvdata(hdev);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < tdev->fwinfo->numrgb; ++i)
|
||||
thingm_remove_rgb(tdev->rgb + i);
|
||||
|
||||
sysfs_remove_group(&led->dev->kobj, &blink1_sysfs_group);
|
||||
led_classdev_unregister(led);
|
||||
hid_hw_stop(hdev);
|
||||
}
|
||||
|
||||
|
@ -807,34 +807,18 @@ static int i2c_hid_fetch_hid_descriptor(struct i2c_hid *ihid)
|
||||
unsigned int dsize;
|
||||
int ret;
|
||||
|
||||
/* Fetch the length of HID description, retrieve the 4 first bytes:
|
||||
/* i2c hid fetch using a fixed descriptor size (30 bytes) */
|
||||
i2c_hid_dbg(ihid, "Fetching the HID descriptor\n");
|
||||
ret = i2c_hid_command(client, &hid_descr_cmd, ihid->hdesc_buffer,
|
||||
sizeof(struct i2c_hid_desc));
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "hid_descr_cmd failed\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* Validate the length of HID descriptor, the 4 first bytes:
|
||||
* bytes 0-1 -> length
|
||||
* bytes 2-3 -> bcdVersion (has to be 1.00) */
|
||||
ret = i2c_hid_command(client, &hid_descr_cmd, ihid->hdesc_buffer, 4);
|
||||
|
||||
i2c_hid_dbg(ihid, "%s, ihid->hdesc_buffer: %4ph\n", __func__,
|
||||
ihid->hdesc_buffer);
|
||||
|
||||
if (ret) {
|
||||
dev_err(&client->dev,
|
||||
"unable to fetch the size of HID descriptor (ret=%d)\n",
|
||||
ret);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
dsize = le16_to_cpu(hdesc->wHIDDescLength);
|
||||
/*
|
||||
* the size of the HID descriptor should at least contain
|
||||
* its size and the bcdVersion (4 bytes), and should not be greater
|
||||
* than sizeof(struct i2c_hid_desc) as we directly fill this struct
|
||||
* through i2c_hid_command.
|
||||
*/
|
||||
if (dsize < 4 || dsize > sizeof(struct i2c_hid_desc)) {
|
||||
dev_err(&client->dev, "weird size of HID descriptor (%u)\n",
|
||||
dsize);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* check bcdVersion == 1.0 */
|
||||
if (le16_to_cpu(hdesc->bcdVersion) != 0x0100) {
|
||||
dev_err(&client->dev,
|
||||
@ -843,17 +827,14 @@ static int i2c_hid_fetch_hid_descriptor(struct i2c_hid *ihid)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
i2c_hid_dbg(ihid, "Fetching the HID descriptor\n");
|
||||
|
||||
ret = i2c_hid_command(client, &hid_descr_cmd, ihid->hdesc_buffer,
|
||||
dsize);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "hid_descr_cmd Fail\n");
|
||||
/* Descriptor length should be 30 bytes as per the specification */
|
||||
dsize = le16_to_cpu(hdesc->wHIDDescLength);
|
||||
if (dsize != sizeof(struct i2c_hid_desc)) {
|
||||
dev_err(&client->dev, "weird size of HID descriptor (%u)\n",
|
||||
dsize);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
i2c_hid_dbg(ihid, "HID Descriptor: %*ph\n", dsize, ihid->hdesc_buffer);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -441,12 +441,11 @@ static int uhid_dev_create2(struct uhid_device *uhid,
|
||||
if (uhid->rd_size <= 0 || uhid->rd_size > HID_MAX_DESCRIPTOR_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
uhid->rd_data = kmalloc(uhid->rd_size, GFP_KERNEL);
|
||||
uhid->rd_data = kmemdup(ev->u.create2.rd_data, uhid->rd_size,
|
||||
GFP_KERNEL);
|
||||
if (!uhid->rd_data)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(uhid->rd_data, ev->u.create2.rd_data, uhid->rd_size);
|
||||
|
||||
hid = hid_allocate_device();
|
||||
if (IS_ERR(hid)) {
|
||||
ret = PTR_ERR(hid);
|
||||
|
@ -115,6 +115,7 @@ static const struct hid_blacklist {
|
||||
{ USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_I608X, HID_QUIRK_MULTI_INPUT },
|
||||
{ USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_M610X, HID_QUIRK_MULTI_INPUT },
|
||||
{ USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_DUOSENSE, HID_QUIRK_NO_INIT_REPORTS },
|
||||
{ USB_VENDOR_ID_SEMICO, USB_DEVICE_ID_SEMICO_USB_KEYKOARD, HID_QUIRK_NO_INIT_REPORTS },
|
||||
{ USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_LTS1, HID_QUIRK_NO_INIT_REPORTS },
|
||||
{ USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_LTS2, HID_QUIRK_NO_INIT_REPORTS },
|
||||
{ USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_HD, HID_QUIRK_NO_INIT_REPORTS },
|
||||
|
@ -479,6 +479,8 @@ config LEDS_OT200
|
||||
This option enables support for the LEDs on the Bachmann OT200.
|
||||
Say Y to enable LEDs on the Bachmann OT200.
|
||||
|
||||
comment "LED driver for blink(1) USB RGB LED is under Special HID drivers (HID_THINGM)"
|
||||
|
||||
config LEDS_BLINKM
|
||||
tristate "LED support for the BlinkM I2C RGB LED"
|
||||
depends on LEDS_CLASS
|
||||
|
@ -233,11 +233,6 @@ struct hid_item {
|
||||
#define HID_DG_BARRELSWITCH 0x000d0044
|
||||
#define HID_DG_ERASER 0x000d0045
|
||||
#define HID_DG_TABLETPICK 0x000d0046
|
||||
/*
|
||||
* as of May 20, 2009 the usages below are not yet in the official USB spec
|
||||
* but are being pushed by Microsft as described in their paper "Digitizer
|
||||
* Drivers for Windows Touch and Pen-Based Computers"
|
||||
*/
|
||||
#define HID_DG_CONFIDENCE 0x000d0047
|
||||
#define HID_DG_WIDTH 0x000d0048
|
||||
#define HID_DG_HEIGHT 0x000d0049
|
||||
@ -246,6 +241,8 @@ struct hid_item {
|
||||
#define HID_DG_DEVICEINDEX 0x000d0053
|
||||
#define HID_DG_CONTACTCOUNT 0x000d0054
|
||||
#define HID_DG_CONTACTMAX 0x000d0055
|
||||
#define HID_DG_BARRELSWITCH2 0x000d005a
|
||||
#define HID_DG_TOOLSERIALNUMBER 0x000d005b
|
||||
|
||||
/*
|
||||
* HID report types --- Ouch! HID spec says 1 2 3!
|
||||
@ -299,12 +296,20 @@ struct hid_item {
|
||||
|
||||
/*
|
||||
* HID device groups
|
||||
*
|
||||
* Note: HID_GROUP_ANY is declared in linux/mod_devicetable.h
|
||||
* and has a value of 0x0000
|
||||
*/
|
||||
#define HID_GROUP_GENERIC 0x0001
|
||||
#define HID_GROUP_MULTITOUCH 0x0002
|
||||
#define HID_GROUP_SENSOR_HUB 0x0003
|
||||
#define HID_GROUP_MULTITOUCH_WIN_8 0x0004
|
||||
|
||||
/*
|
||||
* Vendor specific HID device groups
|
||||
*/
|
||||
#define HID_GROUP_RMI 0x0100
|
||||
|
||||
/*
|
||||
* This is the global environment of the parser. This information is
|
||||
* persistent for main-items. The global environment can be saved and
|
||||
@ -570,6 +575,8 @@ struct hid_descriptor {
|
||||
.bus = BUS_USB, .vendor = (ven), .product = (prod)
|
||||
#define HID_BLUETOOTH_DEVICE(ven, prod) \
|
||||
.bus = BUS_BLUETOOTH, .vendor = (ven), .product = (prod)
|
||||
#define HID_I2C_DEVICE(ven, prod) \
|
||||
.bus = BUS_I2C, .vendor = (ven), .product = (prod)
|
||||
|
||||
#define HID_REPORT_ID(rep) \
|
||||
.report_type = (rep)
|
||||
|
@ -462,7 +462,10 @@ struct input_keymap_entry {
|
||||
#define KEY_VIDEO_NEXT 241 /* drive next video source */
|
||||
#define KEY_VIDEO_PREV 242 /* drive previous video source */
|
||||
#define KEY_BRIGHTNESS_CYCLE 243 /* brightness up, after max is min */
|
||||
#define KEY_BRIGHTNESS_ZERO 244 /* brightness off, use ambient */
|
||||
#define KEY_BRIGHTNESS_AUTO 244 /* Set Auto Brightness: manual
|
||||
brightness control is off,
|
||||
rely on ambient */
|
||||
#define KEY_BRIGHTNESS_ZERO KEY_BRIGHTNESS_AUTO
|
||||
#define KEY_DISPLAY_OFF 245 /* display device to off state */
|
||||
|
||||
#define KEY_WWAN 246 /* Wireless WAN (LTE, UMTS, GSM, etc.) */
|
||||
@ -632,6 +635,7 @@ struct input_keymap_entry {
|
||||
#define KEY_ADDRESSBOOK 0x1ad /* AL Contacts/Address Book */
|
||||
#define KEY_MESSENGER 0x1ae /* AL Instant Messaging */
|
||||
#define KEY_DISPLAYTOGGLE 0x1af /* Turn display (LCD) on and off */
|
||||
#define KEY_BRIGHTNESS_TOGGLE KEY_DISPLAYTOGGLE
|
||||
#define KEY_SPELLCHECK 0x1b0 /* AL Spell Check */
|
||||
#define KEY_LOGOFF 0x1b1 /* AL Logoff */
|
||||
|
||||
@ -723,6 +727,17 @@ struct input_keymap_entry {
|
||||
|
||||
#define KEY_ALS_TOGGLE 0x230 /* Ambient light sensor */
|
||||
|
||||
#define KEY_BUTTONCONFIG 0x240 /* AL Button Configuration */
|
||||
#define KEY_TASKMANAGER 0x241 /* AL Task/Project Manager */
|
||||
#define KEY_JOURNAL 0x242 /* AL Log/Journal/Timecard */
|
||||
#define KEY_CONTROLPANEL 0x243 /* AL Control Panel */
|
||||
#define KEY_APPSELECT 0x244 /* AL Select Task/Application */
|
||||
#define KEY_SCREENSAVER 0x245 /* AL Screen Saver */
|
||||
#define KEY_VOICECOMMAND 0x246 /* Listening Voice Command */
|
||||
|
||||
#define KEY_BRIGHTNESS_MIN 0x250 /* Set Brightness to Minimum */
|
||||
#define KEY_BRIGHTNESS_MAX 0x251 /* Set Brightness to Maximum */
|
||||
|
||||
#define BTN_TRIGGER_HAPPY 0x2c0
|
||||
#define BTN_TRIGGER_HAPPY1 0x2c0
|
||||
#define BTN_TRIGGER_HAPPY2 0x2c1
|
||||
|
Loading…
Reference in New Issue
Block a user