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:
Linus Torvalds 2014-06-04 08:52:36 -07:00
commit d6b92c2c37
18 changed files with 1851 additions and 360 deletions

View File

@ -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.

View File

@ -608,7 +608,10 @@ config HID_SAITEK
Support for Saitek devices that are not fully compliant with the Support for Saitek devices that are not fully compliant with the
HID standard. 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 config HID_SAMSUNG
tristate "Samsung InfraRed remote control or keyboards" tristate "Samsung InfraRed remote control or keyboards"
@ -657,6 +660,14 @@ config HID_SUNPLUS
---help--- ---help---
Support for Sunplus wireless desktop. 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 config HID_GREENASIA
tristate "GreenAsia (Product ID 0x12) game controller support" tristate "GreenAsia (Product ID 0x12) game controller support"
depends on HID depends on HID

View File

@ -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-arvo.o hid-roccat-isku.o hid-roccat-kone.o \
hid-roccat-koneplus.o hid-roccat-konepure.o hid-roccat-kovaplus.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 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_SAITEK) += hid-saitek.o
obj-$(CONFIG_HID_SAMSUNG) += hid-samsung.o obj-$(CONFIG_HID_SAMSUNG) += hid-samsung.o
obj-$(CONFIG_HID_SMARTJOYPLUS) += hid-sjoy.o obj-$(CONFIG_HID_SMARTJOYPLUS) += hid-sjoy.o

View File

@ -779,6 +779,14 @@ static int hid_scan_report(struct hid_device *hid)
(hid->group == HID_GROUP_MULTITOUCH)) (hid->group == HID_GROUP_MULTITOUCH))
hid->group = HID_GROUP_MULTITOUCH_WIN_8; 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); vfree(parser);
return 0; 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 * ->numbered being checked, which may not always be the case when
* drivers go to access report values. * drivers go to access report values.
*/ */
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]; report = hid->report_enum[type].report_id_hash[id];
}
if (!report) { if (!report) {
hid_err(hid, "missing %s %u\n", hid_report_names[type], id); hid_err(hid, "missing %s %u\n", hid_report_names[type], id);
return NULL; 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_RYOS_MK_PRO) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_SAVU) }, { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_SAVU) },
#endif #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_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_IR_REMOTE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD_MOUSE) }, { 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) }, { HID_USB_DEVICE(USB_VENDOR_ID_SKYCABLE, USB_DEVICE_ID_SKYCABLE_WIRELESS_PRESENTER) },

View File

@ -165,6 +165,8 @@ static const struct hid_usage_entry hid_usage_table[] = {
{0, 0x53, "DeviceIndex"}, {0, 0x53, "DeviceIndex"},
{0, 0x54, "ContactCount"}, {0, 0x54, "ContactCount"},
{0, 0x55, "ContactMaximumNumber"}, {0, 0x55, "ContactMaximumNumber"},
{0, 0x5A, "SecondaryBarrelSwitch"},
{0, 0x5B, "TransducerSerialNumber"},
{ 15, 0, "PhysicalInterfaceDevice" }, { 15, 0, "PhysicalInterfaceDevice" },
{0, 0x00, "Undefined"}, {0, 0x00, "Undefined"},
{0, 0x01, "Physical_Interface_Device"}, {0, 0x01, "Physical_Interface_Device"},
@ -272,6 +274,85 @@ static const struct hid_usage_entry hid_usage_table[] = {
{0, 0xAA, "Shared_Parameter_Blocks"}, {0, 0xAA, "Shared_Parameter_Blocks"},
{0, 0xAB, "Create_New_Effect_Report"}, {0, 0xAB, "Create_New_Effect_Report"},
{0, 0xAC, "RAM_Pool_Available"}, {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, 0, "Power Device" },
{ 0x84, 0x02, "PresentStatus" }, { 0x84, 0x02, "PresentStatus" },
{ 0x84, 0x03, "ChangeStatus" }, { 0x84, 0x03, "ChangeStatus" },
@ -855,6 +936,16 @@ static const char *keys[KEY_MAX + 1] = {
[KEY_KBDILLUMDOWN] = "KbdIlluminationDown", [KEY_KBDILLUMDOWN] = "KbdIlluminationDown",
[KEY_KBDILLUMUP] = "KbdIlluminationUp", [KEY_KBDILLUMUP] = "KbdIlluminationUp",
[KEY_SWITCHVIDEOMODE] = "SwitchVideoMode", [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] = { static const char *relatives[REL_MAX + 1] = {

View File

@ -463,6 +463,7 @@
#define USB_VENDOR_ID_STM_0 0x0483 #define USB_VENDOR_ID_STM_0 0x0483
#define USB_DEVICE_ID_STM_HID_SENSOR 0x91d1 #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_VENDOR_ID_ION 0x15e4
#define USB_DEVICE_ID_ICADE 0x0132 #define USB_DEVICE_ID_ICADE 0x0132
@ -633,6 +634,9 @@
#define USB_DEVICE_ID_MS_PRESENTER_8K_USB 0x0713 #define USB_DEVICE_ID_MS_PRESENTER_8K_USB 0x0713
#define USB_DEVICE_ID_MS_DIGITAL_MEDIA_3K 0x0730 #define USB_DEVICE_ID_MS_DIGITAL_MEDIA_3K 0x0730
#define USB_DEVICE_ID_MS_COMFORT_MOUSE_4500 0x076c #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_VENDOR_ID_MOJO 0x8282
#define USB_DEVICE_ID_RETRO_ADAPTER 0x3201 #define USB_DEVICE_ID_RETRO_ADAPTER 0x3201
@ -764,11 +768,16 @@
#define USB_VENDOR_ID_SAITEK 0x06a3 #define USB_VENDOR_ID_SAITEK 0x06a3
#define USB_DEVICE_ID_SAITEK_RUMBLEPAD 0xff17 #define USB_DEVICE_ID_SAITEK_RUMBLEPAD 0xff17
#define USB_DEVICE_ID_SAITEK_PS1000 0x0621 #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_VENDOR_ID_SAMSUNG 0x0419
#define USB_DEVICE_ID_SAMSUNG_IR_REMOTE 0x0001 #define USB_DEVICE_ID_SAMSUNG_IR_REMOTE 0x0001
#define USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD_MOUSE 0x0600 #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_VENDOR_ID_SENNHEISER 0x1395
#define USB_DEVICE_ID_SENNHEISER_BTD500USB 0x002c #define USB_DEVICE_ID_SENNHEISER_BTD500USB 0x002c

View File

@ -684,9 +684,14 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
break; break;
case 0x46: /* TabletPick */ case 0x46: /* TabletPick */
case 0x5a: /* SecondaryBarrelSwitch */
map_key_clear(BTN_STYLUS2); map_key_clear(BTN_STYLUS2);
break; break;
case 0x5b: /* TransducerSerialNumber */
set_bit(MSC_SERIAL, input->mscbit);
break;
default: goto unknown; default: goto unknown;
} }
break; 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 0x06c: map_key_clear(KEY_YELLOW); break;
case 0x06d: map_key_clear(KEY_ZOOM); 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 0x082: map_key_clear(KEY_VIDEO_NEXT); break;
case 0x083: map_key_clear(KEY_LAST); break; case 0x083: map_key_clear(KEY_LAST); break;
case 0x084: map_key_clear(KEY_ENTER); 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 0x0bf: map_key_clear(KEY_SLOW); break;
case 0x0cd: map_key_clear(KEY_PLAYPAUSE); 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 0x0e0: map_abs_clear(ABS_VOLUME); break;
case 0x0e2: map_key_clear(KEY_MUTE); break; case 0x0e2: map_key_clear(KEY_MUTE); break;
case 0x0e5: map_key_clear(KEY_BASSBOOST); 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 0x0ea: map_key_clear(KEY_VOLUMEDOWN); break;
case 0x0f5: map_key_clear(KEY_SLOW); 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 0x182: map_key_clear(KEY_BOOKMARKS); break;
case 0x183: map_key_clear(KEY_CONFIG); break; case 0x183: map_key_clear(KEY_CONFIG); break;
case 0x184: map_key_clear(KEY_WORDPROCESSOR); 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 0x18c: map_key_clear(KEY_VOICEMAIL); break;
case 0x18d: map_key_clear(KEY_ADDRESSBOOK); break; case 0x18d: map_key_clear(KEY_ADDRESSBOOK); break;
case 0x18e: map_key_clear(KEY_CALENDAR); 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 0x191: map_key_clear(KEY_FINANCE); break;
case 0x192: map_key_clear(KEY_CALC); break; case 0x192: map_key_clear(KEY_CALC); break;
case 0x193: map_key_clear(KEY_PLAYER); 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 0x199: map_key_clear(KEY_CHAT); break;
case 0x19c: map_key_clear(KEY_LOGOFF); break; case 0x19c: map_key_clear(KEY_LOGOFF); break;
case 0x19e: map_key_clear(KEY_COFFEE); 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 0x1a3: map_key_clear(KEY_NEXT); break;
case 0x1a4: map_key_clear(KEY_PREVIOUS); break; case 0x1a4: map_key_clear(KEY_PREVIOUS); break;
case 0x1a6: map_key_clear(KEY_HELP); break; case 0x1a6: map_key_clear(KEY_HELP); break;
case 0x1a7: map_key_clear(KEY_DOCUMENTS); break; case 0x1a7: map_key_clear(KEY_DOCUMENTS); break;
case 0x1ab: map_key_clear(KEY_SPELLCHECK); break; case 0x1ab: map_key_clear(KEY_SPELLCHECK); break;
case 0x1ae: map_key_clear(KEY_KEYBOARD); 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 0x1b4: map_key_clear(KEY_FILE); break;
case 0x1b6: map_key_clear(KEY_IMAGES); break; case 0x1b6: map_key_clear(KEY_IMAGES); break;
case 0x1b7: map_key_clear(KEY_AUDIO); break; case 0x1b7: map_key_clear(KEY_AUDIO); break;

920
drivers/hid/hid-rmi.c Normal file
View 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");

View File

@ -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 * 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. * clearing the constant bit on the input reports for buttons and d-pad.
* (This module is based on "hid-ortek".) * (This module is based on "hid-ortek".)
*
* Copyright (c) 2012 Andreas Hübner * 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" #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, static __u8 *saitek_report_fixup(struct hid_device *hdev, __u8 *rdesc,
unsigned int *rsize) unsigned int *rsize)
{ {
if (*rsize == 137 && rdesc[20] == 0x09 && rdesc[21] == 0x33 struct saitek_sc *ssc = hid_get_drvdata(hdev);
&& rdesc[94] == 0x81 && rdesc[95] == 0x03
&& rdesc[110] == 0x81 && rdesc[111] == 0x03) { 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"); 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; 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[] = { 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 = { static struct hid_driver saitek_driver = {
.name = "saitek", .name = "saitek",
.id_table = saitek_devices, .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); module_hid_driver(saitek_driver);

View File

@ -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, { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_INTEL_1,
USB_DEVICE_ID_INTEL_HID_SENSOR_1), USB_DEVICE_ID_INTEL_HID_SENSOR_1),
.driver_data = HID_SENSOR_HUB_ENUM_QUIRK}, .driver_data = HID_SENSOR_HUB_ENUM_QUIRK},
{ HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_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, { 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}, .driver_data = HID_SENSOR_HUB_ENUM_QUIRK},
{ HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_TEXAS_INSTRUMENTS, { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_TEXAS_INSTRUMENTS,
USB_DEVICE_ID_TEXAS_INSTRUMENTS_LENOVO_YOGA), USB_DEVICE_ID_TEXAS_INSTRUMENTS_LENOVO_YOGA),

View File

@ -33,6 +33,7 @@
#include <linux/power_supply.h> #include <linux/power_supply.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/list.h> #include <linux/list.h>
#include <linux/idr.h>
#include <linux/input/mt.h> #include <linux/input/mt.h>
#include "hid-ids.h" #include "hid-ids.h"
@ -717,8 +718,39 @@ static enum power_supply_property sony_battery_props[] = {
POWER_SUPPLY_PROP_STATUS, 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 spinlock_t sony_dev_list_lock;
static LIST_HEAD(sony_device_list); static LIST_HEAD(sony_device_list);
static DEFINE_IDA(sony_device_id_allocator);
struct sony_sc { struct sony_sc {
spinlock_t lock; spinlock_t lock;
@ -728,6 +760,7 @@ struct sony_sc {
unsigned long quirks; unsigned long quirks;
struct work_struct state_worker; struct work_struct state_worker;
struct power_supply battery; struct power_supply battery;
int device_id;
#ifdef CONFIG_SONY_FF #ifdef CONFIG_SONY_FF
__u8 left; __u8 left;
@ -740,6 +773,8 @@ struct sony_sc {
__u8 battery_charging; __u8 battery_charging;
__u8 battery_capacity; __u8 battery_capacity;
__u8 led_state[MAX_LEDS]; __u8 led_state[MAX_LEDS];
__u8 led_delay_on[MAX_LEDS];
__u8 led_delay_off[MAX_LEDS];
__u8 led_count; __u8 led_count;
}; };
@ -1048,6 +1083,52 @@ static int dualshock4_set_operational_bt(struct hid_device *hdev)
HID_FEATURE_REPORT, HID_REQ_GET_REPORT); 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) static void buzz_set_leds(struct hid_device *hdev, const __u8 *leds)
{ {
struct list_head *report_list = 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); 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; int n;
BUG_ON(count > MAX_LEDS); BUG_ON(count > MAX_LEDS);
if (drv_data->quirks & BUZZ_CONTROLLER && count == 4) { if (sc->quirks & BUZZ_CONTROLLER && count == 4) {
buzz_set_leds(hdev, leds); buzz_set_leds(sc->hdev, leds);
} else { } else {
for (n = 0; n < count; n++) for (n = 0; n < count; n++)
drv_data->led_state[n] = leds[n]; sc->led_state[n] = leds[n];
schedule_work(&drv_data->state_worker); schedule_work(&sc->state_worker);
} }
} }
@ -1090,6 +1170,7 @@ static void sony_led_set_brightness(struct led_classdev *led,
struct sony_sc *drv_data; struct sony_sc *drv_data;
int n; int n;
int force_update;
drv_data = hid_get_drvdata(hdev); drv_data = hid_get_drvdata(hdev);
if (!drv_data) { if (!drv_data) {
@ -1097,12 +1178,29 @@ static void sony_led_set_brightness(struct led_classdev *led,
return; 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++) { for (n = 0; n < drv_data->led_count; n++) {
if (led == drv_data->leds[n]) { if (led == drv_data->leds[n] && (force_update ||
if (value != drv_data->led_state[n]) { (value != drv_data->led_state[n] ||
drv_data->led_delay_on[n] ||
drv_data->led_delay_off[n]))) {
drv_data->led_state[n] = value; drv_data->led_state[n] = value;
sony_set_leds(hdev, drv_data->led_state, drv_data->led_count);
} /* 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; break;
} }
} }
@ -1130,63 +1228,112 @@ static enum led_brightness sony_led_get_brightness(struct led_classdev *led)
return LED_OFF; 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; struct led_classdev *led;
int n; int n;
drv_data = hid_get_drvdata(hdev); BUG_ON(!(sc->quirks & SONY_LED_SUPPORT));
BUG_ON(!(drv_data->quirks & SONY_LED_SUPPORT));
for (n = 0; n < drv_data->led_count; n++) { for (n = 0; n < sc->led_count; n++) {
led = drv_data->leds[n]; led = sc->leds[n];
drv_data->leds[n] = NULL; sc->leds[n] = NULL;
if (!led) if (!led)
continue; continue;
led_classdev_unregister(led); led_classdev_unregister(led);
kfree(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 n, ret = 0;
int max_brightness; int use_ds4_names;
int use_colors;
struct led_classdev *led; struct led_classdev *led;
size_t name_sz; size_t name_sz;
char *name; char *name;
size_t name_len; size_t name_len;
const char *name_fmt; const char *name_fmt;
static const char * const color_str[] = { "red", "green", "blue" }; static const char * const ds4_name_str[] = { "red", "green", "blue",
static const __u8 initial_values[MAX_LEDS] = { 0x00, 0x00, 0x00, 0x00 }; "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(!(sc->quirks & SONY_LED_SUPPORT));
BUG_ON(!(drv_data->quirks & SONY_LED_SUPPORT));
if (drv_data->quirks & BUZZ_CONTROLLER) { if (sc->quirks & BUZZ_CONTROLLER) {
drv_data->led_count = 4; sc->led_count = 4;
max_brightness = 1; use_ds4_names = 0;
use_colors = 0;
name_len = strlen("::buzz#"); name_len = strlen("::buzz#");
name_fmt = "%s::buzz%d"; name_fmt = "%s::buzz%d";
/* Validate expected report characteristics. */ /* Validate expected report characteristics. */
if (!hid_validate_values(hdev, HID_OUTPUT_REPORT, 0, 0, 7)) if (!hid_validate_values(hdev, HID_OUTPUT_REPORT, 0, 0, 7))
return -ENODEV; return -ENODEV;
} else if (drv_data->quirks & DUALSHOCK4_CONTROLLER) { } else if (sc->quirks & DUALSHOCK4_CONTROLLER) {
drv_data->led_count = 3; dualshock4_set_leds_from_id(sc->device_id, initial_values);
max_brightness = 255; initial_values[3] = 1;
use_colors = 1; sc->led_count = 4;
memset(max_brightness, 255, 3);
use_hw_blink[3] = 1;
use_ds4_names = 1;
name_len = 0; name_len = 0;
name_fmt = "%s:%s"; name_fmt = "%s:%s";
} else { } else {
drv_data->led_count = 4; sixaxis_set_leds_from_id(sc->device_id, initial_values);
max_brightness = 1; sc->led_count = 4;
use_colors = 0; memset(use_hw_blink, 1, 4);
use_ds4_names = 0;
name_len = strlen("::sony#"); name_len = strlen("::sony#");
name_fmt = "%s::sony%d"; 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 * only relevant if the driver is loaded after somebody actively set the
* LEDs to on * 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; 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) if (use_ds4_names)
name_sz = strlen(dev_name(&hdev->dev)) + strlen(color_str[n]) + 2; name_sz = strlen(dev_name(&hdev->dev)) + strlen(ds4_name_str[n]) + 2;
led = kzalloc(sizeof(struct led_classdev) + name_sz, GFP_KERNEL); led = kzalloc(sizeof(struct led_classdev) + name_sz, GFP_KERNEL);
if (!led) { if (!led) {
@ -1213,30 +1360,35 @@ static int sony_leds_init(struct hid_device *hdev)
} }
name = (void *)(&led[1]); name = (void *)(&led[1]);
if (use_colors) if (use_ds4_names)
snprintf(name, name_sz, name_fmt, dev_name(&hdev->dev), color_str[n]); snprintf(name, name_sz, name_fmt, dev_name(&hdev->dev),
ds4_name_str[n]);
else else
snprintf(name, name_sz, name_fmt, dev_name(&hdev->dev), n + 1); snprintf(name, name_sz, name_fmt, dev_name(&hdev->dev), n + 1);
led->name = name; led->name = name;
led->brightness = 0; led->brightness = initial_values[n];
led->max_brightness = max_brightness; led->max_brightness = max_brightness[n];
led->brightness_get = sony_led_get_brightness; led->brightness_get = sony_led_get_brightness;
led->brightness_set = sony_led_set_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); ret = led_classdev_register(&hdev->dev, led);
if (ret) { if (ret) {
hid_err(hdev, "Failed to register LED %d\n", n); hid_err(hdev, "Failed to register LED %d\n", n);
sc->leds[n] = NULL;
kfree(led); kfree(led);
goto error_leds; goto error_leds;
} }
drv_data->leds[n] = led;
} }
return ret; return ret;
error_leds: error_leds:
sony_leds_remove(hdev); sony_leds_remove(sc);
return ret; return ret;
} }
@ -1244,7 +1396,9 @@ static int sony_leds_init(struct hid_device *hdev)
static void sixaxis_state_worker(struct work_struct *work) static void sixaxis_state_worker(struct work_struct *work)
{ {
struct sony_sc *sc = container_of(work, struct sony_sc, state_worker); struct sony_sc *sc = container_of(work, struct sony_sc, state_worker);
unsigned char buf[] = { int n;
union sixaxis_output_report_01 report = {
.buf = {
0x01, 0x01,
0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@ -1253,20 +1407,41 @@ static void sixaxis_state_worker(struct work_struct *work)
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 0x00, 0x00, 0x00, 0x00, 0x00
}
}; };
#ifdef CONFIG_SONY_FF #ifdef CONFIG_SONY_FF
buf[3] = sc->right ? 1 : 0; report.data.rumble.right_motor_on = sc->right ? 1 : 0;
buf[5] = sc->left; report.data.rumble.left_motor_force = sc->left;
#endif #endif
buf[10] |= sc->led_state[0] << 1; report.data.leds_bitmap |= sc->led_state[0] << 1;
buf[10] |= sc->led_state[1] << 2; report.data.leds_bitmap |= sc->led_state[1] << 2;
buf[10] |= sc->led_state[2] << 3; report.data.leds_bitmap |= sc->led_state[2] << 3;
buf[10] |= sc->led_state[3] << 4; report.data.leds_bitmap |= sc->led_state[3] << 4;
hid_hw_raw_request(sc->hdev, 0x01, buf, sizeof(buf), HID_OUTPUT_REPORT, /* Set flag for all leds off, required for 3rd party INTEC controller */
HID_REQ_SET_REPORT); 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) 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) { if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) {
buf[0] = 0x05; buf[0] = 0x05;
buf[1] = 0x03; buf[1] = 0xFF;
offset = 4; offset = 4;
} else { } else {
buf[0] = 0x11; buf[0] = 0x11;
@ -1295,9 +1470,18 @@ static void dualshock4_state_worker(struct work_struct *work)
offset += 2; offset += 2;
#endif #endif
/* LED 3 is the global control */
if (sc->led_state[3]) {
buf[offset++] = sc->led_state[0]; buf[offset++] = sc->led_state[0];
buf[offset++] = sc->led_state[1]; buf[offset++] = sc->led_state[1];
buf[offset++] = sc->led_state[2]; 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) if (sc->quirks & DUALSHOCK4_CONTROLLER_USB)
hid_hw_output_report(hdev, buf, 32); hid_hw_output_report(hdev, buf, 32);
@ -1323,9 +1507,9 @@ static int sony_play_effect(struct input_dev *dev, void *data,
return 0; 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 hid_input, list);
struct input_dev *input_dev = hidinput->input; struct input_dev *input_dev = hidinput->input;
@ -1334,7 +1518,7 @@ static int sony_init_ff(struct hid_device *hdev)
} }
#else #else
static int sony_init_ff(struct hid_device *hdev) static int sony_init_ff(struct sony_sc *sc)
{ {
return 0; 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 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; struct hid_device *hdev = sc->hdev;
int ret; int ret;
@ -1395,15 +1577,13 @@ static int sony_battery_probe(struct sony_sc *sc)
*/ */
sc->battery_capacity = 100; sc->battery_capacity = 100;
power_id = (unsigned long)atomic_inc_return(&power_id_seq);
sc->battery.properties = sony_battery_props; sc->battery.properties = sony_battery_props;
sc->battery.num_properties = ARRAY_SIZE(sony_battery_props); sc->battery.num_properties = ARRAY_SIZE(sony_battery_props);
sc->battery.get_property = sony_battery_get_property; sc->battery.get_property = sony_battery_get_property;
sc->battery.type = POWER_SUPPLY_TYPE_BATTERY; sc->battery.type = POWER_SUPPLY_TYPE_BATTERY;
sc->battery.use_for_apm = 0; sc->battery.use_for_apm = 0;
sc->battery.name = kasprintf(GFP_KERNEL, "sony_controller_battery_%lu", sc->battery.name = kasprintf(GFP_KERNEL, "sony_controller_battery_%pMR",
power_id); sc->mac_address);
if (!sc->battery.name) if (!sc->battery.name)
return -ENOMEM; return -ENOMEM;
@ -1578,6 +1758,52 @@ static int sony_check_add(struct sony_sc *sc)
return sony_check_add_dev_list(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) 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; 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) { if (sc->quirks & SIXAXIS_CONTROLLER_USB) {
/* /*
* The Sony Sixaxis does not handle HID Output Reports on the * 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_NO_OUTPUT_REPORTS_ON_INTR_EP;
hdev->quirks |= HID_QUIRK_SKIP_OUTPUT_REPORT_ID; hdev->quirks |= HID_QUIRK_SKIP_OUTPUT_REPORT_ID;
ret = sixaxis_set_operational_usb(hdev); ret = sixaxis_set_operational_usb(hdev);
sc->worker_initialized = 1; sony_init_work(sc, sixaxis_state_worker);
INIT_WORK(&sc->state_worker, sixaxis_state_worker);
} else if (sc->quirks & SIXAXIS_CONTROLLER_BT) { } else if (sc->quirks & SIXAXIS_CONTROLLER_BT) {
/* /*
* The Sixaxis wants output reports sent on the ctrl endpoint * 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; hdev->quirks |= HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP;
ret = sixaxis_set_operational_bt(hdev); ret = sixaxis_set_operational_bt(hdev);
sc->worker_initialized = 1; sony_init_work(sc, sixaxis_state_worker);
INIT_WORK(&sc->state_worker, sixaxis_state_worker);
} else if (sc->quirks & DUALSHOCK4_CONTROLLER) { } else if (sc->quirks & DUALSHOCK4_CONTROLLER) {
if (sc->quirks & DUALSHOCK4_CONTROLLER_BT) { 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) if (ret < 0)
goto err_stop; goto err_stop;
sc->worker_initialized = 1; sony_init_work(sc, dualshock4_state_worker);
INIT_WORK(&sc->state_worker, dualshock4_state_worker);
} else { } else {
ret = 0; ret = 0;
} }
@ -1675,7 +1904,7 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
goto err_stop; goto err_stop;
if (sc->quirks & SONY_LED_SUPPORT) { if (sc->quirks & SONY_LED_SUPPORT) {
ret = sony_leds_init(hdev); ret = sony_leds_init(sc);
if (ret < 0) if (ret < 0)
goto err_stop; 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) { if (sc->quirks & SONY_FF_SUPPORT) {
ret = sony_init_ff(hdev); ret = sony_init_ff(sc);
if (ret < 0) if (ret < 0)
goto err_close; goto err_close;
} }
@ -1704,12 +1933,12 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
hid_hw_close(hdev); hid_hw_close(hdev);
err_stop: err_stop:
if (sc->quirks & SONY_LED_SUPPORT) if (sc->quirks & SONY_LED_SUPPORT)
sony_leds_remove(hdev); sony_leds_remove(sc);
if (sc->quirks & SONY_BATTERY_SUPPORT) if (sc->quirks & SONY_BATTERY_SUPPORT)
sony_battery_remove(sc); sony_battery_remove(sc);
if (sc->worker_initialized) sony_cancel_work_sync(sc);
cancel_work_sync(&sc->state_worker);
sony_remove_dev_list(sc); sony_remove_dev_list(sc);
sony_release_device_id(sc);
hid_hw_stop(hdev); hid_hw_stop(hdev);
return ret; return ret;
} }
@ -1719,18 +1948,19 @@ static void sony_remove(struct hid_device *hdev)
struct sony_sc *sc = hid_get_drvdata(hdev); struct sony_sc *sc = hid_get_drvdata(hdev);
if (sc->quirks & SONY_LED_SUPPORT) if (sc->quirks & SONY_LED_SUPPORT)
sony_leds_remove(hdev); sony_leds_remove(sc);
if (sc->quirks & SONY_BATTERY_SUPPORT) { if (sc->quirks & SONY_BATTERY_SUPPORT) {
hid_hw_close(hdev); hid_hw_close(hdev);
sony_battery_remove(sc); sony_battery_remove(sc);
} }
if (sc->worker_initialized) sony_cancel_work_sync(sc);
cancel_work_sync(&sc->state_worker);
sony_remove_dev_list(sc); sony_remove_dev_list(sc);
sony_release_device_id(sc);
hid_hw_stop(hdev); hid_hw_stop(hdev);
} }
@ -1775,6 +2005,22 @@ static struct hid_driver sony_driver = {
.report_fixup = sony_report_fixup, .report_fixup = sony_report_fixup,
.raw_event = sony_raw_event .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"); MODULE_LICENSE("GPL");

View File

@ -1,7 +1,7 @@
/* /*
* ThingM blink(1) USB RGB LED driver * 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> * Vivien Didelot <vivien.didelot@savoirfairelinux.com>
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
@ -10,244 +10,285 @@
*/ */
#include <linux/hid.h> #include <linux/hid.h>
#include <linux/hidraw.h>
#include <linux/leds.h> #include <linux/leds.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/mutex.h>
#include <linux/workqueue.h>
#include "hid-ids.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) /* Firmware major number of supported devices */
#define blink1_rgb_to_g(rgb) ((rgb & 0x00FF00) >> 8) #define THINGM_MAJOR_MK1 '1'
#define blink1_rgb_to_b(rgb) ((rgb & 0x0000FF) >> 0) #define THINGM_MAJOR_MK2 '2'
/** struct thingm_fwinfo {
* struct blink1_data - blink(1) device specific data char major;
* @hdev: HID device. unsigned numrgb;
* @led_cdev: LED class instance. unsigned first;
* @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;
}; };
static int blink1_send_command(struct blink1_data *data, static const struct thingm_fwinfo thingm_fwinfo[] = {
u8 buf[BLINK1_CMD_SIZE]) {
.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; 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[0], buf[1], buf[2], buf[3], buf[4],
buf[5], buf[6], buf[7], buf[8]); buf[5], buf[6], buf[7], buf[8]);
ret = hid_hw_raw_request(data->hdev, buf[0], buf, BLINK1_CMD_SIZE, ret = hid_hw_raw_request(tdev->hdev, buf[0], buf, REPORT_SIZE,
HID_FEATURE_REPORT, HID_REQ_SET_REPORT); HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
return ret < 0 ? ret : 0; 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) { ret = hid_hw_raw_request(tdev->hdev, buf[0], buf, REPORT_SIZE,
unsigned int coef = DIV_ROUND_CLOSEST(255, data->brightness); HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
if (ret < 0)
return ret;
buf[2] = DIV_ROUND_CLOSEST(blink1_rgb_to_r(data->rgb), coef); hid_dbg(tdev->hdev, "<- %d %c %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx\n",
buf[3] = DIV_ROUND_CLOSEST(blink1_rgb_to_g(data->rgb), coef); buf[0], buf[1], buf[2], buf[3], buf[4],
buf[4] = DIV_ROUND_CLOSEST(blink1_rgb_to_b(data->rgb), coef); buf[5], buf[6], buf[7], buf[8]);
return 0;
} }
if (data->fade) { static int thingm_version(struct thingm_device *tdev)
buf[1] = 'c'; {
buf[5] = (data->fade & 0xFF00) >> 8; u8 buf[REPORT_SIZE] = { REPORT_ID, 'v', 0, 0, 0, 0, 0, 0, 0 };
buf[6] = (data->fade & 0x00FF); 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;
} }
return blink1_send_command(data, buf); 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 blink1_led_set(struct led_classdev *led_cdev, 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) 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; /* the ledclass has already stored the brightness value */
if (blink1_update_color(data)) schedule_work(&led->rgb->work);
hid_err(data->hdev, "failed to update color\n");
} }
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, err = led_classdev_register(&rgb->tdev->hdev->dev, &rgb->red.ldev);
struct device_attribute *attr, char *buf) if (err)
{ return err;
struct blink1_data *data = dev_get_drvdata(dev->parent);
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, err = led_classdev_register(&rgb->tdev->hdev->dev, &rgb->green.ldev);
struct device_attribute *attr, const char *buf, size_t count) if (err)
{ goto unregister_red;
struct blink1_data *data = dev_get_drvdata(dev->parent);
long unsigned int rgb;
int ret;
ret = kstrtoul(buf, 16, &rgb); /* Register the blue diode */
if (ret) snprintf(rgb->blue.name, sizeof(rgb->blue.name),
return ret; "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 */ err = led_classdev_register(&rgb->tdev->hdev->dev, &rgb->blue.ldev);
if (rgb > 0xFFFFFF) if (err)
return -EINVAL; goto unregister_green;
data->rgb = rgb; INIT_WORK(&rgb->work, thingm_work);
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;
return 0; return 0;
remove_led: unregister_green:
led_classdev_unregister(led); 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: stop:
hid_hw_stop(hdev); hid_hw_stop(hdev);
error: error:
return ret; return err;
} }
static void thingm_remove(struct hid_device *hdev) static void thingm_remove(struct hid_device *hdev)
{ {
struct blink1_data *data = hid_get_drvdata(hdev); struct thingm_device *tdev = hid_get_drvdata(hdev);
struct led_classdev *led = &data->led_cdev; 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); hid_hw_stop(hdev);
} }

View File

@ -807,34 +807,18 @@ static int i2c_hid_fetch_hid_descriptor(struct i2c_hid *ihid)
unsigned int dsize; unsigned int dsize;
int ret; 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 0-1 -> length
* bytes 2-3 -> bcdVersion (has to be 1.00) */ * 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 */ /* check bcdVersion == 1.0 */
if (le16_to_cpu(hdesc->bcdVersion) != 0x0100) { if (le16_to_cpu(hdesc->bcdVersion) != 0x0100) {
dev_err(&client->dev, dev_err(&client->dev,
@ -843,17 +827,14 @@ static int i2c_hid_fetch_hid_descriptor(struct i2c_hid *ihid)
return -ENODEV; return -ENODEV;
} }
i2c_hid_dbg(ihid, "Fetching the HID descriptor\n"); /* Descriptor length should be 30 bytes as per the specification */
dsize = le16_to_cpu(hdesc->wHIDDescLength);
ret = i2c_hid_command(client, &hid_descr_cmd, ihid->hdesc_buffer, if (dsize != sizeof(struct i2c_hid_desc)) {
dev_err(&client->dev, "weird size of HID descriptor (%u)\n",
dsize); dsize);
if (ret) {
dev_err(&client->dev, "hid_descr_cmd Fail\n");
return -ENODEV; return -ENODEV;
} }
i2c_hid_dbg(ihid, "HID Descriptor: %*ph\n", dsize, ihid->hdesc_buffer); i2c_hid_dbg(ihid, "HID Descriptor: %*ph\n", dsize, ihid->hdesc_buffer);
return 0; return 0;
} }

View File

@ -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) if (uhid->rd_size <= 0 || uhid->rd_size > HID_MAX_DESCRIPTOR_SIZE)
return -EINVAL; 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) if (!uhid->rd_data)
return -ENOMEM; return -ENOMEM;
memcpy(uhid->rd_data, ev->u.create2.rd_data, uhid->rd_size);
hid = hid_allocate_device(); hid = hid_allocate_device();
if (IS_ERR(hid)) { if (IS_ERR(hid)) {
ret = PTR_ERR(hid); ret = PTR_ERR(hid);

View File

@ -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_MOUSEPEN_I608X, HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_M610X, HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_M610X, HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_DUOSENSE, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_DUOSENSE, HID_QUIRK_NO_INIT_REPORTS },
{ 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_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_LTS2, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_HD, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_HD, HID_QUIRK_NO_INIT_REPORTS },

View File

@ -479,6 +479,8 @@ config LEDS_OT200
This option enables support for the LEDs on the Bachmann OT200. This option enables support for the LEDs on the Bachmann OT200.
Say Y to enable 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 config LEDS_BLINKM
tristate "LED support for the BlinkM I2C RGB LED" tristate "LED support for the BlinkM I2C RGB LED"
depends on LEDS_CLASS depends on LEDS_CLASS

View File

@ -233,11 +233,6 @@ struct hid_item {
#define HID_DG_BARRELSWITCH 0x000d0044 #define HID_DG_BARRELSWITCH 0x000d0044
#define HID_DG_ERASER 0x000d0045 #define HID_DG_ERASER 0x000d0045
#define HID_DG_TABLETPICK 0x000d0046 #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_CONFIDENCE 0x000d0047
#define HID_DG_WIDTH 0x000d0048 #define HID_DG_WIDTH 0x000d0048
#define HID_DG_HEIGHT 0x000d0049 #define HID_DG_HEIGHT 0x000d0049
@ -246,6 +241,8 @@ struct hid_item {
#define HID_DG_DEVICEINDEX 0x000d0053 #define HID_DG_DEVICEINDEX 0x000d0053
#define HID_DG_CONTACTCOUNT 0x000d0054 #define HID_DG_CONTACTCOUNT 0x000d0054
#define HID_DG_CONTACTMAX 0x000d0055 #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! * HID report types --- Ouch! HID spec says 1 2 3!
@ -299,12 +296,20 @@ struct hid_item {
/* /*
* HID device groups * 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_GENERIC 0x0001
#define HID_GROUP_MULTITOUCH 0x0002 #define HID_GROUP_MULTITOUCH 0x0002
#define HID_GROUP_SENSOR_HUB 0x0003 #define HID_GROUP_SENSOR_HUB 0x0003
#define HID_GROUP_MULTITOUCH_WIN_8 0x0004 #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 * This is the global environment of the parser. This information is
* persistent for main-items. The global environment can be saved and * 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) .bus = BUS_USB, .vendor = (ven), .product = (prod)
#define HID_BLUETOOTH_DEVICE(ven, prod) \ #define HID_BLUETOOTH_DEVICE(ven, prod) \
.bus = BUS_BLUETOOTH, .vendor = (ven), .product = (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) \ #define HID_REPORT_ID(rep) \
.report_type = (rep) .report_type = (rep)

View File

@ -462,7 +462,10 @@ struct input_keymap_entry {
#define KEY_VIDEO_NEXT 241 /* drive next video source */ #define KEY_VIDEO_NEXT 241 /* drive next video source */
#define KEY_VIDEO_PREV 242 /* drive previous 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_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_DISPLAY_OFF 245 /* display device to off state */
#define KEY_WWAN 246 /* Wireless WAN (LTE, UMTS, GSM, etc.) */ #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_ADDRESSBOOK 0x1ad /* AL Contacts/Address Book */
#define KEY_MESSENGER 0x1ae /* AL Instant Messaging */ #define KEY_MESSENGER 0x1ae /* AL Instant Messaging */
#define KEY_DISPLAYTOGGLE 0x1af /* Turn display (LCD) on and off */ #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_SPELLCHECK 0x1b0 /* AL Spell Check */
#define KEY_LOGOFF 0x1b1 /* AL Logoff */ #define KEY_LOGOFF 0x1b1 /* AL Logoff */
@ -723,6 +727,17 @@ struct input_keymap_entry {
#define KEY_ALS_TOGGLE 0x230 /* Ambient light sensor */ #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_HAPPY 0x2c0
#define BTN_TRIGGER_HAPPY1 0x2c0 #define BTN_TRIGGER_HAPPY1 0x2c0
#define BTN_TRIGGER_HAPPY2 0x2c1 #define BTN_TRIGGER_HAPPY2 0x2c1