Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
linux
Commits
b0eae38c
Commit
b0eae38c
authored
Oct 25, 2011
by
Jiri Kosina
Browse files
Options
Browse Files
Download
Plain Diff
Merge branches 'acrux', 'logitech', 'multitouch', 'roccat' and 'wiimote' into for-linus
parents
3ee72ca9
b55ebc27
e00ddc9b
0db3bfc7
3200a6a5
b22e00f3
Changes
20
Show whitespace changes
Inline
Side-by-side
Showing
20 changed files
with
2474 additions
and
129 deletions
+2474
-129
Documentation/ABI/testing/sysfs-driver-hid-logitech-lg4ff
Documentation/ABI/testing/sysfs-driver-hid-logitech-lg4ff
+7
-0
MAINTAINERS
MAINTAINERS
+6
-0
drivers/hid/Kconfig
drivers/hid/Kconfig
+24
-5
drivers/hid/Makefile
drivers/hid/Makefile
+2
-1
drivers/hid/hid-axff.c
drivers/hid/hid-axff.c
+24
-12
drivers/hid/hid-core.c
drivers/hid/hid-core.c
+11
-0
drivers/hid/hid-ids.h
drivers/hid/hid-ids.h
+9
-0
drivers/hid/hid-input.c
drivers/hid/hid-input.c
+11
-0
drivers/hid/hid-lg.c
drivers/hid/hid-lg.c
+21
-8
drivers/hid/hid-lg.h
drivers/hid/hid-lg.h
+3
-1
drivers/hid/hid-lg4ff.c
drivers/hid/hid-lg4ff.c
+378
-25
drivers/hid/hid-lgff.c
drivers/hid/hid-lgff.c
+0
-13
drivers/hid/hid-logitech-dj.c
drivers/hid/hid-logitech-dj.c
+922
-0
drivers/hid/hid-logitech-dj.h
drivers/hid/hid-logitech-dj.h
+123
-0
drivers/hid/hid-multitouch.c
drivers/hid/hid-multitouch.c
+79
-14
drivers/hid/hid-roccat-kone.c
drivers/hid/hid-roccat-kone.c
+39
-24
drivers/hid/hid-roccat-kovaplus.c
drivers/hid/hid-roccat-kovaplus.c
+15
-2
drivers/hid/hid-roccat-pyra.c
drivers/hid/hid-roccat-pyra.c
+15
-8
drivers/hid/hid-wiimote.c
drivers/hid/hid-wiimote.c
+784
-16
include/linux/hid.h
include/linux/hid.h
+1
-0
No files found.
Documentation/ABI/testing/sysfs-driver-hid-logitech-lg4ff
0 → 100644
View file @
b0eae38c
What: /sys/module/hid_logitech/drivers/hid:logitech/<dev>/range.
Date: July 2011
KernelVersion: 3.2
Contact: Michal Malý <madcatxster@gmail.com>
Description: Display minimum, maximum and current range of the steering
wheel. Writing a value within min and max boundaries sets the
range of the wheel.
MAINTAINERS
View file @
b0eae38c
...
...
@@ -7142,6 +7142,12 @@ L: linux-scsi@vger.kernel.org
S: Maintained
F: drivers/scsi/wd7000.c
WIIMOTE HID DRIVER
M: David Herrmann <dh.herrmann@googlemail.com>
L: linux-input@vger.kernel.org
S: Maintained
F: drivers/hid/hid-wiimote*
WINBOND CIR DRIVER
M: David Härdeman <david@hardeman.nu>
S: Maintained
...
...
drivers/hid/Kconfig
View file @
b0eae38c
...
...
@@ -69,7 +69,7 @@ config HID_ACRUX
Say Y here if you want to enable support for ACRUX game controllers.
config HID_ACRUX_FF
tristate
"ACRUX force feedback support"
bool
"ACRUX force feedback support"
depends on HID_ACRUX
select INPUT_FF_MEMLESS
---help---
...
...
@@ -245,6 +245,15 @@ config HID_LOGITECH
---help---
Support for Logitech devices that are not fully compliant with HID standard.
config HID_LOGITECH_DJ
tristate "Logitech Unifying receivers full support"
depends on HID_LOGITECH
default m
---help---
Say Y if you want support for Logitech Unifying receivers and devices.
Unifying receivers are capable of pairing up to 6 Logitech compliant
devices to the same receiver.
config LOGITECH_FF
bool "Logitech force feedback support"
depends on HID_LOGITECH
...
...
@@ -278,13 +287,21 @@ config LOGIG940_FF
Say Y here if you want to enable force feedback support for Logitech
Flight System G940 devices.
config LOGIW
II
_FF
bool "Logitech
Speed Force Wireless
force feedback support"
config LOGIW
HEELS
_FF
bool "Logitech
wheels configuration and
force feedback support"
depends on HID_LOGITECH
select INPUT_FF_MEMLESS
default LOGITECH_FF
help
Say Y here if you want to enable force feedback support for Logitech
Speed Force Wireless (Wii) devices.
Say Y here if you want to enable force feedback and range setting
support for following Logitech wheels:
- Logitech Driving Force
- Logitech Driving Force Pro
- Logitech Driving Force GT
- Logitech G25
- Logitech G27
- Logitech MOMO/MOMO 2
- Logitech Formula Force EX
config HID_MAGICMOUSE
tristate "Apple MagicMouse multi-touch support"
...
...
@@ -328,6 +345,7 @@ config HID_MULTITOUCH
- Hanvon dual touch panels
- Ilitek dual touch panels
- IrTouch Infrared USB panels
- LG Display panels (Dell ST2220Tc)
- Lumio CrystalTouch panels
- MosArt dual-touch panels
- PenMount dual touch panels
...
...
@@ -590,6 +608,7 @@ config HID_WIIMOTE
tristate "Nintendo Wii Remote support"
depends on BT_HIDP
depends on LEDS_CLASS
select POWER_SUPPLY
---help---
Support for the Nintendo Wii Remote bluetooth device.
...
...
drivers/hid/Makefile
View file @
b0eae38c
...
...
@@ -21,7 +21,7 @@ endif
ifdef
CONFIG_LOGIG940_FF
hid-logitech-y
+=
hid-lg3ff.o
endif
ifdef
CONFIG_LOGIW
II
_FF
ifdef
CONFIG_LOGIW
HEELS
_FF
hid-logitech-y
+=
hid-lg4ff.o
endif
...
...
@@ -43,6 +43,7 @@ obj-$(CONFIG_HID_KEYTOUCH) += hid-keytouch.o
obj-$(CONFIG_HID_KYE)
+=
hid-kye.o
obj-$(CONFIG_HID_LCPOWER)
+=
hid-lcpower.o
obj-$(CONFIG_HID_LOGITECH)
+=
hid-logitech.o
obj-$(CONFIG_HID_LOGITECH_DJ)
+=
hid-logitech-dj.o
obj-$(CONFIG_HID_MAGICMOUSE)
+=
hid-magicmouse.o
obj-$(CONFIG_HID_MICROSOFT)
+=
hid-microsoft.o
obj-$(CONFIG_HID_MONTEREY)
+=
hid-monterey.o
...
...
drivers/hid/hid-axff.c
View file @
b0eae38c
...
...
@@ -6,7 +6,7 @@
* Xbox 360 controller.
*
* 1a34:0802 "ACRUX USB GAMEPAD 8116"
* - tested with a EXEQ EQ-PCU-02090 game controller.
* - tested with a
n
EXEQ EQ-PCU-02090 game controller.
*
* Copyright (c) 2010 Sergei Kolzun <x0r@dv-life.ru>
*/
...
...
@@ -45,7 +45,10 @@ static int axff_play(struct input_dev *dev, void *data, struct ff_effect *effect
{
struct
hid_device
*
hid
=
input_get_drvdata
(
dev
);
struct
axff_device
*
axff
=
data
;
struct
hid_report
*
report
=
axff
->
report
;
int
field_count
=
0
;
int
left
,
right
;
int
i
,
j
;
left
=
effect
->
u
.
rumble
.
strong_magnitude
;
right
=
effect
->
u
.
rumble
.
weak_magnitude
;
...
...
@@ -55,10 +58,14 @@ static int axff_play(struct input_dev *dev, void *data, struct ff_effect *effect
left
=
left
*
0xff
/
0xffff
;
right
=
right
*
0xff
/
0xffff
;
axff
->
report
->
field
[
0
]
->
value
[
0
]
=
left
;
axff
->
report
->
field
[
1
]
->
value
[
0
]
=
right
;
axff
->
report
->
field
[
2
]
->
value
[
0
]
=
left
;
axff
->
report
->
field
[
3
]
->
value
[
0
]
=
right
;
for
(
i
=
0
;
i
<
report
->
maxfield
;
i
++
)
{
for
(
j
=
0
;
j
<
report
->
field
[
i
]
->
report_count
;
j
++
)
{
report
->
field
[
i
]
->
value
[
j
]
=
field_count
%
2
?
right
:
left
;
field_count
++
;
}
}
dbg_hid
(
"running with 0x%02x 0x%02x"
,
left
,
right
);
usbhid_submit_report
(
hid
,
axff
->
report
,
USB_DIR_OUT
);
...
...
@@ -72,6 +79,8 @@ static int axff_init(struct hid_device *hid)
struct
hid_input
*
hidinput
=
list_first_entry
(
&
hid
->
inputs
,
struct
hid_input
,
list
);
struct
list_head
*
report_list
=&
hid
->
report_enum
[
HID_OUTPUT_REPORT
].
report_list
;
struct
input_dev
*
dev
=
hidinput
->
input
;
int
field_count
=
0
;
int
i
,
j
;
int
error
;
if
(
list_empty
(
report_list
))
{
...
...
@@ -80,9 +89,16 @@ static int axff_init(struct hid_device *hid)
}
report
=
list_first_entry
(
report_list
,
struct
hid_report
,
list
);
for
(
i
=
0
;
i
<
report
->
maxfield
;
i
++
)
{
for
(
j
=
0
;
j
<
report
->
field
[
i
]
->
report_count
;
j
++
)
{
report
->
field
[
i
]
->
value
[
j
]
=
0x00
;
field_count
++
;
}
}
if
(
report
->
maxfield
<
4
)
{
hid_err
(
hid
,
"no fields in the report: %d
\n
"
,
report
->
maxfield
);
if
(
field_count
<
4
)
{
hid_err
(
hid
,
"not enough fields in the report: %d
\n
"
,
field_count
);
return
-
ENODEV
;
}
...
...
@@ -97,13 +113,9 @@ static int axff_init(struct hid_device *hid)
goto
err_free_mem
;
axff
->
report
=
report
;
axff
->
report
->
field
[
0
]
->
value
[
0
]
=
0x00
;
axff
->
report
->
field
[
1
]
->
value
[
0
]
=
0x00
;
axff
->
report
->
field
[
2
]
->
value
[
0
]
=
0x00
;
axff
->
report
->
field
[
3
]
->
value
[
0
]
=
0x00
;
usbhid_submit_report
(
hid
,
axff
->
report
,
USB_DIR_OUT
);
hid_info
(
hid
,
"Force Feedback for ACRUX game controllers by Sergei Kolzun<x0r@dv-life.ru>
\n
"
);
hid_info
(
hid
,
"Force Feedback for ACRUX game controllers by Sergei Kolzun
<x0r@dv-life.ru>
\n
"
);
return
0
;
...
...
drivers/hid/hid-core.c
View file @
b0eae38c
...
...
@@ -1212,6 +1212,12 @@ int hid_connect(struct hid_device *hdev, unsigned int connect_mask)
if
((
connect_mask
&
HID_CONNECT_HIDINPUT
)
&&
!
hidinput_connect
(
hdev
,
connect_mask
&
HID_CONNECT_HIDINPUT_FORCE
))
hdev
->
claimed
|=
HID_CLAIMED_INPUT
;
if
(
hdev
->
quirks
&
HID_QUIRK_MULTITOUCH
)
{
/* this device should be handled by hid-multitouch, skip it */
hdev
->
quirks
&=
~
HID_QUIRK_MULTITOUCH
;
return
-
ENODEV
;
}
if
((
connect_mask
&
HID_CONNECT_HIDDEV
)
&&
hdev
->
hiddev_connect
&&
!
hdev
->
hiddev_connect
(
hdev
,
connect_mask
&
HID_CONNECT_HIDDEV_FORCE
))
...
...
@@ -1391,6 +1397,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
{
HID_USB_DEVICE
(
USB_VENDOR_ID_GYRATION
,
USB_DEVICE_ID_GYRATION_REMOTE_2
)
},
{
HID_USB_DEVICE
(
USB_VENDOR_ID_GYRATION
,
USB_DEVICE_ID_GYRATION_REMOTE_3
)
},
{
HID_USB_DEVICE
(
USB_VENDOR_ID_HANVON
,
USB_DEVICE_ID_HANVON_MULTITOUCH
)
},
{
HID_USB_DEVICE
(
USB_VENDOR_ID_IDEACOM
,
USB_DEVICE_ID_IDEACOM_IDC6650
)
},
{
HID_USB_DEVICE
(
USB_VENDOR_ID_HOLTEK
,
USB_DEVICE_ID_HOLTEK_ON_LINE_GRIP
)
},
{
HID_USB_DEVICE
(
USB_VENDOR_ID_ILITEK
,
USB_DEVICE_ID_ILITEK_MULTITOUCH
)
},
{
HID_USB_DEVICE
(
USB_VENDOR_ID_IRTOUCHSYSTEMS
,
USB_DEVICE_ID_IRTOUCH_INFRARED_USB
)
},
...
...
@@ -1399,6 +1406,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
{
HID_USB_DEVICE
(
USB_VENDOR_ID_KYE
,
USB_DEVICE_ID_KYE_ERGO_525V
)
},
{
HID_USB_DEVICE
(
USB_VENDOR_ID_LABTEC
,
USB_DEVICE_ID_LABTEC_WIRELESS_KEYBOARD
)
},
{
HID_USB_DEVICE
(
USB_VENDOR_ID_LCPOWER
,
USB_DEVICE_ID_LCPOWER_LC1000
)
},
{
HID_USB_DEVICE
(
USB_VENDOR_ID_LG
,
USB_DEVICE_ID_LG_MULTITOUCH
)
},
{
HID_USB_DEVICE
(
USB_VENDOR_ID_LOGITECH
,
USB_DEVICE_ID_MX3000_RECEIVER
)
},
{
HID_USB_DEVICE
(
USB_VENDOR_ID_LOGITECH
,
USB_DEVICE_ID_S510_RECEIVER
)
},
{
HID_USB_DEVICE
(
USB_VENDOR_ID_LOGITECH
,
USB_DEVICE_ID_S510_RECEIVER_2
)
},
...
...
@@ -1420,8 +1428,11 @@ static const struct hid_device_id hid_have_special_driver[] = {
{
HID_USB_DEVICE
(
USB_VENDOR_ID_LOGITECH
,
USB_DEVICE_ID_LOGITECH_MOMO_WHEEL
)
},
{
HID_USB_DEVICE
(
USB_VENDOR_ID_LOGITECH
,
USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2
)
},
{
HID_USB_DEVICE
(
USB_VENDOR_ID_LOGITECH
,
USB_DEVICE_ID_LOGITECH_DFP_WHEEL
)
},
{
HID_USB_DEVICE
(
USB_VENDOR_ID_LOGITECH
,
USB_DEVICE_ID_LOGITECH_DFGT_WHEEL
)
},
{
HID_USB_DEVICE
(
USB_VENDOR_ID_LOGITECH
,
USB_DEVICE_ID_LOGITECH_G25_WHEEL
)
},
{
HID_USB_DEVICE
(
USB_VENDOR_ID_LOGITECH
,
USB_DEVICE_ID_LOGITECH_G27_WHEEL
)
},
{
HID_USB_DEVICE
(
USB_VENDOR_ID_LOGITECH
,
USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER
)
},
{
HID_USB_DEVICE
(
USB_VENDOR_ID_LOGITECH
,
USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER_2
)
},
{
HID_USB_DEVICE
(
USB_VENDOR_ID_LOGITECH
,
USB_DEVICE_ID_LOGITECH_WII_WHEEL
)
},
{
HID_USB_DEVICE
(
USB_VENDOR_ID_LOGITECH
,
USB_DEVICE_ID_LOGITECH_RUMBLEPAD2
)
},
{
HID_USB_DEVICE
(
USB_VENDOR_ID_LOGITECH
,
USB_DEVICE_ID_SPACETRAVELLER
)
},
...
...
drivers/hid/hid-ids.h
View file @
b0eae38c
...
...
@@ -351,6 +351,9 @@
#define USB_DEVICE_ID_UGCI_FLYING 0x0020
#define USB_DEVICE_ID_UGCI_FIGHTING 0x0030
#define USB_VENDOR_ID_IDEACOM 0x1cb6
#define USB_DEVICE_ID_IDEACOM_IDC6650 0x6650
#define USB_VENDOR_ID_ILITEK 0x222a
#define USB_DEVICE_ID_ILITEK_MULTITOUCH 0x0001
...
...
@@ -423,6 +426,9 @@
#define USB_DEVICE_ID_LD_HYBRID 0x2090
#define USB_DEVICE_ID_LD_HEATCONTROL 0x20A0
#define USB_VENDOR_ID_LG 0x1fd2
#define USB_DEVICE_ID_LG_MULTITOUCH 0x0064
#define USB_VENDOR_ID_LOGITECH 0x046d
#define USB_DEVICE_ID_LOGITECH_RECEIVER 0xc101
#define USB_DEVICE_ID_LOGITECH_HARMONY_FIRST 0xc110
...
...
@@ -440,6 +446,7 @@
#define USB_DEVICE_ID_LOGITECH_MOMO_WHEEL 0xc295
#define USB_DEVICE_ID_LOGITECH_DFP_WHEEL 0xc298
#define USB_DEVICE_ID_LOGITECH_G25_WHEEL 0xc299
#define USB_DEVICE_ID_LOGITECH_DFGT_WHEEL 0xc29a
#define USB_DEVICE_ID_LOGITECH_G27_WHEEL 0xc29b
#define USB_DEVICE_ID_LOGITECH_WII_WHEEL 0xc29c
#define USB_DEVICE_ID_LOGITECH_ELITE_KBD 0xc30a
...
...
@@ -447,6 +454,8 @@
#define USB_DEVICE_ID_S510_RECEIVER_2 0xc517
#define USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500 0xc512
#define USB_DEVICE_ID_MX3000_RECEIVER 0xc513
#define USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER 0xc52b
#define USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER_2 0xc532
#define USB_DEVICE_ID_SPACETRAVELLER 0xc623
#define USB_DEVICE_ID_SPACENAVIGATOR 0xc626
#define USB_DEVICE_ID_DINOVO_DESKTOP 0xc704
...
...
drivers/hid/hid-input.c
View file @
b0eae38c
...
...
@@ -474,6 +474,10 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
map_key_clear
(
BTN_STYLUS2
);
break
;
case
0x51
:
/* ContactID */
device
->
quirks
|=
HID_QUIRK_MULTITOUCH
;
goto
unknown
;
default:
goto
unknown
;
}
break
;
...
...
@@ -978,6 +982,13 @@ int hidinput_connect(struct hid_device *hid, unsigned int force)
}
}
if
(
hid
->
quirks
&
HID_QUIRK_MULTITOUCH
)
{
/* generic hid does not know how to handle multitouch devices */
if
(
hidinput
)
goto
out_cleanup
;
goto
out_unwind
;
}
if
(
hidinput
&&
input_register_device
(
hidinput
->
input
))
goto
out_cleanup
;
...
...
drivers/hid/hid-lg.c
View file @
b0eae38c
...
...
@@ -363,7 +363,7 @@ static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id)
goto
err_free
;
}
if
(
quirks
&
(
LG_FF
|
LG_FF2
|
LG_FF3
))
if
(
quirks
&
(
LG_FF
|
LG_FF2
|
LG_FF3
|
LG_FF4
))
connect_mask
&=
~
HID_CONNECT_FF
;
ret
=
hid_hw_start
(
hdev
,
connect_mask
);
...
...
@@ -372,7 +372,8 @@ static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id)
goto
err_free
;
}
if
(
quirks
&
LG_FF4
)
{
/* Setup wireless link with Logitech Wii wheel */
if
(
hdev
->
product
==
USB_DEVICE_ID_LOGITECH_WII_WHEEL
)
{
unsigned
char
buf
[]
=
{
0x00
,
0xAF
,
0x01
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
};
ret
=
hdev
->
hid_output_raw_report
(
hdev
,
buf
,
sizeof
(
buf
),
HID_FEATURE_REPORT
);
...
...
@@ -405,6 +406,15 @@ static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id)
return
ret
;
}
static
void
lg_remove
(
struct
hid_device
*
hdev
)
{
unsigned
long
quirks
=
(
unsigned
long
)
hid_get_drvdata
(
hdev
);
if
(
quirks
&
LG_FF4
)
lg4ff_deinit
(
hdev
);
hid_hw_stop
(
hdev
);
}
static
const
struct
hid_device_id
lg_devices
[]
=
{
{
HID_USB_DEVICE
(
USB_VENDOR_ID_LOGITECH
,
USB_DEVICE_ID_MX3000_RECEIVER
),
.
driver_data
=
LG_RDESC
|
LG_WIRELESS
},
...
...
@@ -431,7 +441,7 @@ static const struct hid_device_id lg_devices[] = {
{
HID_USB_DEVICE
(
USB_VENDOR_ID_LOGITECH
,
USB_DEVICE_ID_LOGITECH_EXTREME_3D
),
.
driver_data
=
LG_NOGET
},
{
HID_USB_DEVICE
(
USB_VENDOR_ID_LOGITECH
,
USB_DEVICE_ID_LOGITECH_WHEEL
),
.
driver_data
=
LG_NOGET
|
LG_FF
},
.
driver_data
=
LG_NOGET
|
LG_FF
4
},
{
HID_USB_DEVICE
(
USB_VENDOR_ID_LOGITECH
,
USB_DEVICE_ID_LOGITECH_RUMBLEPAD_CORD
),
.
driver_data
=
LG_FF2
},
...
...
@@ -444,15 +454,17 @@ static const struct hid_device_id lg_devices[] = {
{
HID_USB_DEVICE
(
USB_VENDOR_ID_LOGITECH
,
USB_DEVICE_ID_LOGITECH_FORCE3D_PRO
),
.
driver_data
=
LG_FF
},
{
HID_USB_DEVICE
(
USB_VENDOR_ID_LOGITECH
,
USB_DEVICE_ID_LOGITECH_MOMO_WHEEL
),
.
driver_data
=
LG_FF
},
.
driver_data
=
LG_FF
4
},
{
HID_USB_DEVICE
(
USB_VENDOR_ID_LOGITECH
,
USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2
),
.
driver_data
=
LG_FF
},
.
driver_data
=
LG_FF
4
},
{
HID_USB_DEVICE
(
USB_VENDOR_ID_LOGITECH
,
USB_DEVICE_ID_LOGITECH_G25_WHEEL
),
.
driver_data
=
LG_FF
},
.
driver_data
=
LG_FF4
},
{
HID_USB_DEVICE
(
USB_VENDOR_ID_LOGITECH
,
USB_DEVICE_ID_LOGITECH_DFGT_WHEEL
),
.
driver_data
=
LG_FF4
},
{
HID_USB_DEVICE
(
USB_VENDOR_ID_LOGITECH
,
USB_DEVICE_ID_LOGITECH_G27_WHEEL
),
.
driver_data
=
LG_FF
},
.
driver_data
=
LG_FF
4
},
{
HID_USB_DEVICE
(
USB_VENDOR_ID_LOGITECH
,
USB_DEVICE_ID_LOGITECH_DFP_WHEEL
),
.
driver_data
=
LG_NOGET
|
LG_FF
},
.
driver_data
=
LG_NOGET
|
LG_FF
4
},
{
HID_USB_DEVICE
(
USB_VENDOR_ID_LOGITECH
,
USB_DEVICE_ID_LOGITECH_WII_WHEEL
),
.
driver_data
=
LG_FF4
},
{
HID_USB_DEVICE
(
USB_VENDOR_ID_LOGITECH
,
USB_DEVICE_ID_LOGITECH_WINGMAN_FFG
),
...
...
@@ -478,6 +490,7 @@ static struct hid_driver lg_driver = {
.
input_mapped
=
lg_input_mapped
,
.
event
=
lg_event
,
.
probe
=
lg_probe
,
.
remove
=
lg_remove
,
};
static
int
__init
lg_init
(
void
)
...
...
drivers/hid/hid-lg.h
View file @
b0eae38c
...
...
@@ -19,10 +19,12 @@ int lg3ff_init(struct hid_device *hdev);
static
inline
int
lg3ff_init
(
struct
hid_device
*
hdev
)
{
return
-
1
;
}
#endif
#ifdef CONFIG_LOGIW
II
_FF
#ifdef CONFIG_LOGIW
HEELS
_FF
int
lg4ff_init
(
struct
hid_device
*
hdev
);
int
lg4ff_deinit
(
struct
hid_device
*
hdev
);
#else
static
inline
int
lg4ff_init
(
struct
hid_device
*
hdev
)
{
return
-
1
;
}
static
inline
int
lg4ff_deinit
(
struct
hid_device
*
hdev
)
{
return
-
1
;
}
#endif
#endif
drivers/hid/hid-lg4ff.c
View file @
b0eae38c
...
...
@@ -29,19 +29,108 @@
#include "usbhid/usbhid.h"
#include "hid-lg.h"
#include "hid-ids.h"
struct
lg4ff_device
{
struct
hid_report
*
report
;
#define DFGT_REV_MAJ 0x13
#define DFGT_REV_MIN 0x22
#define DFP_REV_MAJ 0x11
#define DFP_REV_MIN 0x06
#define FFEX_REV_MAJ 0x21
#define FFEX_REV_MIN 0x00
#define G25_REV_MAJ 0x12
#define G25_REV_MIN 0x22
#define G27_REV_MAJ 0x12
#define G27_REV_MIN 0x38
#define to_hid_device(pdev) container_of(pdev, struct hid_device, dev)
static
void
hid_lg4ff_set_range_dfp
(
struct
hid_device
*
hid
,
u16
range
);
static
void
hid_lg4ff_set_range_g25
(
struct
hid_device
*
hid
,
u16
range
);
static
ssize_t
lg4ff_range_show
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
char
*
buf
);
static
ssize_t
lg4ff_range_store
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
const
char
*
buf
,
size_t
count
);
static
DEVICE_ATTR
(
range
,
S_IRWXU
|
S_IRWXG
|
S_IRWXO
,
lg4ff_range_show
,
lg4ff_range_store
);
static
bool
list_inited
;
struct
lg4ff_device_entry
{
char
*
device_id
;
/* Use name in respective kobject structure's address as the ID */
__u16
range
;
__u16
min_range
;
__u16
max_range
;
__u8
leds
;
struct
list_head
list
;
void
(
*
set_range
)(
struct
hid_device
*
hid
,
u16
range
);
};
static
const
signed
short
ff4_wheel_ac
[]
=
{
static
struct
lg4ff_device_entry
device_list
;
static
const
signed
short
lg4ff_wheel_effects
[]
=
{
FF_CONSTANT
,
FF_AUTOCENTER
,
-
1
};
static
int
hid_lg4ff_play
(
struct
input_dev
*
dev
,
void
*
data
,
struct
ff_effect
*
effect
)
struct
lg4ff_wheel
{
const
__u32
product_id
;
const
signed
short
*
ff_effects
;
const
__u16
min_range
;
const
__u16
max_range
;
void
(
*
set_range
)(
struct
hid_device
*
hid
,
u16
range
);
};
static
const
struct
lg4ff_wheel
lg4ff_devices
[]
=
{
{
USB_DEVICE_ID_LOGITECH_WHEEL
,
lg4ff_wheel_effects
,
40
,
270
,
NULL
},
{
USB_DEVICE_ID_LOGITECH_MOMO_WHEEL
,
lg4ff_wheel_effects
,
40
,
270
,
NULL
},
{
USB_DEVICE_ID_LOGITECH_DFP_WHEEL
,
lg4ff_wheel_effects
,
40
,
900
,
hid_lg4ff_set_range_dfp
},
{
USB_DEVICE_ID_LOGITECH_G25_WHEEL
,
lg4ff_wheel_effects
,
40
,
900
,
hid_lg4ff_set_range_g25
},
{
USB_DEVICE_ID_LOGITECH_DFGT_WHEEL
,
lg4ff_wheel_effects
,
40
,
900
,
hid_lg4ff_set_range_g25
},
{
USB_DEVICE_ID_LOGITECH_G27_WHEEL
,
lg4ff_wheel_effects
,
40
,
900
,
hid_lg4ff_set_range_g25
},
{
USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2
,
lg4ff_wheel_effects
,
40
,
270
,
NULL
},
{
USB_DEVICE_ID_LOGITECH_WII_WHEEL
,
lg4ff_wheel_effects
,
40
,
270
,
NULL
}
};
struct
lg4ff_native_cmd
{
const
__u8
cmd_num
;
/* Number of commands to send */
const
__u8
cmd
[];
};
struct
lg4ff_usb_revision
{
const
__u16
rev_maj
;
const
__u16
rev_min
;
const
struct
lg4ff_native_cmd
*
command
;
};
static
const
struct
lg4ff_native_cmd
native_dfp
=
{
1
,
{
0xf8
,
0x01
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
}
};
static
const
struct
lg4ff_native_cmd
native_dfgt
=
{
2
,
{
0xf8
,
0x0a
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
/* 1st command */
0xf8
,
0x09
,
0x03
,
0x01
,
0x00
,
0x00
,
0x00
}
/* 2nd command */
};
static
const
struct
lg4ff_native_cmd
native_g25
=
{
1
,
{
0xf8
,
0x10
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
}
};
static
const
struct
lg4ff_native_cmd
native_g27
=
{
2
,
{
0xf8
,
0x0a
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
/* 1st command */
0xf8
,
0x09
,
0x04
,
0x01
,
0x00
,
0x00
,
0x00
}
/* 2nd command */
};
static
const
struct
lg4ff_usb_revision
lg4ff_revs
[]
=
{
{
DFGT_REV_MAJ
,
DFGT_REV_MIN
,
&
native_dfgt
},
/* Driving Force GT */
{
DFP_REV_MAJ
,
DFP_REV_MIN
,
&
native_dfp
},
/* Driving Force Pro */
{
G25_REV_MAJ
,
G25_REV_MIN
,
&
native_g25
},
/* G25 */
{
G27_REV_MAJ
,
G27_REV_MIN
,
&
native_g27
},
/* G27 */
};
static
int
hid_lg4ff_play
(
struct
input_dev
*
dev
,
void
*
data
,
struct
ff_effect
*
effect
)
{
struct
hid_device
*
hid
=
input_get_drvdata
(
dev
);
struct
list_head
*
report_list
=
&
hid
->
report_enum
[
HID_OUTPUT_REPORT
].
report_list
;
...
...
@@ -55,13 +144,12 @@ static int hid_lg4ff_play(struct input_dev *dev, void *data,
x
=
effect
->
u
.
ramp
.
start_level
+
0x80
;
/* 0x80 is no force */
CLAMP
(
x
);
report
->
field
[
0
]
->
value
[
0
]
=
0x11
;
/* Slot 1 */
report
->
field
[
0
]
->
value
[
1
]
=
0x
10
;
report
->
field
[
0
]
->
value
[
1
]
=
0x
08
;
report
->
field
[
0
]
->
value
[
2
]
=
x
;
report
->
field
[
0
]
->
value
[
3
]
=
0x
0
0
;
report
->
field
[
0
]
->
value
[
3
]
=
0x
8
0
;
report
->
field
[
0
]
->
value
[
4
]
=
0x00
;
report
->
field
[
0
]
->
value
[
5
]
=
0x0
8
;
report
->
field
[
0
]
->
value
[
5
]
=
0x0
0
;
report
->
field
[
0
]
->
value
[
6
]
=
0x00
;
dbg_hid
(
"Autocenter, x=0x%02X
\n
"
,
x
);
usbhid_submit_report
(
hid
,
report
,
USB_DIR_OUT
);
break
;
...
...
@@ -69,24 +157,184 @@ static int hid_lg4ff_play(struct input_dev *dev, void *data,
return
0
;
}
static
void
hid_lg4ff_set_autocenter
(
struct
input_dev
*
dev
,
u16
magnitude
)
/* Sends default autocentering command compatible with
* all wheels except Formula Force EX */
static
void
hid_lg4ff_set_autocenter_default
(
struct
input_dev
*
dev
,
u16
magnitude
)
{
struct
hid_device
*
hid
=
input_get_drvdata
(
dev
);
struct
list_head
*
report_list
=
&
hid
->
report_enum
[
HID_OUTPUT_REPORT
].
report_list
;
struct
hid_report
*
report
=
list_entry
(
report_list
->
next
,
struct
hid_report
,
list
);
report
->
field
[
0
]
->
value
[
0
]
=
0xfe
;
report
->
field
[
0
]
->
value
[
1
]
=
0x0d
;
report
->
field
[
0
]
->
value
[
2
]
=
magnitude
>>
13
;
report
->
field
[
0
]
->
value
[
3
]
=
magnitude
>>
13
;
report
->
field
[
0
]
->
value
[
4
]
=
magnitude
>>
8
;
report
->
field
[
0
]
->
value
[
5
]
=
0x00
;
report
->
field
[
0
]
->
value
[
6
]
=
0x00
;
usbhid_submit_report
(
hid
,
report
,
USB_DIR_OUT
);
}
/* Sends autocentering command compatible with Formula Force EX */
static
void
hid_lg4ff_set_autocenter_ffex
(
struct
input_dev
*
dev
,
u16
magnitude
)
{
struct
hid_device
*
hid
=
input_get_drvdata
(
dev
);
struct
list_head
*
report_list
=
&
hid
->
report_enum
[
HID_OUTPUT_REPORT
].
report_list
;
struct
hid_report
*
report
=
list_entry
(
report_list
->
next
,
struct
hid_report
,
list
);
__s32
*
value
=
report
->
field
[
0
]
->
value
;
magnitude
=
magnitude
*
90
/
65535
;
*
value
++
=
0xfe
;
*
value
++
=
0x0d
;
*
value
++
=
0x07
;
*
value
++
=
0x07
;
*
value
++
=
(
magnitude
>>
8
)
&
0xff
;
*
value
++
=
0x00
;
*
value
=
0x00
;
report
->
field
[
0
]
->
value
[
0
]
=
0xfe
;
report
->
field
[
0
]
->
value
[
1
]
=
0x03
;
report
->
field
[
0
]
->
value
[
2
]
=
magnitude
>>
14
;
report
->
field
[
0
]
->
value
[
3
]
=
magnitude
>>
14
;
report
->
field
[
0
]
->
value
[
4
]
=
magnitude
;
report
->
field
[
0
]
->
value
[
5
]
=
0x00
;
report
->
field
[
0
]
->
value
[
6
]
=
0x00
;
usbhid_submit_report
(
hid
,
report
,
USB_DIR_OUT
);
}
/* Sends command to set range compatible with G25/G27/Driving Force GT */
static
void
hid_lg4ff_set_range_g25
(
struct
hid_device
*
hid
,
u16
range
)
{
struct
list_head
*
report_list
=
&
hid
->
report_enum
[
HID_OUTPUT_REPORT
].
report_list
;
struct
hid_report
*
report
=
list_entry
(
report_list
->
next
,
struct
hid_report
,
list
);
dbg_hid
(
"G25/G27/DFGT: setting range to %u
\n
"
,
range
);
report
->
field
[
0
]
->
value
[
0
]
=
0xf8
;
report
->
field
[
0
]
->
value
[
1
]
=
0x81
;
report
->
field
[
0
]
->
value
[
2
]
=
range
&
0x00ff
;
report
->
field
[
0
]
->
value
[
3
]
=
(
range
&
0xff00
)
>>
8
;
report
->
field
[
0
]
->
value
[
4
]
=
0x00
;
report
->
field
[
0
]
->
value
[
5
]
=
0x00
;
report
->
field
[
0
]
->
value
[
6
]
=
0x00
;
usbhid_submit_report
(
hid
,
report
,
USB_DIR_OUT
);
}
/* Sends commands to set range compatible with Driving Force Pro wheel */
static
void
hid_lg4ff_set_range_dfp
(
struct
hid_device
*
hid
,
__u16
range
)
{
struct
list_head
*
report_list
=
&
hid
->
report_enum
[
HID_OUTPUT_REPORT
].
report_list
;
struct
hid_report
*
report
=
list_entry
(
report_list
->
next
,
struct
hid_report
,
list
);
int
start_left
,
start_right
,
full_range
;
dbg_hid
(
"Driving Force Pro: setting range to %u
\n
"
,
range
);
/* Prepare "coarse" limit command */
report
->
field
[
0
]
->
value
[
0
]
=
0xf8
;
report
->
field
[
0
]
->
value
[
1
]
=
0x00
;
/* Set later */
report
->
field
[
0
]
->
value
[
2
]
=
0x00
;
report
->
field
[
0
]
->
value
[
3
]
=
0x00
;
report
->
field
[
0
]
->
value
[
4
]
=
0x00
;
report
->
field
[
0
]
->
value
[
5
]
=
0x00
;
report
->
field
[
0
]
->
value
[
6
]
=
0x00
;
if
(
range
>
200
)
{
report
->
field
[
0
]
->
value
[
1
]
=
0x03
;
full_range
=
900
;
}
else
{
report
->
field
[
0
]
->
value
[
1
]
=
0x02
;
full_range
=
200
;
}
usbhid_submit_report
(
hid
,
report
,
USB_DIR_OUT
);
/* Prepare "fine" limit command */
report
->
field
[
0
]
->
value
[
0
]
=
0x81
;
report
->
field
[
0
]
->
value
[
1
]
=
0x0b
;
report
->
field
[
0
]
->
value
[
2
]
=
0x00
;
report
->
field
[
0
]
->
value
[
3
]
=
0x00
;
report
->
field
[
0
]
->
value
[
4
]
=
0x00
;
report
->
field
[
0
]
->
value
[
5
]
=
0x00
;
report
->
field
[
0
]
->
value
[
6
]
=
0x00
;
if
(
range
==
200
||
range
==
900
)
{
/* Do not apply any fine limit */
usbhid_submit_report
(
hid
,
report
,
USB_DIR_OUT
);
return
;
}
/* Construct fine limit command */
start_left
=
(((
full_range
-
range
+
1
)
*
2047
)
/
full_range
);
start_right
=
0xfff
-
start_left
;
report
->
field
[
0
]
->
value
[
2
]
=
start_left
>>
4
;
report
->
field
[
0
]
->
value
[
3
]
=
start_right
>>
4
;
report
->
field
[
0
]
->
value
[
4
]
=
0xff
;
report
->
field
[
0
]
->
value
[
5
]
=
(
start_right
&
0xe
)
<<
4
|
(
start_left
&
0xe
);
report
->
field
[
0
]
->
value
[
6
]
=
0xff
;
usbhid_submit_report
(
hid
,
report
,
USB_DIR_OUT
);
}
static
void
hid_lg4ff_switch_native
(
struct
hid_device
*
hid
,
const
struct
lg4ff_native_cmd
*
cmd
)
{
struct
list_head
*
report_list
=
&
hid
->
report_enum
[
HID_OUTPUT_REPORT
].
report_list
;
struct
hid_report
*
report
=
list_entry
(
report_list
->
next
,
struct
hid_report
,
list
);
__u8
i
,
j
;
j
=
0
;
while
(
j
<
7
*
cmd
->
cmd_num
)
{
for
(
i
=
0
;
i
<
7
;
i
++
)
report
->
field
[
0
]
->
value
[
i
]
=
cmd
->
cmd
[
j
++
];
usbhid_submit_report
(
hid
,
report
,
USB_DIR_OUT
);
}
}
/* Read current range and display it in terminal */
static
ssize_t
lg4ff_range_show
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
char
*
buf
)
{
struct
lg4ff_device_entry
*
uninitialized_var
(
entry
);
struct
list_head
*
h
;
struct
hid_device
*
hid
=
to_hid_device
(
dev
);
size_t
count
;
list_for_each
(
h
,
&
device_list
.
list
)
{
entry
=
list_entry
(
h
,
struct
lg4ff_device_entry
,
list
);
if
(
strcmp
(
entry
->
device_id
,
(
&
hid
->
dev
)
->
kobj
.
name
)
==
0
)
break
;
}
if
(
h
==
&
device_list
.
list
)
{
dbg_hid
(
"Device not found!"
);
return
0
;
}
count
=
scnprintf
(
buf
,
PAGE_SIZE
,
"%u
\n
"
,
entry
->
range
);
return
count
;
}
/* Set range to user specified value, call appropriate function
* according to the type of the wheel */
static
ssize_t
lg4ff_range_store
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
const
char
*
buf
,
size_t
count
)
{
struct
lg4ff_device_entry
*
uninitialized_var
(
entry
);
struct
list_head
*
h
;
struct
hid_device
*
hid
=
to_hid_device
(
dev
);
__u16
range
=
simple_strtoul
(
buf
,
NULL
,
10
);
list_for_each
(
h
,
&
device_list
.
list
)
{
entry
=
list_entry
(
h
,
struct
lg4ff_device_entry
,
list
);
if
(
strcmp
(
entry
->
device_id
,
(
&
hid
->
dev
)
->
kobj
.
name
)
==
0
)
break
;
}
if
(
h
==
&
device_list
.
list
)
{
dbg_hid
(
"Device not found!"
);
return
count
;
}
if
(
range
==
0
)
range
=
entry
->
max_range
;
/* Check if the wheel supports range setting
* and that the range is within limits for the wheel */
if
(
entry
->
set_range
!=
NULL
&&
range
>=
entry
->
min_range
&&
range
<=
entry
->
max_range
)
{
entry
->
set_range
(
hid
,
range
);
entry
->
range
=
range
;
}
return
count
;
}
int
lg4ff_init
(
struct
hid_device
*
hid
)
{
...
...
@@ -95,9 +343,10 @@ int lg4ff_init(struct hid_device *hid)
struct
input_dev
*
dev
=
hidinput
->
input
;
struct
hid_report
*
report
;
struct
hid_field
*
field
;
const
signed
short
*
ff_bits
=
ff4_wheel_ac
;
int
error
;
int
i
;
struct
lg4ff_device_entry
*
entry
;
struct
usb_device_descriptor
*
udesc
;
int
error
,
i
,
j
;
__u16
bcdDevice
,
rev_maj
,
rev_min
;
/* Find the report to use */
if
(
list_empty
(
report_list
))
{
...
...
@@ -118,18 +367,122 @@ int lg4ff_init(struct hid_device *hid)
return
-
1
;
}
for
(
i
=
0
;
ff_bits
[
i
]
>=
0
;
i
++
)
set_bit
(
ff_bits
[
i
],
dev
->
ffbit
);
/* Check what wheel has been connected */
for
(
i
=
0
;
i
<
ARRAY_SIZE
(
lg4ff_devices
);
i
++
)
{
if
(
hid
->
product
==
lg4ff_devices
[
i
].
product_id
)
{
dbg_hid
(
"Found compatible device, product ID %04X
\n
"
,
lg4ff_devices
[
i
].
product_id
);
break
;
}
}
if
(
i
==
ARRAY_SIZE
(
lg4ff_devices
))
{
hid_err
(
hid
,
"Device is not supported by lg4ff driver. If you think it should be, consider reporting a bug to"
"LKML, Simon Wood <simon@mungewell.org> or Michal Maly <madcatxster@gmail.com>
\n
"
);
return
-
1
;
}
/* Attempt to switch wheel to native mode when applicable */
udesc
=
&
(
hid_to_usb_dev
(
hid
)
->
descriptor
);
if
(
!
udesc
)
{
hid_err
(
hid
,
"NULL USB device descriptor
\n
"
);
return
-
1
;
}
bcdDevice
=
le16_to_cpu
(
udesc
->
bcdDevice
);
rev_maj
=
bcdDevice
>>
8
;
rev_min
=
bcdDevice
&
0xff
;
if
(
lg4ff_devices
[
i
].
product_id
==
USB_DEVICE_ID_LOGITECH_WHEEL
)
{
dbg_hid
(
"Generic wheel detected, can it do native?
\n
"
);
dbg_hid
(
"USB revision: %2x.%02x
\n
"
,
rev_maj
,
rev_min
);
for
(
j
=
0
;
j
<
ARRAY_SIZE
(
lg4ff_revs
);
j
++
)
{
if
(
lg4ff_revs
[
j
].
rev_maj
==
rev_maj
&&
lg4ff_revs
[
j
].
rev_min
==
rev_min
)
{
hid_lg4ff_switch_native
(
hid
,
lg4ff_revs
[
j
].
command
);
hid_info
(
hid
,
"Switched to native mode
\n
"
);
}
}
}
/* Set supported force feedback capabilities */
for
(
j
=
0
;
lg4ff_devices
[
i
].
ff_effects
[
j
]
>=
0
;
j
++
)
set_bit
(
lg4ff_devices
[
i
].
ff_effects
[
j
],
dev
->
ffbit
);
error
=
input_ff_create_memless
(
dev
,
NULL
,
hid_lg4ff_play
);
if
(
error
)
return
error
;
if
(
test_bit
(
FF_AUTOCENTER
,
dev
->
ffbit
))
dev
->
ff
->
set_autocenter
=
hid_lg4ff_set_autocenter
;
/* Check if autocentering is available and
* set the centering force to zero by default */
if
(
test_bit
(
FF_AUTOCENTER
,
dev
->
ffbit
))
{
if
(
rev_maj
==
FFEX_REV_MAJ
&&
rev_min
==
FFEX_REV_MIN
)
/* Formula Force EX expects different autocentering command */
dev
->
ff
->
set_autocenter
=
hid_lg4ff_set_autocenter_ffex
;
else
dev
->
ff
->
set_autocenter
=
hid_lg4ff_set_autocenter_default
;
dev
->
ff
->
set_autocenter
(
dev
,
0
);
}
/* Initialize device_list if this is the first device to handle by lg4ff */
if
(
!
list_inited
)
{
INIT_LIST_HEAD
(
&
device_list
.
list
);
list_inited
=
1
;
}
/* Add the device to device_list */
entry
=
(
struct
lg4ff_device_entry
*
)
kzalloc
(
sizeof
(
struct
lg4ff_device_entry
),
GFP_KERNEL
);
if
(
!
entry
)
{
hid_err
(
hid
,
"Cannot add device, insufficient memory.
\n
"
);
return
-
ENOMEM
;
}
entry
->
device_id
=
kstrdup
((
&
hid
->
dev
)
->
kobj
.
name
,
GFP_KERNEL
);
if
(
!
entry
->
device_id
)
{
hid_err
(
hid
,
"Cannot set device_id, insufficient memory.
\n
"
);
kfree
(
entry
);
return
-
ENOMEM
;
}
entry
->
min_range
=
lg4ff_devices
[
i
].
min_range
;
entry
->
max_range
=
lg4ff_devices
[
i
].
max_range
;
entry
->
set_range
=
lg4ff_devices
[
i
].
set_range
;
list_add
(
&
entry
->
list
,
&
device_list
.
list
);
/* Create sysfs interface */
error
=
device_create_file
(
&
hid
->
dev
,
&
dev_attr_range
);
if
(
error
)
return
error
;
dbg_hid
(
"sysfs interface created
\n
"
);
/* Set the maximum range to start with */
entry
->
range
=
entry
->
max_range
;
if
(
entry
->
set_range
!=
NULL
)
entry
->
set_range
(
hid
,
entry
->
range
);
hid_info
(
hid
,
"Force feedback for Logitech Speed Force Wireless by Simon Wood <simon@mungewell.org>
\n
"
);
return
0
;
}
int
lg4ff_deinit
(
struct
hid_device
*
hid
)
{
bool
found
=
0
;
struct
lg4ff_device_entry
*
entry
;
struct
list_head
*
h
,
*
g
;
list_for_each_safe
(
h
,
g
,
&
device_list
.
list
)
{
entry
=
list_entry
(
h
,
struct
lg4ff_device_entry
,
list
);
if
(
strcmp
(
entry
->
device_id
,
(
&
hid
->
dev
)
->
kobj
.
name
)
==
0
)
{
list_del
(
h
);
kfree
(
entry
->
device_id
);
kfree
(
entry
);
found
=
1
;
break
;
}
}
if
(
!
found
)
{
dbg_hid
(
"Device entry not found!
\n
"
);
return
-
1
;
}
device_remove_file
(
&
hid
->
dev
,
&
dev_attr_range
);
dbg_hid
(
"Device successfully unregistered
\n
"
);
return
0
;
}
drivers/hid/hid-lgff.c
View file @
b0eae38c
...
...
@@ -58,12 +58,6 @@ static const signed short ff_joystick_ac[] = {
-
1
};
static
const
signed
short
ff_wheel
[]
=
{
FF_CONSTANT
,
FF_AUTOCENTER
,
-
1
};
static
const
struct
dev_type
devices
[]
=
{
{
0x046d
,
0xc211
,
ff_rumble
},
{
0x046d
,
0xc219
,
ff_rumble
},
...
...
@@ -71,14 +65,7 @@ static const struct dev_type devices[] = {
{
0x046d
,
0xc286
,
ff_joystick_ac
},
{
0x046d
,
0xc287
,
ff_joystick_ac
},
{
0x046d
,
0xc293
,
ff_joystick
},
{
0x046d
,
0xc294
,
ff_wheel
},
{
0x046d
,
0xc298
,
ff_wheel
},
{
0x046d
,
0xc299
,
ff_wheel
},
{
0x046d
,
0xc29b
,
ff_wheel
},
{
0x046d
,
0xc295
,
ff_joystick
},
{
0x046d
,
0xc298
,
ff_wheel
},
{
0x046d
,
0xc299
,
ff_wheel
},
{
0x046d
,
0xca03
,
ff_wheel
},
};
static
int
hid_lgff_play
(
struct
input_dev
*
dev
,
void
*
data
,
struct
ff_effect
*
effect
)
...
...
drivers/hid/hid-logitech-dj.c
0 → 100644
View file @
b0eae38c
/*
* HID driver for Logitech Unifying receivers
*
* Copyright (c) 2011 Logitech
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <linux/device.h>
#include <linux/hid.h>
#include <linux/module.h>
#include <linux/usb.h>
#include "usbhid/usbhid.h"
#include "hid-ids.h"
#include "hid-logitech-dj.h"
/* Keyboard descriptor (1) */
static
const
char
kbd_descriptor
[]
=
{
0x05
,
0x01
,
/* USAGE_PAGE (generic Desktop) */
0x09
,
0x06
,
/* USAGE (Keyboard) */
0xA1
,
0x01
,
/* COLLECTION (Application) */
0x85
,
0x01
,
/* REPORT_ID (1) */
0x95
,
0x08
,
/* REPORT_COUNT (8) */
0x75
,
0x01
,
/* REPORT_SIZE (1) */
0x15
,
0x00
,
/* LOGICAL_MINIMUM (0) */
0x25
,
0x01
,
/* LOGICAL_MAXIMUM (1) */
0x05
,
0x07
,
/* USAGE_PAGE (Keyboard) */
0x19
,
0xE0
,
/* USAGE_MINIMUM (Left Control) */
0x29
,
0xE7
,
/* USAGE_MAXIMUM (Right GUI) */
0x81
,
0x02
,
/* INPUT (Data,Var,Abs) */
0x95
,
0x05
,
/* REPORT COUNT (5) */
0x05
,
0x08
,
/* USAGE PAGE (LED page) */
0x19
,
0x01
,
/* USAGE MINIMUM (1) */
0x29
,
0x05
,
/* USAGE MAXIMUM (5) */
0x91
,
0x02
,
/* OUTPUT (Data, Variable, Absolute) */
0x95
,
0x01
,
/* REPORT COUNT (1) */
0x75
,
0x03
,
/* REPORT SIZE (3) */
0x91
,
0x01
,
/* OUTPUT (Constant) */
0x95
,
0x06
,
/* REPORT_COUNT (6) */
0x75
,
0x08
,
/* REPORT_SIZE (8) */
0x15
,
0x00
,
/* LOGICAL_MINIMUM (0) */
0x26
,
0xFF
,
0x00
,
/* LOGICAL_MAXIMUM (255) */
0x05
,
0x07
,
/* USAGE_PAGE (Keyboard) */
0x19
,
0x00
,
/* USAGE_MINIMUM (no event) */
0x2A
,
0xFF
,
0x00
,
/* USAGE_MAXIMUM (reserved) */
0x81
,
0x00
,
/* INPUT (Data,Ary,Abs) */
0xC0
};
/* Mouse descriptor (2) */
static
const
char
mse_descriptor
[]
=
{
0x05
,
0x01
,
/* USAGE_PAGE (Generic Desktop) */
0x09
,
0x02
,
/* USAGE (Mouse) */
0xA1
,
0x01
,
/* COLLECTION (Application) */
0x85
,
0x02
,
/* REPORT_ID = 2 */
0x09
,
0x01
,
/* USAGE (pointer) */
0xA1
,
0x00
,
/* COLLECTION (physical) */
0x05
,
0x09
,
/* USAGE_PAGE (buttons) */
0x19
,
0x01
,
/* USAGE_MIN (1) */
0x29
,
0x10
,
/* USAGE_MAX (16) */
0x15
,
0x00
,
/* LOGICAL_MIN (0) */
0x25
,
0x01
,
/* LOGICAL_MAX (1) */
0x95
,
0x10
,
/* REPORT_COUNT (16) */
0x75
,
0x01
,
/* REPORT_SIZE (1) */
0x81
,
0x02
,
/* INPUT (data var abs) */
0x05
,
0x01
,
/* USAGE_PAGE (generic desktop) */
0x16
,
0x01
,
0xF8
,
/* LOGICAL_MIN (-2047) */
0x26
,
0xFF
,
0x07
,
/* LOGICAL_MAX (2047) */
0x75
,
0x0C
,
/* REPORT_SIZE (12) */
0x95
,
0x02
,
/* REPORT_COUNT (2) */
0x09
,
0x30
,
/* USAGE (X) */
0x09
,
0x31
,
/* USAGE (Y) */
0x81
,
0x06
,
/* INPUT */
0x15
,
0x81
,
/* LOGICAL_MIN (-127) */
0x25
,
0x7F
,
/* LOGICAL_MAX (127) */
0x75
,
0x08
,
/* REPORT_SIZE (8) */
0x95
,
0x01
,
/* REPORT_COUNT (1) */
0x09
,
0x38
,
/* USAGE (wheel) */
0x81
,
0x06
,
/* INPUT */
0x05
,
0x0C
,
/* USAGE_PAGE(consumer) */
0x0A
,
0x38
,
0x02
,
/* USAGE(AC Pan) */
0x95
,
0x01
,
/* REPORT_COUNT (1) */
0x81
,
0x06
,
/* INPUT */
0xC0
,
/* END_COLLECTION */
0xC0
,
/* END_COLLECTION */
};
/* Consumer Control descriptor (3) */
static
const
char
consumer_descriptor
[]
=
{
0x05
,
0x0C
,
/* USAGE_PAGE (Consumer Devices) */
0x09
,
0x01
,
/* USAGE (Consumer Control) */
0xA1
,
0x01
,
/* COLLECTION (Application) */
0x85
,
0x03
,
/* REPORT_ID = 3 */
0x75
,
0x10
,
/* REPORT_SIZE (16) */
0x95
,
0x02
,
/* REPORT_COUNT (2) */
0x15
,
0x01
,
/* LOGICAL_MIN (1) */
0x26
,
0x8C
,
0x02
,
/* LOGICAL_MAX (652) */
0x19
,
0x01
,
/* USAGE_MIN (1) */
0x2A
,
0x8C
,
0x02
,
/* USAGE_MAX (652) */
0x81
,
0x00
,
/* INPUT (Data Ary Abs) */
0xC0
,
/* END_COLLECTION */
};
/* */
/* System control descriptor (4) */
static
const
char
syscontrol_descriptor
[]
=
{
0x05
,
0x01
,
/* USAGE_PAGE (Generic Desktop) */
0x09
,
0x80
,
/* USAGE (System Control) */
0xA1
,
0x01
,
/* COLLECTION (Application) */
0x85
,
0x04
,
/* REPORT_ID = 4 */
0x75
,
0x02
,
/* REPORT_SIZE (2) */
0x95
,
0x01
,
/* REPORT_COUNT (1) */
0x15
,
0x01
,
/* LOGICAL_MIN (1) */
0x25
,
0x03
,
/* LOGICAL_MAX (3) */
0x09
,
0x82
,
/* USAGE (System Sleep) */
0x09
,
0x81
,
/* USAGE (System Power Down) */
0x09
,
0x83
,
/* USAGE (System Wake Up) */
0x81
,
0x60
,
/* INPUT (Data Ary Abs NPrf Null) */
0x75
,
0x06
,
/* REPORT_SIZE (6) */
0x81
,
0x03
,
/* INPUT (Cnst Var Abs) */
0xC0
,
/* END_COLLECTION */
};
/* Media descriptor (8) */
static
const
char
media_descriptor
[]
=
{
0x06
,
0xbc
,
0xff
,
/* Usage Page 0xffbc */
0x09
,
0x88
,
/* Usage 0x0088 */
0xa1
,
0x01
,
/* BeginCollection */
0x85
,
0x08
,
/* Report ID 8 */
0x19
,
0x01
,
/* Usage Min 0x0001 */
0x29
,
0xff
,
/* Usage Max 0x00ff */
0x15
,
0x01
,
/* Logical Min 1 */
0x26
,
0xff
,
0x00
,
/* Logical Max 255 */
0x75
,
0x08
,
/* Report Size 8 */
0x95
,
0x01
,
/* Report Count 1 */
0x81
,
0x00
,
/* Input */
0xc0
,
/* EndCollection */
};
/* */
/* Maximum size of all defined hid reports in bytes (including report id) */
#define MAX_REPORT_SIZE 8
/* Number of possible hid report types that can be created by this driver.
*
* Right now, RF report types have the same report types (or report id's)
* than the hid report created from those RF reports. In the future
* this doesnt have to be true.
*
* For instance, RF report type 0x01 which has a size of 8 bytes, corresponds
* to hid report id 0x01, this is standard keyboard. Same thing applies to mice
* reports and consumer control, etc. If a new RF report is created, it doesn't
* has to have the same report id as its corresponding hid report, so an
* translation may have to take place for future report types.
*/
#define NUMBER_OF_HID_REPORTS 32
static
const
u8
hid_reportid_size_map
[
NUMBER_OF_HID_REPORTS
]
=
{
[
1
]
=
8
,
/* Standard keyboard */
[
2
]
=
8
,
/* Standard mouse */
[
3
]
=
5
,
/* Consumer control */
[
4
]
=
2
,
/* System control */
[
8
]
=
2
,
/* Media Center */
};
#define LOGITECH_DJ_INTERFACE_NUMBER 0x02
static
struct
hid_ll_driver
logi_dj_ll_driver
;
static
int
logi_dj_output_hidraw_report
(
struct
hid_device
*
hid
,
u8
*
buf
,
size_t
count
,
unsigned
char
report_type
);
static
void
logi_dj_recv_destroy_djhid_device
(
struct
dj_receiver_dev
*
djrcv_dev
,
struct
dj_report
*
dj_report
)
{
/* Called in delayed work context */
struct
dj_device
*
dj_dev
;
unsigned
long
flags
;
spin_lock_irqsave
(
&
djrcv_dev
->
lock
,
flags
);
dj_dev
=
djrcv_dev
->
paired_dj_devices
[
dj_report
->
device_index
];
djrcv_dev
->
paired_dj_devices
[
dj_report
->
device_index
]
=
NULL
;
spin_unlock_irqrestore
(
&
djrcv_dev
->
lock
,
flags
);
if
(
dj_dev
!=
NULL
)
{
hid_destroy_device
(
dj_dev
->
hdev
);
kfree
(
dj_dev
);
}
else
{
dev_err
(
&
djrcv_dev
->
hdev
->
dev
,
"%s: can't destroy a NULL device
\n
"
,
__func__
);
}
}
static
void
logi_dj_recv_add_djhid_device
(
struct
dj_receiver_dev
*
djrcv_dev
,
struct
dj_report
*
dj_report
)
{
/* Called in delayed work context */
struct
hid_device
*
djrcv_hdev
=
djrcv_dev
->
hdev
;
struct
usb_interface
*
intf
=
to_usb_interface
(
djrcv_hdev
->
dev
.
parent
);
struct
usb_device
*
usbdev
=
interface_to_usbdev
(
intf
);
struct
hid_device
*
dj_hiddev
;
struct
dj_device
*
dj_dev
;
/* Device index goes from 1 to 6, we need 3 bytes to store the
* semicolon, the index, and a null terminator
*/
unsigned
char
tmpstr
[
3
];
if
(
dj_report
->
report_params
[
DEVICE_PAIRED_PARAM_SPFUNCTION
]
&
SPFUNCTION_DEVICE_LIST_EMPTY
)
{
dbg_hid
(
"%s: device list is empty
\n
"
,
__func__
);
return
;
}
if
((
dj_report
->
device_index
<
DJ_DEVICE_INDEX_MIN
)
||
(
dj_report
->
device_index
>
DJ_DEVICE_INDEX_MAX
))
{
dev_err
(
&
djrcv_hdev
->
dev
,
"%s: invalid device index:%d
\n
"
,
__func__
,
dj_report
->
device_index
);
return
;
}
dj_hiddev
=
hid_allocate_device
();
if
(
IS_ERR
(
dj_hiddev
))
{
dev_err
(
&
djrcv_hdev
->
dev
,
"%s: hid_allocate_device failed
\n
"
,
__func__
);
return
;
}
dj_hiddev
->
ll_driver
=
&
logi_dj_ll_driver
;
dj_hiddev
->
hid_output_raw_report
=
logi_dj_output_hidraw_report
;
dj_hiddev
->
dev
.
parent
=
&
djrcv_hdev
->
dev
;
dj_hiddev
->
bus
=
BUS_USB
;
dj_hiddev
->
vendor
=
le16_to_cpu
(
usbdev
->
descriptor
.
idVendor
);
dj_hiddev
->
product
=
le16_to_cpu
(
usbdev
->
descriptor
.
idProduct
);
snprintf
(
dj_hiddev
->
name
,
sizeof
(
dj_hiddev
->
name
),
"Logitech Unifying Device. Wireless PID:%02x%02x"
,
dj_report
->
report_params
[
DEVICE_PAIRED_PARAM_EQUAD_ID_MSB
],
dj_report
->
report_params
[
DEVICE_PAIRED_PARAM_EQUAD_ID_LSB
]);
usb_make_path
(
usbdev
,
dj_hiddev
->
phys
,
sizeof
(
dj_hiddev
->
phys
));
snprintf
(
tmpstr
,
sizeof
(
tmpstr
),
":%d"
,
dj_report
->
device_index
);
strlcat
(
dj_hiddev
->
phys
,
tmpstr
,
sizeof
(
dj_hiddev
->
phys
));
dj_dev
=
kzalloc
(
sizeof
(
struct
dj_device
),
GFP_KERNEL
);
if
(
!
dj_dev
)
{
dev_err
(
&
djrcv_hdev
->
dev
,
"%s: failed allocating dj_device
\n
"
,
__func__
);
goto
dj_device_allocate_fail
;
}
dj_dev
->
reports_supported
=
le32_to_cpu
(
dj_report
->
report_params
[
DEVICE_PAIRED_RF_REPORT_TYPE
]);
dj_dev
->
hdev
=
dj_hiddev
;
dj_dev
->
dj_receiver_dev
=
djrcv_dev
;
dj_dev
->
device_index
=
dj_report
->
device_index
;
dj_hiddev
->
driver_data
=
dj_dev
;
djrcv_dev
->
paired_dj_devices
[
dj_report
->
device_index
]
=
dj_dev
;
if
(
hid_add_device
(
dj_hiddev
))
{
dev_err
(
&
djrcv_hdev
->
dev
,
"%s: failed adding dj_device
\n
"
,
__func__
);
goto
hid_add_device_fail
;
}
return
;
hid_add_device_fail:
djrcv_dev
->
paired_dj_devices
[
dj_report
->
device_index
]
=
NULL
;
kfree
(
dj_dev
);
dj_device_allocate_fail:
hid_destroy_device
(
dj_hiddev
);
}
static
void
delayedwork_callback
(
struct
work_struct
*
work
)
{
struct
dj_receiver_dev
*
djrcv_dev
=
container_of
(
work
,
struct
dj_receiver_dev
,
work
);
struct
dj_report
dj_report
;
unsigned
long
flags
;
int
count
;
dbg_hid
(
"%s
\n
"
,
__func__
);
spin_lock_irqsave
(
&
djrcv_dev
->
lock
,
flags
);
count
=
kfifo_out
(
&
djrcv_dev
->
notif_fifo
,
&
dj_report
,
sizeof
(
struct
dj_report
));
if
(
count
!=
sizeof
(
struct
dj_report
))
{
dev_err
(
&
djrcv_dev
->
hdev
->
dev
,
"%s: workitem triggered without "
"notifications available
\n
"
,
__func__
);
spin_unlock_irqrestore
(
&
djrcv_dev
->
lock
,
flags
);
return
;
}
if
(
!
kfifo_is_empty
(
&
djrcv_dev
->
notif_fifo
))
{
if
(
schedule_work
(
&
djrcv_dev
->
work
)
==
0
)
{
dbg_hid
(
"%s: did not schedule the work item, was "
"already queued
\n
"
,
__func__
);
}
}
spin_unlock_irqrestore
(
&
djrcv_dev
->
lock
,
flags
);
switch
(
dj_report
.
report_type
)
{
case
REPORT_TYPE_NOTIF_DEVICE_PAIRED
:
logi_dj_recv_add_djhid_device
(
djrcv_dev
,
&
dj_report
);
break
;
case
REPORT_TYPE_NOTIF_DEVICE_UNPAIRED
:
logi_dj_recv_destroy_djhid_device
(
djrcv_dev
,
&
dj_report
);
break
;
default:
dbg_hid
(
"%s: unexpected report type
\n
"
,
__func__
);
}
}
static
void
logi_dj_recv_queue_notification
(
struct
dj_receiver_dev
*
djrcv_dev
,
struct
dj_report
*
dj_report
)
{
/* We are called from atomic context (tasklet && djrcv->lock held) */
kfifo_in
(
&
djrcv_dev
->
notif_fifo
,
dj_report
,
sizeof
(
struct
dj_report
));
if
(
schedule_work
(
&
djrcv_dev
->
work
)
==
0
)
{
dbg_hid
(
"%s: did not schedule the work item, was already "
"queued
\n
"
,
__func__
);
}
}
static
void
logi_dj_recv_forward_null_report
(
struct
dj_receiver_dev
*
djrcv_dev
,
struct
dj_report
*
dj_report
)
{
/* We are called from atomic context (tasklet && djrcv->lock held) */
unsigned
int
i
;
u8
reportbuffer
[
MAX_REPORT_SIZE
];
struct
dj_device
*
djdev
;
djdev
=
djrcv_dev
->
paired_dj_devices
[
dj_report
->
device_index
];
if
(
!
djdev
)
{
dbg_hid
(
"djrcv_dev->paired_dj_devices[dj_report->device_index]"
" is NULL, index %d
\n
"
,
dj_report
->
device_index
);
return
;
}
memset
(
reportbuffer
,
0
,
sizeof
(
reportbuffer
));
for
(
i
=
0
;
i
<
NUMBER_OF_HID_REPORTS
;
i
++
)
{
if
(
djdev
->
reports_supported
&
(
1
<<
i
))
{
reportbuffer
[
0
]
=
i
;
if
(
hid_input_report
(
djdev
->
hdev
,
HID_INPUT_REPORT
,
reportbuffer
,
hid_reportid_size_map
[
i
],
1
))
{
dbg_hid
(
"hid_input_report error sending null "
"report
\n
"
);
}
}
}
}
static
void
logi_dj_recv_forward_report
(
struct
dj_receiver_dev
*
djrcv_dev
,
struct
dj_report
*
dj_report
)
{
/* We are called from atomic context (tasklet && djrcv->lock held) */
struct
dj_device
*
dj_device
;
dj_device
=
djrcv_dev
->
paired_dj_devices
[
dj_report
->
device_index
];
if
(
dj_device
==
NULL
)
{
dbg_hid
(
"djrcv_dev->paired_dj_devices[dj_report->device_index]"
" is NULL, index %d
\n
"
,
dj_report
->
device_index
);
return
;
}
if
((
dj_report
->
report_type
>
ARRAY_SIZE
(
hid_reportid_size_map
)
-
1
)
||
(
hid_reportid_size_map
[
dj_report
->
report_type
]
==
0
))
{
dbg_hid
(
"invalid report type:%x
\n
"
,
dj_report
->
report_type
);
return
;
}
if
(
hid_input_report
(
dj_device
->
hdev
,
HID_INPUT_REPORT
,
&
dj_report
->
report_type
,
hid_reportid_size_map
[
dj_report
->
report_type
],
1
))
{
dbg_hid
(
"hid_input_report error
\n
"
);
}
}
static
int
logi_dj_recv_send_report
(
struct
dj_receiver_dev
*
djrcv_dev
,
struct
dj_report
*
dj_report
)
{
struct
hid_device
*
hdev
=
djrcv_dev
->
hdev
;
int
sent_bytes
;
if
(
!
hdev
->
hid_output_raw_report
)
{
dev_err
(
&
hdev
->
dev
,
"%s:"
"hid_output_raw_report is null
\n
"
,
__func__
);
return
-
ENODEV
;
}
sent_bytes
=
hdev
->
hid_output_raw_report
(
hdev
,
(
u8
*
)
dj_report
,
sizeof
(
struct
dj_report
),
HID_OUTPUT_REPORT
);
return
(
sent_bytes
<
0
)
?
sent_bytes
:
0
;
}
static
int
logi_dj_recv_query_paired_devices
(
struct
dj_receiver_dev
*
djrcv_dev
)
{
struct
dj_report
dj_report
;
memset
(
&
dj_report
,
0
,
sizeof
(
dj_report
));
dj_report
.
report_id
=
REPORT_ID_DJ_SHORT
;
dj_report
.
device_index
=
0xFF
;
dj_report
.
report_type
=
REPORT_TYPE_CMD_GET_PAIRED_DEVICES
;
return
logi_dj_recv_send_report
(
djrcv_dev
,
&
dj_report
);
}
static
int
logi_dj_recv_switch_to_dj_mode
(
struct
dj_receiver_dev
*
djrcv_dev
,
unsigned
timeout
)
{
struct
dj_report
dj_report
;
memset
(
&
dj_report
,
0
,
sizeof
(
dj_report
));
dj_report
.
report_id
=
REPORT_ID_DJ_SHORT
;
dj_report
.
device_index
=
0xFF
;
dj_report
.
report_type
=
REPORT_TYPE_CMD_SWITCH
;
dj_report
.
report_params
[
CMD_SWITCH_PARAM_DEVBITFIELD
]
=
0x1F
;
dj_report
.
report_params
[
CMD_SWITCH_PARAM_TIMEOUT_SECONDS
]
=
(
u8
)
timeout
;
return
logi_dj_recv_send_report
(
djrcv_dev
,
&
dj_report
);
}
static
int
logi_dj_ll_open
(
struct
hid_device
*
hid
)
{
dbg_hid
(
"%s:%s
\n
"
,
__func__
,
hid
->
phys
);
return
0
;
}
static
void
logi_dj_ll_close
(
struct
hid_device
*
hid
)
{
dbg_hid
(
"%s:%s
\n
"
,
__func__
,
hid
->
phys
);
}
static
int
logi_dj_output_hidraw_report
(
struct
hid_device
*
hid
,
u8
*
buf
,
size_t
count
,
unsigned
char
report_type
)
{
/* Called by hid raw to send data */
dbg_hid
(
"%s
\n
"
,
__func__
);
return
0
;
}
static
int
logi_dj_ll_parse
(
struct
hid_device
*
hid
)
{
struct
dj_device
*
djdev
=
hid
->
driver_data
;
int
retval
;
dbg_hid
(
"%s
\n
"
,
__func__
);
djdev
->
hdev
->
version
=
0x0111
;
djdev
->
hdev
->
country
=
0x00
;
if
(
djdev
->
reports_supported
&
STD_KEYBOARD
)
{
dbg_hid
(
"%s: sending a kbd descriptor, reports_supported: %x
\n
"
,
__func__
,
djdev
->
reports_supported
);
retval
=
hid_parse_report
(
hid
,
(
u8
*
)
kbd_descriptor
,
sizeof
(
kbd_descriptor
));
if
(
retval
)
{
dbg_hid
(
"%s: sending a kbd descriptor, hid_parse failed"
" error: %d
\n
"
,
__func__
,
retval
);
return
retval
;
}
}
if
(
djdev
->
reports_supported
&
STD_MOUSE
)
{
dbg_hid
(
"%s: sending a mouse descriptor, reports_supported: "
"%x
\n
"
,
__func__
,
djdev
->
reports_supported
);
retval
=
hid_parse_report
(
hid
,
(
u8
*
)
mse_descriptor
,
sizeof
(
mse_descriptor
));
if
(
retval
)
{
dbg_hid
(
"%s: sending a mouse descriptor, hid_parse "
"failed error: %d
\n
"
,
__func__
,
retval
);
return
retval
;
}
}
if
(
djdev
->
reports_supported
&
MULTIMEDIA
)
{
dbg_hid
(
"%s: sending a multimedia report descriptor: %x
\n
"
,
__func__
,
djdev
->
reports_supported
);
retval
=
hid_parse_report
(
hid
,
(
u8
*
)
consumer_descriptor
,
sizeof
(
consumer_descriptor
));
if
(
retval
)
{
dbg_hid
(
"%s: sending a consumer_descriptor, hid_parse "
"failed error: %d
\n
"
,
__func__
,
retval
);
return
retval
;
}
}
if
(
djdev
->
reports_supported
&
POWER_KEYS
)
{
dbg_hid
(
"%s: sending a power keys report descriptor: %x
\n
"
,
__func__
,
djdev
->
reports_supported
);
retval
=
hid_parse_report
(
hid
,
(
u8
*
)
syscontrol_descriptor
,
sizeof
(
syscontrol_descriptor
));
if
(
retval
)
{
dbg_hid
(
"%s: sending a syscontrol_descriptor, "
"hid_parse failed error: %d
\n
"
,
__func__
,
retval
);
return
retval
;
}
}
if
(
djdev
->
reports_supported
&
MEDIA_CENTER
)
{
dbg_hid
(
"%s: sending a media center report descriptor: %x
\n
"
,
__func__
,
djdev
->
reports_supported
);
retval
=
hid_parse_report
(
hid
,
(
u8
*
)
media_descriptor
,
sizeof
(
media_descriptor
));
if
(
retval
)
{
dbg_hid
(
"%s: sending a media_descriptor, hid_parse "
"failed error: %d
\n
"
,
__func__
,
retval
);
return
retval
;
}
}
if
(
djdev
->
reports_supported
&
KBD_LEDS
)
{
dbg_hid
(
"%s: need to send kbd leds report descriptor: %x
\n
"
,
__func__
,
djdev
->
reports_supported
);
}
return
0
;
}
static
int
logi_dj_ll_input_event
(
struct
input_dev
*
dev
,
unsigned
int
type
,
unsigned
int
code
,
int
value
)
{
/* Sent by the input layer to handle leds and Force Feedback */
struct
hid_device
*
dj_hiddev
=
input_get_drvdata
(
dev
);
struct
dj_device
*
dj_dev
=
dj_hiddev
->
driver_data
;
struct
dj_receiver_dev
*
djrcv_dev
=
dev_get_drvdata
(
dj_hiddev
->
dev
.
parent
);
struct
hid_device
*
dj_rcv_hiddev
=
djrcv_dev
->
hdev
;
struct
hid_report_enum
*
output_report_enum
;
struct
hid_field
*
field
;
struct
hid_report
*
report
;
unsigned
char
data
[
8
];
int
offset
;
dbg_hid
(
"%s: %s, type:%d | code:%d | value:%d
\n
"
,
__func__
,
dev
->
phys
,
type
,
code
,
value
);
if
(
type
!=
EV_LED
)
return
-
1
;
offset
=
hidinput_find_field
(
dj_hiddev
,
type
,
code
,
&
field
);
if
(
offset
==
-
1
)
{
dev_warn
(
&
dev
->
dev
,
"event field not found
\n
"
);
return
-
1
;
}
hid_set_field
(
field
,
offset
,
value
);
hid_output_report
(
field
->
report
,
&
data
[
0
]);
output_report_enum
=
&
dj_rcv_hiddev
->
report_enum
[
HID_OUTPUT_REPORT
];
report
=
output_report_enum
->
report_id_hash
[
REPORT_ID_DJ_SHORT
];
hid_set_field
(
report
->
field
[
0
],
0
,
dj_dev
->
device_index
);
hid_set_field
(
report
->
field
[
0
],
1
,
REPORT_TYPE_LEDS
);
hid_set_field
(
report
->
field
[
0
],
2
,
data
[
1
]);
usbhid_submit_report
(
dj_rcv_hiddev
,
report
,
USB_DIR_OUT
);
return
0
;
}
static
int
logi_dj_ll_start
(
struct
hid_device
*
hid
)
{
dbg_hid
(
"%s
\n
"
,
__func__
);
return
0
;
}
static
void
logi_dj_ll_stop
(
struct
hid_device
*
hid
)
{
dbg_hid
(
"%s
\n
"
,
__func__
);
}
static
struct
hid_ll_driver
logi_dj_ll_driver
=
{
.
parse
=
logi_dj_ll_parse
,
.
start
=
logi_dj_ll_start
,
.
stop
=
logi_dj_ll_stop
,
.
open
=
logi_dj_ll_open
,
.
close
=
logi_dj_ll_close
,
.
hidinput_input_event
=
logi_dj_ll_input_event
,
};
static
int
logi_dj_raw_event
(
struct
hid_device
*
hdev
,
struct
hid_report
*
report
,
u8
*
data
,
int
size
)
{
struct
dj_receiver_dev
*
djrcv_dev
=
hid_get_drvdata
(
hdev
);
struct
dj_report
*
dj_report
=
(
struct
dj_report
*
)
data
;
unsigned
long
flags
;
bool
report_processed
=
false
;
dbg_hid
(
"%s, size:%d
\n
"
,
__func__
,
size
);
/* Here we receive all data coming from iface 2, there are 4 cases:
*
* 1) Data should continue its normal processing i.e. data does not
* come from the DJ collection, in which case we do nothing and
* return 0, so hid-core can continue normal processing (will forward
* to associated hidraw device)
*
* 2) Data is from DJ collection, and is intended for this driver i. e.
* data contains arrival, departure, etc notifications, in which case
* we queue them for delayed processing by the work queue. We return 1
* to hid-core as no further processing is required from it.
*
* 3) Data is from DJ collection, and informs a connection change,
* if the change means rf link loss, then we must send a null report
* to the upper layer to discard potentially pressed keys that may be
* repeated forever by the input layer. Return 1 to hid-core as no
* further processing is required.
*
* 4) Data is from DJ collection and is an actual input event from
* a paired DJ device in which case we forward it to the correct hid
* device (via hid_input_report() ) and return 1 so hid-core does not do
* anything else with it.
*/
spin_lock_irqsave
(
&
djrcv_dev
->
lock
,
flags
);
if
(
dj_report
->
report_id
==
REPORT_ID_DJ_SHORT
)
{
switch
(
dj_report
->
report_type
)
{
case
REPORT_TYPE_NOTIF_DEVICE_PAIRED
:
case
REPORT_TYPE_NOTIF_DEVICE_UNPAIRED
:
logi_dj_recv_queue_notification
(
djrcv_dev
,
dj_report
);
break
;
case
REPORT_TYPE_NOTIF_CONNECTION_STATUS
:
if
(
dj_report
->
report_params
[
CONNECTION_STATUS_PARAM_STATUS
]
==
STATUS_LINKLOSS
)
{
logi_dj_recv_forward_null_report
(
djrcv_dev
,
dj_report
);
}
break
;
default:
logi_dj_recv_forward_report
(
djrcv_dev
,
dj_report
);
}
report_processed
=
true
;
}
spin_unlock_irqrestore
(
&
djrcv_dev
->
lock
,
flags
);
return
report_processed
;
}
static
int
logi_dj_probe
(
struct
hid_device
*
hdev
,
const
struct
hid_device_id
*
id
)
{
struct
usb_interface
*
intf
=
to_usb_interface
(
hdev
->
dev
.
parent
);
struct
dj_receiver_dev
*
djrcv_dev
;
int
retval
;
if
(
is_dj_device
((
struct
dj_device
*
)
hdev
->
driver_data
))
return
-
ENODEV
;
dbg_hid
(
"%s called for ifnum %d
\n
"
,
__func__
,
intf
->
cur_altsetting
->
desc
.
bInterfaceNumber
);
/* Ignore interfaces 0 and 1, they will not carry any data, dont create
* any hid_device for them */
if
(
intf
->
cur_altsetting
->
desc
.
bInterfaceNumber
!=
LOGITECH_DJ_INTERFACE_NUMBER
)
{
dbg_hid
(
"%s: ignoring ifnum %d
\n
"
,
__func__
,
intf
->
cur_altsetting
->
desc
.
bInterfaceNumber
);
return
-
ENODEV
;
}
/* Treat interface 2 */
djrcv_dev
=
kzalloc
(
sizeof
(
struct
dj_receiver_dev
),
GFP_KERNEL
);
if
(
!
djrcv_dev
)
{
dev_err
(
&
hdev
->
dev
,
"%s:failed allocating dj_receiver_dev
\n
"
,
__func__
);
return
-
ENOMEM
;
}
djrcv_dev
->
hdev
=
hdev
;
INIT_WORK
(
&
djrcv_dev
->
work
,
delayedwork_callback
);
spin_lock_init
(
&
djrcv_dev
->
lock
);
if
(
kfifo_alloc
(
&
djrcv_dev
->
notif_fifo
,
DJ_MAX_NUMBER_NOTIFICATIONS
*
sizeof
(
struct
dj_report
),
GFP_KERNEL
))
{
dev_err
(
&
hdev
->
dev
,
"%s:failed allocating notif_fifo
\n
"
,
__func__
);
kfree
(
djrcv_dev
);
return
-
ENOMEM
;
}
hid_set_drvdata
(
hdev
,
djrcv_dev
);
/* Call to usbhid to fetch the HID descriptors of interface 2 and
* subsequently call to the hid/hid-core to parse the fetched
* descriptors, this will in turn create the hidraw and hiddev nodes
* for interface 2 of the receiver */
retval
=
hid_parse
(
hdev
);
if
(
retval
)
{
dev_err
(
&
hdev
->
dev
,
"%s:parse of interface 2 failed
\n
"
,
__func__
);
goto
hid_parse_fail
;
}
/* Starts the usb device and connects to upper interfaces hiddev and
* hidraw */
retval
=
hid_hw_start
(
hdev
,
HID_CONNECT_DEFAULT
);
if
(
retval
)
{
dev_err
(
&
hdev
->
dev
,
"%s:hid_hw_start returned error
\n
"
,
__func__
);
goto
hid_hw_start_fail
;
}
retval
=
logi_dj_recv_switch_to_dj_mode
(
djrcv_dev
,
0
);
if
(
retval
<
0
)
{
dev_err
(
&
hdev
->
dev
,
"%s:logi_dj_recv_switch_to_dj_mode returned error:%d
\n
"
,
__func__
,
retval
);
goto
switch_to_dj_mode_fail
;
}
/* This is enabling the polling urb on the IN endpoint */
retval
=
hdev
->
ll_driver
->
open
(
hdev
);
if
(
retval
<
0
)
{
dev_err
(
&
hdev
->
dev
,
"%s:hdev->ll_driver->open returned "
"error:%d
\n
"
,
__func__
,
retval
);
goto
llopen_failed
;
}
retval
=
logi_dj_recv_query_paired_devices
(
djrcv_dev
);
if
(
retval
<
0
)
{
dev_err
(
&
hdev
->
dev
,
"%s:logi_dj_recv_query_paired_devices "
"error:%d
\n
"
,
__func__
,
retval
);
goto
logi_dj_recv_query_paired_devices_failed
;
}
return
retval
;
logi_dj_recv_query_paired_devices_failed:
hdev
->
ll_driver
->
close
(
hdev
);
llopen_failed:
switch_to_dj_mode_fail:
hid_hw_stop
(
hdev
);
hid_hw_start_fail:
hid_parse_fail:
kfifo_free
(
&
djrcv_dev
->
notif_fifo
);
kfree
(
djrcv_dev
);
hid_set_drvdata
(
hdev
,
NULL
);
return
retval
;
}
#ifdef CONFIG_PM
static
int
logi_dj_reset_resume
(
struct
hid_device
*
hdev
)
{
int
retval
;
struct
dj_receiver_dev
*
djrcv_dev
=
hid_get_drvdata
(
hdev
);
retval
=
logi_dj_recv_switch_to_dj_mode
(
djrcv_dev
,
0
);
if
(
retval
<
0
)
{
dev_err
(
&
hdev
->
dev
,
"%s:logi_dj_recv_switch_to_dj_mode returned error:%d
\n
"
,
__func__
,
retval
);
}
return
0
;
}
#endif
static
void
logi_dj_remove
(
struct
hid_device
*
hdev
)
{
struct
dj_receiver_dev
*
djrcv_dev
=
hid_get_drvdata
(
hdev
);
struct
dj_device
*
dj_dev
;
int
i
;
dbg_hid
(
"%s
\n
"
,
__func__
);
cancel_work_sync
(
&
djrcv_dev
->
work
);
hdev
->
ll_driver
->
close
(
hdev
);
hid_hw_stop
(
hdev
);
/* I suppose that at this point the only context that can access
* the djrecv_data is this thread as the work item is guaranteed to
* have finished and no more raw_event callbacks should arrive after
* the remove callback was triggered so no locks are put around the
* code below */
for
(
i
=
0
;
i
<
(
DJ_MAX_PAIRED_DEVICES
+
DJ_DEVICE_INDEX_MIN
);
i
++
)
{
dj_dev
=
djrcv_dev
->
paired_dj_devices
[
i
];
if
(
dj_dev
!=
NULL
)
{
hid_destroy_device
(
dj_dev
->
hdev
);
kfree
(
dj_dev
);
djrcv_dev
->
paired_dj_devices
[
i
]
=
NULL
;
}
}
kfifo_free
(
&
djrcv_dev
->
notif_fifo
);
kfree
(
djrcv_dev
);
hid_set_drvdata
(
hdev
,
NULL
);
}
static
int
logi_djdevice_probe
(
struct
hid_device
*
hdev
,
const
struct
hid_device_id
*
id
)
{
int
ret
;
struct
dj_device
*
dj_dev
=
hdev
->
driver_data
;
if
(
!
is_dj_device
(
dj_dev
))
return
-
ENODEV
;
ret
=
hid_parse
(
hdev
);
if
(
!
ret
)
ret
=
hid_hw_start
(
hdev
,
HID_CONNECT_DEFAULT
);
return
ret
;
}
static
const
struct
hid_device_id
logi_dj_receivers
[]
=
{
{
HID_USB_DEVICE
(
USB_VENDOR_ID_LOGITECH
,
USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER
)},
{
HID_USB_DEVICE
(
USB_VENDOR_ID_LOGITECH
,
USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER_2
)},
{}
};
MODULE_DEVICE_TABLE
(
hid
,
logi_dj_receivers
);
static
struct
hid_driver
logi_djreceiver_driver
=
{
.
name
=
"logitech-djreceiver"
,
.
id_table
=
logi_dj_receivers
,
.
probe
=
logi_dj_probe
,
.
remove
=
logi_dj_remove
,
.
raw_event
=
logi_dj_raw_event
,
#ifdef CONFIG_PM
.
reset_resume
=
logi_dj_reset_resume
,
#endif
};
static
const
struct
hid_device_id
logi_dj_devices
[]
=
{
{
HID_USB_DEVICE
(
USB_VENDOR_ID_LOGITECH
,
USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER
)},
{
HID_USB_DEVICE
(
USB_VENDOR_ID_LOGITECH
,
USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER_2
)},
{}
};
static
struct
hid_driver
logi_djdevice_driver
=
{
.
name
=
"logitech-djdevice"
,
.
id_table
=
logi_dj_devices
,
.
probe
=
logi_djdevice_probe
,
};
static
int
__init
logi_dj_init
(
void
)
{
int
retval
;
dbg_hid
(
"Logitech-DJ:%s
\n
"
,
__func__
);
retval
=
hid_register_driver
(
&
logi_djreceiver_driver
);
if
(
retval
)
return
retval
;
retval
=
hid_register_driver
(
&
logi_djdevice_driver
);
if
(
retval
)
hid_unregister_driver
(
&
logi_djreceiver_driver
);
return
retval
;
}
static
void
__exit
logi_dj_exit
(
void
)
{
dbg_hid
(
"Logitech-DJ:%s
\n
"
,
__func__
);
hid_unregister_driver
(
&
logi_djdevice_driver
);
hid_unregister_driver
(
&
logi_djreceiver_driver
);
}
module_init
(
logi_dj_init
);
module_exit
(
logi_dj_exit
);
MODULE_LICENSE
(
"GPL"
);
MODULE_AUTHOR
(
"Logitech"
);
MODULE_AUTHOR
(
"Nestor Lopez Casado"
);
MODULE_AUTHOR
(
"nlopezcasad@logitech.com"
);
drivers/hid/hid-logitech-dj.h
0 → 100644
View file @
b0eae38c
#ifndef __HID_LOGITECH_DJ_H
#define __HID_LOGITECH_DJ_H
/*
* HID driver for Logitech Unifying receivers
*
* Copyright (c) 2011 Logitech
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <linux/kfifo.h>
#define DJ_MAX_PAIRED_DEVICES 6
#define DJ_MAX_NUMBER_NOTIFICATIONS 8
#define DJ_DEVICE_INDEX_MIN 1
#define DJ_DEVICE_INDEX_MAX 6
#define DJREPORT_SHORT_LENGTH 15
#define DJREPORT_LONG_LENGTH 32
#define REPORT_ID_DJ_SHORT 0x20
#define REPORT_ID_DJ_LONG 0x21
#define REPORT_TYPE_RFREPORT_FIRST 0x01
#define REPORT_TYPE_RFREPORT_LAST 0x1F
/* Command Switch to DJ mode */
#define REPORT_TYPE_CMD_SWITCH 0x80
#define CMD_SWITCH_PARAM_DEVBITFIELD 0x00
#define CMD_SWITCH_PARAM_TIMEOUT_SECONDS 0x01
#define TIMEOUT_NO_KEEPALIVE 0x00
/* Command to Get the list of Paired devices */
#define REPORT_TYPE_CMD_GET_PAIRED_DEVICES 0x81
/* Device Paired Notification */
#define REPORT_TYPE_NOTIF_DEVICE_PAIRED 0x41
#define SPFUNCTION_MORE_NOTIF_EXPECTED 0x01
#define SPFUNCTION_DEVICE_LIST_EMPTY 0x02
#define DEVICE_PAIRED_PARAM_SPFUNCTION 0x00
#define DEVICE_PAIRED_PARAM_EQUAD_ID_LSB 0x01
#define DEVICE_PAIRED_PARAM_EQUAD_ID_MSB 0x02
#define DEVICE_PAIRED_RF_REPORT_TYPE 0x03
/* Device Un-Paired Notification */
#define REPORT_TYPE_NOTIF_DEVICE_UNPAIRED 0x40
/* Connection Status Notification */
#define REPORT_TYPE_NOTIF_CONNECTION_STATUS 0x42
#define CONNECTION_STATUS_PARAM_STATUS 0x00
#define STATUS_LINKLOSS 0x01
/* Error Notification */
#define REPORT_TYPE_NOTIF_ERROR 0x7F
#define NOTIF_ERROR_PARAM_ETYPE 0x00
#define ETYPE_KEEPALIVE_TIMEOUT 0x01
/* supported DJ HID && RF report types */
#define REPORT_TYPE_KEYBOARD 0x01
#define REPORT_TYPE_MOUSE 0x02
#define REPORT_TYPE_CONSUMER_CONTROL 0x03
#define REPORT_TYPE_SYSTEM_CONTROL 0x04
#define REPORT_TYPE_MEDIA_CENTER 0x08
#define REPORT_TYPE_LEDS 0x0E
/* RF Report types bitfield */
#define STD_KEYBOARD 0x00000002
#define STD_MOUSE 0x00000004
#define MULTIMEDIA 0x00000008
#define POWER_KEYS 0x00000010
#define MEDIA_CENTER 0x00000100
#define KBD_LEDS 0x00004000
struct
dj_report
{
u8
report_id
;
u8
device_index
;
u8
report_type
;
u8
report_params
[
DJREPORT_SHORT_LENGTH
-
3
];
};
struct
dj_receiver_dev
{
struct
hid_device
*
hdev
;
struct
dj_device
*
paired_dj_devices
[
DJ_MAX_PAIRED_DEVICES
+
DJ_DEVICE_INDEX_MIN
];
struct
work_struct
work
;
struct
kfifo
notif_fifo
;
spinlock_t
lock
;
};
struct
dj_device
{
struct
hid_device
*
hdev
;
struct
dj_receiver_dev
*
dj_receiver_dev
;
u32
reports_supported
;
u8
device_index
;
};
/**
* is_dj_device - know if the given dj_device is not the receiver.
* @dj_dev: the dj device to test
*
* This macro tests if a struct dj_device pointer is a device created
* by the bus enumarator.
*/
#define is_dj_device(dj_dev) \
(&(dj_dev)->dj_receiver_dev->hdev->dev == (dj_dev)->hdev->dev.parent)
#endif
drivers/hid/hid-multitouch.c
View file @
b0eae38c
...
...
@@ -47,10 +47,11 @@ MODULE_LICENSE("GPL");
#define MT_QUIRK_SLOT_IS_CONTACTID (1 << 1)
#define MT_QUIRK_CYPRESS (1 << 2)
#define MT_QUIRK_SLOT_IS_CONTACTNUMBER (1 << 3)
#define MT_QUIRK_VALID_IS_INRANGE (1 << 4)
#define MT_QUIRK_VALID_IS_CONFIDENCE (1 << 5)
#define MT_QUIRK_EGALAX_XYZ_FIXUP (1 << 6)
#define MT_QUIRK_SLOT_IS_CONTACTID_MINUS_ONE (1 << 7)
#define MT_QUIRK_ALWAYS_VALID (1 << 4)
#define MT_QUIRK_VALID_IS_INRANGE (1 << 5)
#define MT_QUIRK_VALID_IS_CONFIDENCE (1 << 6)
#define MT_QUIRK_EGALAX_XYZ_FIXUP (1 << 7)
#define MT_QUIRK_SLOT_IS_CONTACTID_MINUS_ONE (1 << 8)
struct
mt_slot
{
__s32
x
,
y
,
p
,
w
,
h
;
...
...
@@ -86,11 +87,12 @@ struct mt_class {
/* classes of device behavior */
#define MT_CLS_DEFAULT 0x0001
#define MT_CLS_CONFIDENCE 0x0002
#define MT_CLS_CONFIDENCE_MINUS_ONE 0x0003
#define MT_CLS_DUAL_INRANGE_CONTACTID 0x0004
#define MT_CLS_DUAL_INRANGE_CONTACTNUMBER 0x0005
#define MT_CLS_DUAL_NSMU_CONTACTID 0x0006
#define MT_CLS_SERIAL 0x0002
#define MT_CLS_CONFIDENCE 0x0003
#define MT_CLS_CONFIDENCE_MINUS_ONE 0x0004
#define MT_CLS_DUAL_INRANGE_CONTACTID 0x0005
#define MT_CLS_DUAL_INRANGE_CONTACTNUMBER 0x0006
#define MT_CLS_DUAL_NSMU_CONTACTID 0x0007
/* vendor specific classes */
#define MT_CLS_3M 0x0101
...
...
@@ -134,6 +136,8 @@ static int find_slot_from_contactid(struct mt_device *td)
struct
mt_class
mt_classes
[]
=
{
{
.
name
=
MT_CLS_DEFAULT
,
.
quirks
=
MT_QUIRK_NOT_SEEN_MEANS_UP
},
{
.
name
=
MT_CLS_SERIAL
,
.
quirks
=
MT_QUIRK_ALWAYS_VALID
},
{
.
name
=
MT_CLS_CONFIDENCE
,
.
quirks
=
MT_QUIRK_VALID_IS_CONFIDENCE
},
{
.
name
=
MT_CLS_CONFIDENCE_MINUS_ONE
,
...
...
@@ -213,6 +217,16 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
struct
mt_class
*
cls
=
td
->
mtclass
;
__s32
quirks
=
cls
->
quirks
;
/* Only map fields from TouchScreen or TouchPad collections.
* We need to ignore fields that belong to other collections
* such as Mouse that might have the same GenericDesktop usages. */
if
(
field
->
application
==
HID_DG_TOUCHSCREEN
)
set_bit
(
INPUT_PROP_DIRECT
,
hi
->
input
->
propbit
);
else
if
(
field
->
application
==
HID_DG_TOUCHPAD
)
set_bit
(
INPUT_PROP_POINTER
,
hi
->
input
->
propbit
);
else
return
0
;
switch
(
usage
->
hid
&
HID_USAGE_PAGE
)
{
case
HID_UP_GENDESK
:
...
...
@@ -277,6 +291,7 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
td
->
last_slot_field
=
usage
->
hid
;
td
->
last_field_index
=
field
->
index
;
td
->
last_mt_collection
=
usage
->
collection_index
;
hdev
->
quirks
&=
~
HID_QUIRK_MULTITOUCH
;
return
1
;
case
HID_DG_WIDTH
:
hid_map_usage
(
hi
,
usage
,
bit
,
max
,
...
...
@@ -435,7 +450,9 @@ static int mt_event(struct hid_device *hid, struct hid_field *field,
if
(
hid
->
claimed
&
HID_CLAIMED_INPUT
&&
td
->
slots
)
{
switch
(
usage
->
hid
)
{
case
HID_DG_INRANGE
:
if
(
quirks
&
MT_QUIRK_VALID_IS_INRANGE
)
if
(
quirks
&
MT_QUIRK_ALWAYS_VALID
)
td
->
curvalid
=
true
;
else
if
(
quirks
&
MT_QUIRK_VALID_IS_INRANGE
)
td
->
curvalid
=
value
;
break
;
case
HID_DG_TIPSWITCH
:
...
...
@@ -513,12 +530,44 @@ static void mt_set_input_mode(struct hid_device *hdev)
}
}
/* a list of devices for which there is a specialized multitouch driver */
static
const
struct
hid_device_id
mt_have_special_driver
[]
=
{
{
HID_USB_DEVICE
(
USB_VENDOR_ID_NTRIG
,
0x0001
)
},
{
HID_USB_DEVICE
(
USB_VENDOR_ID_NTRIG
,
0x0006
)
},
{
HID_USB_DEVICE
(
USB_VENDOR_ID_QUANTA
,
USB_DEVICE_ID_PIXART_IMAGING_INC_OPTICAL_TOUCH_SCREEN
)
},
{
HID_USB_DEVICE
(
USB_VENDOR_ID_QUANTA
,
USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH
)
},
{
}
};
static
bool
mt_match_one_id
(
struct
hid_device
*
hdev
,
const
struct
hid_device_id
*
id
)
{
return
id
->
bus
==
hdev
->
bus
&&
(
id
->
vendor
==
HID_ANY_ID
||
id
->
vendor
==
hdev
->
vendor
)
&&
(
id
->
product
==
HID_ANY_ID
||
id
->
product
==
hdev
->
product
);
}
static
const
struct
hid_device_id
*
mt_match_id
(
struct
hid_device
*
hdev
,
const
struct
hid_device_id
*
id
)
{
for
(;
id
->
bus
;
id
++
)
if
(
mt_match_one_id
(
hdev
,
id
))
return
id
;
return
NULL
;
}
static
int
mt_probe
(
struct
hid_device
*
hdev
,
const
struct
hid_device_id
*
id
)
{
int
ret
,
i
;
struct
mt_device
*
td
;
struct
mt_class
*
mtclass
=
mt_classes
;
/* MT_CLS_DEFAULT */
if
(
mt_match_id
(
hdev
,
mt_have_special_driver
))
return
-
ENODEV
;
for
(
i
=
0
;
mt_classes
[
i
].
name
;
i
++
)
{
if
(
id
->
driver_data
==
mt_classes
[
i
].
name
)
{
mtclass
=
&
(
mt_classes
[
i
]);
...
...
@@ -526,10 +575,6 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
}
}
/* This allows the driver to correctly support devices
* that emit events over several HID messages.
*/
hdev
->
quirks
|=
HID_QUIRK_NO_INPUT_SYNC
;
td
=
kzalloc
(
sizeof
(
struct
mt_device
),
GFP_KERNEL
);
if
(
!
td
)
{
...
...
@@ -545,10 +590,16 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
if
(
ret
!=
0
)
goto
fail
;
hdev
->
quirks
|=
HID_QUIRK_MULTITOUCH
;
ret
=
hid_hw_start
(
hdev
,
HID_CONNECT_DEFAULT
);
if
(
ret
)
goto
fail
;
/* This allows the driver to correctly support devices
* that emit events over several HID messages.
*/
hdev
->
quirks
|=
HID_QUIRK_NO_INPUT_SYNC
;
td
->
slots
=
kzalloc
(
td
->
maxcontacts
*
sizeof
(
struct
mt_slot
),
GFP_KERNEL
);
if
(
!
td
->
slots
)
{
...
...
@@ -662,6 +713,11 @@ static const struct hid_device_id mt_devices[] = {
HID_USB_DEVICE
(
USB_VENDOR_ID_GOODTOUCH
,
USB_DEVICE_ID_GOODTOUCH_000f
)
},
/* Ideacom panel */
{
.
driver_data
=
MT_CLS_SERIAL
,
HID_USB_DEVICE
(
USB_VENDOR_ID_IDEACOM
,
USB_DEVICE_ID_IDEACOM_IDC6650
)
},
/* Ilitek dual touch panel */
{
.
driver_data
=
MT_CLS_DEFAULT
,
HID_USB_DEVICE
(
USB_VENDOR_ID_ILITEK
,
...
...
@@ -672,6 +728,11 @@ static const struct hid_device_id mt_devices[] = {
HID_USB_DEVICE
(
USB_VENDOR_ID_IRTOUCHSYSTEMS
,
USB_DEVICE_ID_IRTOUCH_INFRARED_USB
)
},
/* LG Display panels */
{
.
driver_data
=
MT_CLS_DEFAULT
,
HID_USB_DEVICE
(
USB_VENDOR_ID_LG
,
USB_DEVICE_ID_LG_MULTITOUCH
)
},
/* Lumio panels */
{
.
driver_data
=
MT_CLS_CONFIDENCE_MINUS_ONE
,
HID_USB_DEVICE
(
USB_VENDOR_ID_LUMIO
,
...
...
@@ -732,6 +793,10 @@ static const struct hid_device_id mt_devices[] = {
HID_USB_DEVICE
(
USB_VENDOR_ID_XAT
,
USB_DEVICE_ID_XAT_CSR
)
},
/* Rest of the world */
{
.
driver_data
=
MT_CLS_DEFAULT
,
HID_USB_DEVICE
(
HID_ANY_ID
,
HID_ANY_ID
)
},
{
}
};
MODULE_DEVICE_TABLE
(
hid
,
mt_devices
);
...
...
drivers/hid/hid-roccat-kone.c
View file @
b0eae38c
...
...
@@ -37,6 +37,21 @@
static
uint
profile_numbers
[
5
]
=
{
0
,
1
,
2
,
3
,
4
};
static
void
kone_profile_activated
(
struct
kone_device
*
kone
,
uint
new_profile
)
{
kone
->
actual_profile
=
new_profile
;
kone
->
actual_dpi
=
kone
->
profiles
[
new_profile
-
1
].
startup_dpi
;
}
static
void
kone_profile_report
(
struct
kone_device
*
kone
,
uint
new_profile
)
{
struct
kone_roccat_report
roccat_report
;
roccat_report
.
event
=
kone_mouse_event_switch_profile
;
roccat_report
.
value
=
new_profile
;
roccat_report
.
key
=
0
;
roccat_report_event
(
kone
->
chrdev_minor
,
(
uint8_t
*
)
&
roccat_report
);
}
static
int
kone_receive
(
struct
usb_device
*
usb_dev
,
uint
usb_command
,
void
*
data
,
uint
size
)
{
...
...
@@ -283,7 +298,7 @@ static ssize_t kone_sysfs_write_settings(struct file *fp, struct kobject *kobj,
container_of
(
kobj
,
struct
device
,
kobj
)
->
parent
->
parent
;
struct
kone_device
*
kone
=
hid_get_drvdata
(
dev_get_drvdata
(
dev
));
struct
usb_device
*
usb_dev
=
interface_to_usbdev
(
to_usb_interface
(
dev
));
int
retval
=
0
,
difference
;
int
retval
=
0
,
difference
,
old_profile
;
/* I need to get my data in one piece */
if
(
off
!=
0
||
count
!=
sizeof
(
struct
kone_settings
))
...
...
@@ -294,21 +309,20 @@ static ssize_t kone_sysfs_write_settings(struct file *fp, struct kobject *kobj,
if
(
difference
)
{
retval
=
kone_set_settings
(
usb_dev
,
(
struct
kone_settings
const
*
)
buf
);
if
(
!
retval
)
memcpy
(
&
kone
->
settings
,
buf
,
sizeof
(
struct
kone_settings
));
}
if
(
retval
)
{
mutex_unlock
(
&
kone
->
kone_lock
);
if
(
retval
)
return
retval
;
}
/*
* If we get here, treat settings as okay and update actual values
* according to startup_profile
*/
kone
->
actual_profile
=
kone
->
settings
.
startup_profile
;
kone
->
actual_dpi
=
kone
->
profiles
[
kone
->
actual_profile
-
1
].
startup_dpi
;
old_profile
=
kone
->
settings
.
startup_profile
;
memcpy
(
&
kone
->
settings
,
buf
,
sizeof
(
struct
kone_settings
));
kone_profile_activated
(
kone
,
kone
->
settings
.
startup_profile
);
if
(
kone
->
settings
.
startup_profile
!=
old_profile
)
kone_profile_report
(
kone
,
kone
->
settings
.
startup_profile
);
}
mutex_unlock
(
&
kone
->
kone_lock
);
return
sizeof
(
struct
kone_settings
);
}
...
...
@@ -501,6 +515,8 @@ static ssize_t kone_sysfs_set_tcu(struct device *dev,
goto
exit_no_settings
;
goto
exit_unlock
;
}
/* calibration resets profile */
kone_profile_activated
(
kone
,
kone
->
settings
.
startup_profile
);
}
retval
=
size
;
...
...
@@ -544,16 +560,16 @@ static ssize_t kone_sysfs_set_startup_profile(struct device *dev,
kone_set_settings_checksum
(
&
kone
->
settings
);
retval
=
kone_set_settings
(
usb_dev
,
&
kone
->
settings
);
if
(
retval
)
{
mutex_unlock
(
&
kone
->
kone_lock
);
if
(
retval
)
return
retval
;
}
/* changing the startup profile immediately activates this profile */
kone
->
actual_profile
=
new_startup_profile
;
kone
->
actual_dpi
=
kone
->
profiles
[
kone
->
actual_profile
-
1
].
startup_dpi
;
kone
_profile_activated
(
kone
,
new_startup_profile
)
;
kone
_profile_report
(
kone
,
new_startup_profile
)
;
mutex_unlock
(
&
kone
->
kone_lock
);
return
size
;
}
...
...
@@ -665,8 +681,7 @@ static int kone_init_kone_device_struct(struct usb_device *usb_dev,
if
(
retval
)
return
retval
;
kone
->
actual_profile
=
kone
->
settings
.
startup_profile
;
kone
->
actual_dpi
=
kone
->
profiles
[
kone
->
actual_profile
].
startup_dpi
;
kone_profile_activated
(
kone
,
kone
->
settings
.
startup_profile
);
return
0
;
}
...
...
@@ -776,10 +791,10 @@ static void kone_keep_values_up_to_date(struct kone_device *kone,
{
switch
(
event
->
event
)
{
case
kone_mouse_event_switch_profile
:
kone
->
actual_dpi
=
kone
->
profiles
[
event
->
value
-
1
].
startup_dpi
;
case
kone_mouse_event_osd_profile
:
kone
->
actual_profile
=
event
->
value
;
kone
->
actual_dpi
=
kone
->
profiles
[
kone
->
actual_profile
-
1
].
startup_dpi
;
break
;
case
kone_mouse_event_switch_dpi
:
case
kone_mouse_event_osd_dpi
:
...
...
drivers/hid/hid-roccat-kovaplus.c
View file @
b0eae38c
...
...
@@ -323,6 +323,7 @@ static ssize_t kovaplus_sysfs_set_actual_profile(struct device *dev,
struct
usb_device
*
usb_dev
;
unsigned
long
profile
;
int
retval
;
struct
kovaplus_roccat_report
roccat_report
;
dev
=
dev
->
parent
->
parent
;
kovaplus
=
hid_get_drvdata
(
dev_get_drvdata
(
dev
));
...
...
@@ -337,10 +338,22 @@ static ssize_t kovaplus_sysfs_set_actual_profile(struct device *dev,
mutex_lock
(
&
kovaplus
->
kovaplus_lock
);
retval
=
kovaplus_set_actual_profile
(
usb_dev
,
profile
);
kovaplus_profile_activated
(
kovaplus
,
profile
);
if
(
retval
)
{
mutex_unlock
(
&
kovaplus
->
kovaplus_lock
);
if
(
retval
)
return
retval
;
}
kovaplus_profile_activated
(
kovaplus
,
profile
);
roccat_report
.
type
=
KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_PROFILE_1
;
roccat_report
.
profile
=
profile
+
1
;
roccat_report
.
button
=
0
;
roccat_report
.
data1
=
profile
+
1
;
roccat_report
.
data2
=
0
;
roccat_report_event
(
kovaplus
->
chrdev_minor
,
(
uint8_t
const
*
)
&
roccat_report
);
mutex_unlock
(
&
kovaplus
->
kovaplus_lock
);
return
size
;
}
...
...
drivers/hid/hid-roccat-pyra.c
View file @
b0eae38c
...
...
@@ -298,6 +298,7 @@ static ssize_t pyra_sysfs_write_settings(struct file *fp,
struct
usb_device
*
usb_dev
=
interface_to_usbdev
(
to_usb_interface
(
dev
));
int
retval
=
0
;
int
difference
;
struct
pyra_roccat_report
roccat_report
;
if
(
off
!=
0
||
count
!=
sizeof
(
struct
pyra_settings
))
return
-
EINVAL
;
...
...
@@ -307,17 +308,23 @@ static ssize_t pyra_sysfs_write_settings(struct file *fp,
if
(
difference
)
{
retval
=
pyra_set_settings
(
usb_dev
,
(
struct
pyra_settings
const
*
)
buf
);
if
(
!
retval
)
memcpy
(
&
pyra
->
settings
,
buf
,
sizeof
(
struct
pyra_settings
));
}
if
(
retval
)
{
mutex_unlock
(
&
pyra
->
pyra_lock
);
if
(
retval
)
return
retval
;
}
memcpy
(
&
pyra
->
settings
,
buf
,
sizeof
(
struct
pyra_settings
));
profile_activated
(
pyra
,
pyra
->
settings
.
startup_profile
);
roccat_report
.
type
=
PYRA_MOUSE_EVENT_BUTTON_TYPE_PROFILE_2
;
roccat_report
.
value
=
pyra
->
settings
.
startup_profile
+
1
;
roccat_report
.
key
=
0
;
roccat_report_event
(
pyra
->
chrdev_minor
,
(
uint8_t
const
*
)
&
roccat_report
);
}
mutex_unlock
(
&
pyra
->
pyra_lock
);
return
sizeof
(
struct
pyra_settings
);
}
...
...
drivers/hid/hid-wiimote.c
View file @
b0eae38c
...
...
@@ -10,15 +10,18 @@
* any later version.
*/
#include <linux/completion.h>
#include <linux/device.h>
#include <linux/hid.h>
#include <linux/input.h>
#include <linux/leds.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/power_supply.h>
#include <linux/spinlock.h>
#include "hid-ids.h"
#define WIIMOTE_VERSION "0.
1
"
#define WIIMOTE_VERSION "0.
2
"
#define WIIMOTE_NAME "Nintendo Wii Remote"
#define WIIMOTE_BUFSIZE 32
...
...
@@ -30,12 +33,26 @@ struct wiimote_buf {
struct
wiimote_state
{
spinlock_t
lock
;
__u8
flags
;
__u8
accel_split
[
2
];
/* synchronous cmd requests */
struct
mutex
sync
;
struct
completion
ready
;
int
cmd
;
__u32
opt
;
/* results of synchronous requests */
__u8
cmd_battery
;
__u8
cmd_err
;
};
struct
wiimote_data
{
struct
hid_device
*
hdev
;
struct
input_dev
*
input
;
struct
led_classdev
*
leds
[
4
];
struct
input_dev
*
accel
;
struct
input_dev
*
ir
;
struct
power_supply
battery
;
spinlock_t
qlock
;
__u8
head
;
...
...
@@ -50,19 +67,43 @@ struct wiimote_data {
#define WIIPROTO_FLAG_LED2 0x02
#define WIIPROTO_FLAG_LED3 0x04
#define WIIPROTO_FLAG_LED4 0x08
#define WIIPROTO_FLAG_RUMBLE 0x10
#define WIIPROTO_FLAG_ACCEL 0x20
#define WIIPROTO_FLAG_IR_BASIC 0x40
#define WIIPROTO_FLAG_IR_EXT 0x80
#define WIIPROTO_FLAG_IR_FULL 0xc0
/* IR_BASIC | IR_EXT */
#define WIIPROTO_FLAGS_LEDS (WIIPROTO_FLAG_LED1 | WIIPROTO_FLAG_LED2 | \
WIIPROTO_FLAG_LED3 | WIIPROTO_FLAG_LED4)
#define WIIPROTO_FLAGS_IR (WIIPROTO_FLAG_IR_BASIC | WIIPROTO_FLAG_IR_EXT | \
WIIPROTO_FLAG_IR_FULL)
/* return flag for led \num */
#define WIIPROTO_FLAG_LED(num) (WIIPROTO_FLAG_LED1 << (num - 1))
enum
wiiproto_reqs
{
WIIPROTO_REQ_NULL
=
0x0
,
WIIPROTO_REQ_RUMBLE
=
0x10
,
WIIPROTO_REQ_LED
=
0x11
,
WIIPROTO_REQ_DRM
=
0x12
,
WIIPROTO_REQ_IR1
=
0x13
,
WIIPROTO_REQ_SREQ
=
0x15
,
WIIPROTO_REQ_WMEM
=
0x16
,
WIIPROTO_REQ_RMEM
=
0x17
,
WIIPROTO_REQ_IR2
=
0x1a
,
WIIPROTO_REQ_STATUS
=
0x20
,
WIIPROTO_REQ_DATA
=
0x21
,
WIIPROTO_REQ_RETURN
=
0x22
,
WIIPROTO_REQ_DRM_K
=
0x30
,
WIIPROTO_REQ_DRM_KA
=
0x31
,
WIIPROTO_REQ_DRM_KE
=
0x32
,
WIIPROTO_REQ_DRM_KAI
=
0x33
,
WIIPROTO_REQ_DRM_KEE
=
0x34
,
WIIPROTO_REQ_DRM_KAE
=
0x35
,
WIIPROTO_REQ_DRM_KIE
=
0x36
,
WIIPROTO_REQ_DRM_KAIE
=
0x37
,
WIIPROTO_REQ_DRM_E
=
0x3d
,
WIIPROTO_REQ_DRM_SKAI1
=
0x3e
,
WIIPROTO_REQ_DRM_SKAI2
=
0x3f
,
};
enum
wiiproto_keys
{
...
...
@@ -94,6 +135,56 @@ static __u16 wiiproto_keymap[] = {
BTN_MODE
,
/* WIIPROTO_KEY_HOME */
};
static
enum
power_supply_property
wiimote_battery_props
[]
=
{
POWER_SUPPLY_PROP_CAPACITY
};
/* requires the state.lock spinlock to be held */
static
inline
bool
wiimote_cmd_pending
(
struct
wiimote_data
*
wdata
,
int
cmd
,
__u32
opt
)
{
return
wdata
->
state
.
cmd
==
cmd
&&
wdata
->
state
.
opt
==
opt
;
}
/* requires the state.lock spinlock to be held */
static
inline
void
wiimote_cmd_complete
(
struct
wiimote_data
*
wdata
)
{
wdata
->
state
.
cmd
=
WIIPROTO_REQ_NULL
;
complete
(
&
wdata
->
state
.
ready
);
}
static
inline
int
wiimote_cmd_acquire
(
struct
wiimote_data
*
wdata
)
{
return
mutex_lock_interruptible
(
&
wdata
->
state
.
sync
)
?
-
ERESTARTSYS
:
0
;
}
/* requires the state.lock spinlock to be held */
static
inline
void
wiimote_cmd_set
(
struct
wiimote_data
*
wdata
,
int
cmd
,
__u32
opt
)
{
INIT_COMPLETION
(
wdata
->
state
.
ready
);
wdata
->
state
.
cmd
=
cmd
;
wdata
->
state
.
opt
=
opt
;
}
static
inline
void
wiimote_cmd_release
(
struct
wiimote_data
*
wdata
)
{
mutex_unlock
(
&
wdata
->
state
.
sync
);
}
static
inline
int
wiimote_cmd_wait
(
struct
wiimote_data
*
wdata
)
{
int
ret
;
ret
=
wait_for_completion_interruptible_timeout
(
&
wdata
->
state
.
ready
,
HZ
);
if
(
ret
<
0
)
return
-
ERESTARTSYS
;
else
if
(
ret
==
0
)
return
-
EIO
;
else
return
0
;
}
static
ssize_t
wiimote_hid_send
(
struct
hid_device
*
hdev
,
__u8
*
buffer
,
size_t
count
)
{
...
...
@@ -172,6 +263,39 @@ static void wiimote_queue(struct wiimote_data *wdata, const __u8 *buffer,
spin_unlock_irqrestore
(
&
wdata
->
qlock
,
flags
);
}
/*
* This sets the rumble bit on the given output report if rumble is
* currently enabled.
* \cmd1 must point to the second byte in the output report => &cmd[1]
* This must be called on nearly every output report before passing it
* into the output queue!
*/
static
inline
void
wiiproto_keep_rumble
(
struct
wiimote_data
*
wdata
,
__u8
*
cmd1
)
{
if
(
wdata
->
state
.
flags
&
WIIPROTO_FLAG_RUMBLE
)
*
cmd1
|=
0x01
;
}
static
void
wiiproto_req_rumble
(
struct
wiimote_data
*
wdata
,
__u8
rumble
)
{
__u8
cmd
[
2
];
rumble
=
!!
rumble
;
if
(
rumble
==
!!
(
wdata
->
state
.
flags
&
WIIPROTO_FLAG_RUMBLE
))
return
;
if
(
rumble
)
wdata
->
state
.
flags
|=
WIIPROTO_FLAG_RUMBLE
;
else
wdata
->
state
.
flags
&=
~
WIIPROTO_FLAG_RUMBLE
;
cmd
[
0
]
=
WIIPROTO_REQ_RUMBLE
;
cmd
[
1
]
=
0
;
wiiproto_keep_rumble
(
wdata
,
&
cmd
[
1
]);
wiimote_queue
(
wdata
,
cmd
,
sizeof
(
cmd
));
}
static
void
wiiproto_req_leds
(
struct
wiimote_data
*
wdata
,
int
leds
)
{
__u8
cmd
[
2
];
...
...
@@ -193,6 +317,7 @@ static void wiiproto_req_leds(struct wiimote_data *wdata, int leds)
if
(
leds
&
WIIPROTO_FLAG_LED4
)
cmd
[
1
]
|=
0x80
;
wiiproto_keep_rumble
(
wdata
,
&
cmd
[
1
]);
wiimote_queue
(
wdata
,
cmd
,
sizeof
(
cmd
));
}
...
...
@@ -203,7 +328,23 @@ static void wiiproto_req_leds(struct wiimote_data *wdata, int leds)
*/
static
__u8
select_drm
(
struct
wiimote_data
*
wdata
)
{
__u8
ir
=
wdata
->
state
.
flags
&
WIIPROTO_FLAGS_IR
;
if
(
ir
==
WIIPROTO_FLAG_IR_BASIC
)
{
if
(
wdata
->
state
.
flags
&
WIIPROTO_FLAG_ACCEL
)
return
WIIPROTO_REQ_DRM_KAIE
;
else
return
WIIPROTO_REQ_DRM_KIE
;
}
else
if
(
ir
==
WIIPROTO_FLAG_IR_EXT
)
{
return
WIIPROTO_REQ_DRM_KAI
;
}
else
if
(
ir
==
WIIPROTO_FLAG_IR_FULL
)
{
return
WIIPROTO_REQ_DRM_SKAI1
;
}
else
{
if
(
wdata
->
state
.
flags
&
WIIPROTO_FLAG_ACCEL
)
return
WIIPROTO_REQ_DRM_KA
;
else
return
WIIPROTO_REQ_DRM_K
;
}
}
static
void
wiiproto_req_drm
(
struct
wiimote_data
*
wdata
,
__u8
drm
)
...
...
@@ -217,9 +358,256 @@ static void wiiproto_req_drm(struct wiimote_data *wdata, __u8 drm)
cmd
[
1
]
=
0
;
cmd
[
2
]
=
drm
;
wiiproto_keep_rumble
(
wdata
,
&
cmd
[
1
]);
wiimote_queue
(
wdata
,
cmd
,
sizeof
(
cmd
));
}
static
void
wiiproto_req_status
(
struct
wiimote_data
*
wdata
)
{
__u8
cmd
[
2
];
cmd
[
0
]
=
WIIPROTO_REQ_SREQ
;
cmd
[
1
]
=
0
;
wiiproto_keep_rumble
(
wdata
,
&
cmd
[
1
]);
wiimote_queue
(
wdata
,
cmd
,
sizeof
(
cmd
));
}
static
void
wiiproto_req_accel
(
struct
wiimote_data
*
wdata
,
__u8
accel
)
{
accel
=
!!
accel
;
if
(
accel
==
!!
(
wdata
->
state
.
flags
&
WIIPROTO_FLAG_ACCEL
))
return
;
if
(
accel
)
wdata
->
state
.
flags
|=
WIIPROTO_FLAG_ACCEL
;
else
wdata
->
state
.
flags
&=
~
WIIPROTO_FLAG_ACCEL
;
wiiproto_req_drm
(
wdata
,
WIIPROTO_REQ_NULL
);
}
static
void
wiiproto_req_ir1
(
struct
wiimote_data
*
wdata
,
__u8
flags
)
{
__u8
cmd
[
2
];
cmd
[
0
]
=
WIIPROTO_REQ_IR1
;
cmd
[
1
]
=
flags
;
wiiproto_keep_rumble
(
wdata
,
&
cmd
[
1
]);
wiimote_queue
(
wdata
,
cmd
,
sizeof
(
cmd
));
}
static
void
wiiproto_req_ir2
(
struct
wiimote_data
*
wdata
,
__u8
flags
)
{
__u8
cmd
[
2
];
cmd
[
0
]
=
WIIPROTO_REQ_IR2
;
cmd
[
1
]
=
flags
;
wiiproto_keep_rumble
(
wdata
,
&
cmd
[
1
]);
wiimote_queue
(
wdata
,
cmd
,
sizeof
(
cmd
));
}
#define wiiproto_req_wreg(wdata, os, buf, sz) \
wiiproto_req_wmem((wdata), false, (os), (buf), (sz))
#define wiiproto_req_weeprom(wdata, os, buf, sz) \
wiiproto_req_wmem((wdata), true, (os), (buf), (sz))
static
void
wiiproto_req_wmem
(
struct
wiimote_data
*
wdata
,
bool
eeprom
,
__u32
offset
,
const
__u8
*
buf
,
__u8
size
)
{
__u8
cmd
[
22
];
if
(
size
>
16
||
size
==
0
)
{
hid_warn
(
wdata
->
hdev
,
"Invalid length %d wmem request
\n
"
,
size
);
return
;
}
memset
(
cmd
,
0
,
sizeof
(
cmd
));
cmd
[
0
]
=
WIIPROTO_REQ_WMEM
;
cmd
[
2
]
=
(
offset
>>
16
)
&
0xff
;
cmd
[
3
]
=
(
offset
>>
8
)
&
0xff
;
cmd
[
4
]
=
offset
&
0xff
;
cmd
[
5
]
=
size
;
memcpy
(
&
cmd
[
6
],
buf
,
size
);
if
(
!
eeprom
)
cmd
[
1
]
|=
0x04
;
wiiproto_keep_rumble
(
wdata
,
&
cmd
[
1
]);
wiimote_queue
(
wdata
,
cmd
,
sizeof
(
cmd
));
}
/* requries the cmd-mutex to be held */
static
int
wiimote_cmd_write
(
struct
wiimote_data
*
wdata
,
__u32
offset
,
const
__u8
*
wmem
,
__u8
size
)
{
unsigned
long
flags
;
int
ret
;
spin_lock_irqsave
(
&
wdata
->
state
.
lock
,
flags
);
wiimote_cmd_set
(
wdata
,
WIIPROTO_REQ_WMEM
,
0
);
wiiproto_req_wreg
(
wdata
,
offset
,
wmem
,
size
);
spin_unlock_irqrestore
(
&
wdata
->
state
.
lock
,
flags
);
ret
=
wiimote_cmd_wait
(
wdata
);
if
(
!
ret
&&
wdata
->
state
.
cmd_err
)
ret
=
-
EIO
;
return
ret
;
}
static
int
wiimote_battery_get_property
(
struct
power_supply
*
psy
,
enum
power_supply_property
psp
,
union
power_supply_propval
*
val
)
{
struct
wiimote_data
*
wdata
=
container_of
(
psy
,
struct
wiimote_data
,
battery
);
int
ret
=
0
,
state
;
unsigned
long
flags
;
ret
=
wiimote_cmd_acquire
(
wdata
);
if
(
ret
)
return
ret
;
spin_lock_irqsave
(
&
wdata
->
state
.
lock
,
flags
);
wiimote_cmd_set
(
wdata
,
WIIPROTO_REQ_SREQ
,
0
);
wiiproto_req_status
(
wdata
);
spin_unlock_irqrestore
(
&
wdata
->
state
.
lock
,
flags
);
ret
=
wiimote_cmd_wait
(
wdata
);
state
=
wdata
->
state
.
cmd_battery
;
wiimote_cmd_release
(
wdata
);
if
(
ret
)
return
ret
;
switch
(
psp
)
{
case
POWER_SUPPLY_PROP_CAPACITY
:
val
->
intval
=
state
*
100
/
255
;
break
;
default:
ret
=
-
EINVAL
;
break
;
}
return
ret
;
}
static
int
wiimote_init_ir
(
struct
wiimote_data
*
wdata
,
__u16
mode
)
{
int
ret
;
unsigned
long
flags
;
__u8
format
=
0
;
static
const
__u8
data_enable
[]
=
{
0x01
};
static
const
__u8
data_sens1
[]
=
{
0x02
,
0x00
,
0x00
,
0x71
,
0x01
,
0x00
,
0xaa
,
0x00
,
0x64
};
static
const
__u8
data_sens2
[]
=
{
0x63
,
0x03
};
static
const
__u8
data_fin
[]
=
{
0x08
};
spin_lock_irqsave
(
&
wdata
->
state
.
lock
,
flags
);
if
(
mode
==
(
wdata
->
state
.
flags
&
WIIPROTO_FLAGS_IR
))
{
spin_unlock_irqrestore
(
&
wdata
->
state
.
lock
,
flags
);
return
0
;
}
if
(
mode
==
0
)
{
wdata
->
state
.
flags
&=
~
WIIPROTO_FLAGS_IR
;
wiiproto_req_ir1
(
wdata
,
0
);
wiiproto_req_ir2
(
wdata
,
0
);
wiiproto_req_drm
(
wdata
,
WIIPROTO_REQ_NULL
);
spin_unlock_irqrestore
(
&
wdata
->
state
.
lock
,
flags
);
return
0
;
}
spin_unlock_irqrestore
(
&
wdata
->
state
.
lock
,
flags
);
ret
=
wiimote_cmd_acquire
(
wdata
);
if
(
ret
)
return
ret
;
/* send PIXEL CLOCK ENABLE cmd first */
spin_lock_irqsave
(
&
wdata
->
state
.
lock
,
flags
);
wiimote_cmd_set
(
wdata
,
WIIPROTO_REQ_IR1
,
0
);
wiiproto_req_ir1
(
wdata
,
0x06
);
spin_unlock_irqrestore
(
&
wdata
->
state
.
lock
,
flags
);
ret
=
wiimote_cmd_wait
(
wdata
);
if
(
ret
)
goto
unlock
;
if
(
wdata
->
state
.
cmd_err
)
{
ret
=
-
EIO
;
goto
unlock
;
}
/* enable IR LOGIC */
spin_lock_irqsave
(
&
wdata
->
state
.
lock
,
flags
);
wiimote_cmd_set
(
wdata
,
WIIPROTO_REQ_IR2
,
0
);
wiiproto_req_ir2
(
wdata
,
0x06
);
spin_unlock_irqrestore
(
&
wdata
->
state
.
lock
,
flags
);
ret
=
wiimote_cmd_wait
(
wdata
);
if
(
ret
)
goto
unlock
;
if
(
wdata
->
state
.
cmd_err
)
{
ret
=
-
EIO
;
goto
unlock
;
}
/* enable IR cam but do not make it send data, yet */
ret
=
wiimote_cmd_write
(
wdata
,
0xb00030
,
data_enable
,
sizeof
(
data_enable
));
if
(
ret
)
goto
unlock
;
/* write first sensitivity block */
ret
=
wiimote_cmd_write
(
wdata
,
0xb00000
,
data_sens1
,
sizeof
(
data_sens1
));
if
(
ret
)
goto
unlock
;
/* write second sensitivity block */
ret
=
wiimote_cmd_write
(
wdata
,
0xb0001a
,
data_sens2
,
sizeof
(
data_sens2
));
if
(
ret
)
goto
unlock
;
/* put IR cam into desired state */
switch
(
mode
)
{
case
WIIPROTO_FLAG_IR_FULL
:
format
=
5
;
break
;
case
WIIPROTO_FLAG_IR_EXT
:
format
=
3
;
break
;
case
WIIPROTO_FLAG_IR_BASIC
:
format
=
1
;
break
;
}
ret
=
wiimote_cmd_write
(
wdata
,
0xb00033
,
&
format
,
sizeof
(
format
));
if
(
ret
)
goto
unlock
;
/* make IR cam send data */
ret
=
wiimote_cmd_write
(
wdata
,
0xb00030
,
data_fin
,
sizeof
(
data_fin
));
if
(
ret
)
goto
unlock
;
/* request new DRM mode compatible to IR mode */
spin_lock_irqsave
(
&
wdata
->
state
.
lock
,
flags
);
wdata
->
state
.
flags
&=
~
WIIPROTO_FLAGS_IR
;
wdata
->
state
.
flags
|=
mode
&
WIIPROTO_FLAGS_IR
;
wiiproto_req_drm
(
wdata
,
WIIPROTO_REQ_NULL
);
spin_unlock_irqrestore
(
&
wdata
->
state
.
lock
,
flags
);
unlock:
wiimote_cmd_release
(
wdata
);
return
ret
;
}
static
enum
led_brightness
wiimote_leds_get
(
struct
led_classdev
*
led_dev
)
{
struct
wiimote_data
*
wdata
;
...
...
@@ -268,9 +656,28 @@ static void wiimote_leds_set(struct led_classdev *led_dev,
}
}
static
int
wiimote_
input_event
(
struct
input_dev
*
dev
,
unsigned
int
type
,
unsigned
int
code
,
int
value
)
static
int
wiimote_
ff_play
(
struct
input_dev
*
dev
,
void
*
data
,
struct
ff_effect
*
eff
)
{
struct
wiimote_data
*
wdata
=
input_get_drvdata
(
dev
);
__u8
value
;
unsigned
long
flags
;
/*
* The wiimote supports only a single rumble motor so if any magnitude
* is set to non-zero then we start the rumble motor. If both are set to
* zero, we stop the rumble motor.
*/
if
(
eff
->
u
.
rumble
.
strong_magnitude
||
eff
->
u
.
rumble
.
weak_magnitude
)
value
=
1
;
else
value
=
0
;
spin_lock_irqsave
(
&
wdata
->
state
.
lock
,
flags
);
wiiproto_req_rumble
(
wdata
,
value
);
spin_unlock_irqrestore
(
&
wdata
->
state
.
lock
,
flags
);
return
0
;
}
...
...
@@ -288,6 +695,61 @@ static void wiimote_input_close(struct input_dev *dev)
hid_hw_close
(
wdata
->
hdev
);
}
static
int
wiimote_accel_open
(
struct
input_dev
*
dev
)
{
struct
wiimote_data
*
wdata
=
input_get_drvdata
(
dev
);
int
ret
;
unsigned
long
flags
;
ret
=
hid_hw_open
(
wdata
->
hdev
);
if
(
ret
)
return
ret
;
spin_lock_irqsave
(
&
wdata
->
state
.
lock
,
flags
);
wiiproto_req_accel
(
wdata
,
true
);
spin_unlock_irqrestore
(
&
wdata
->
state
.
lock
,
flags
);
return
0
;
}
static
void
wiimote_accel_close
(
struct
input_dev
*
dev
)
{
struct
wiimote_data
*
wdata
=
input_get_drvdata
(
dev
);
unsigned
long
flags
;
spin_lock_irqsave
(
&
wdata
->
state
.
lock
,
flags
);
wiiproto_req_accel
(
wdata
,
false
);
spin_unlock_irqrestore
(
&
wdata
->
state
.
lock
,
flags
);
hid_hw_close
(
wdata
->
hdev
);
}
static
int
wiimote_ir_open
(
struct
input_dev
*
dev
)
{
struct
wiimote_data
*
wdata
=
input_get_drvdata
(
dev
);
int
ret
;
ret
=
hid_hw_open
(
wdata
->
hdev
);
if
(
ret
)
return
ret
;
ret
=
wiimote_init_ir
(
wdata
,
WIIPROTO_FLAG_IR_BASIC
);
if
(
ret
)
{
hid_hw_close
(
wdata
->
hdev
);
return
ret
;
}
return
0
;
}
static
void
wiimote_ir_close
(
struct
input_dev
*
dev
)
{
struct
wiimote_data
*
wdata
=
input_get_drvdata
(
dev
);
wiimote_init_ir
(
wdata
,
0
);
hid_hw_close
(
wdata
->
hdev
);
}
static
void
handler_keys
(
struct
wiimote_data
*
wdata
,
const
__u8
*
payload
)
{
input_report_key
(
wdata
->
input
,
wiiproto_keymap
[
WIIPROTO_KEY_LEFT
],
...
...
@@ -315,12 +777,100 @@ static void handler_keys(struct wiimote_data *wdata, const __u8 *payload)
input_sync
(
wdata
->
input
);
}
static
void
handler_accel
(
struct
wiimote_data
*
wdata
,
const
__u8
*
payload
)
{
__u16
x
,
y
,
z
;
if
(
!
(
wdata
->
state
.
flags
&
WIIPROTO_FLAG_ACCEL
))
return
;
/*
* payload is: BB BB XX YY ZZ
* Accelerometer data is encoded into 3 10bit values. XX, YY and ZZ
* contain the upper 8 bits of each value. The lower 2 bits are
* contained in the buttons data BB BB.
* Bits 6 and 7 of the first buttons byte BB is the lower 2 bits of the
* X accel value. Bit 5 of the second buttons byte is the 2nd bit of Y
* accel value and bit 6 is the second bit of the Z value.
* The first bit of Y and Z values is not available and always set to 0.
* 0x200 is returned on no movement.
*/
x
=
payload
[
2
]
<<
2
;
y
=
payload
[
3
]
<<
2
;
z
=
payload
[
4
]
<<
2
;
x
|=
(
payload
[
0
]
>>
5
)
&
0x3
;
y
|=
(
payload
[
1
]
>>
4
)
&
0x2
;
z
|=
(
payload
[
1
]
>>
5
)
&
0x2
;
input_report_abs
(
wdata
->
accel
,
ABS_RX
,
x
-
0x200
);
input_report_abs
(
wdata
->
accel
,
ABS_RY
,
y
-
0x200
);
input_report_abs
(
wdata
->
accel
,
ABS_RZ
,
z
-
0x200
);
input_sync
(
wdata
->
accel
);
}
#define ir_to_input0(wdata, ir, packed) __ir_to_input((wdata), (ir), (packed), \
ABS_HAT0X, ABS_HAT0Y)
#define ir_to_input1(wdata, ir, packed) __ir_to_input((wdata), (ir), (packed), \
ABS_HAT1X, ABS_HAT1Y)
#define ir_to_input2(wdata, ir, packed) __ir_to_input((wdata), (ir), (packed), \
ABS_HAT2X, ABS_HAT2Y)
#define ir_to_input3(wdata, ir, packed) __ir_to_input((wdata), (ir), (packed), \
ABS_HAT3X, ABS_HAT3Y)
static
void
__ir_to_input
(
struct
wiimote_data
*
wdata
,
const
__u8
*
ir
,
bool
packed
,
__u8
xid
,
__u8
yid
)
{
__u16
x
,
y
;
if
(
!
(
wdata
->
state
.
flags
&
WIIPROTO_FLAGS_IR
))
return
;
/*
* Basic IR data is encoded into 3 bytes. The first two bytes are the
* upper 8 bit of the X/Y data, the 3rd byte contains the lower 2 bits
* of both.
* If data is packed, then the 3rd byte is put first and slightly
* reordered. This allows to interleave packed and non-packed data to
* have two IR sets in 5 bytes instead of 6.
* The resulting 10bit X/Y values are passed to the ABS_HATXY input dev.
*/
if
(
packed
)
{
x
=
ir
[
1
]
<<
2
;
y
=
ir
[
2
]
<<
2
;
x
|=
ir
[
0
]
&
0x3
;
y
|=
(
ir
[
0
]
>>
2
)
&
0x3
;
}
else
{
x
=
ir
[
0
]
<<
2
;
y
=
ir
[
1
]
<<
2
;
x
|=
(
ir
[
2
]
>>
4
)
&
0x3
;
y
|=
(
ir
[
2
]
>>
6
)
&
0x3
;
}
input_report_abs
(
wdata
->
ir
,
xid
,
x
);
input_report_abs
(
wdata
->
ir
,
yid
,
y
);
}
static
void
handler_status
(
struct
wiimote_data
*
wdata
,
const
__u8
*
payload
)
{
handler_keys
(
wdata
,
payload
);
/* on status reports the drm is reset so we need to resend the drm */
wiiproto_req_drm
(
wdata
,
WIIPROTO_REQ_NULL
);
if
(
wiimote_cmd_pending
(
wdata
,
WIIPROTO_REQ_SREQ
,
0
))
{
wdata
->
state
.
cmd_battery
=
payload
[
5
];
wiimote_cmd_complete
(
wdata
);
}
}
static
void
handler_data
(
struct
wiimote_data
*
wdata
,
const
__u8
*
payload
)
{
handler_keys
(
wdata
,
payload
);
}
static
void
handler_return
(
struct
wiimote_data
*
wdata
,
const
__u8
*
payload
)
...
...
@@ -330,9 +880,105 @@ static void handler_return(struct wiimote_data *wdata, const __u8 *payload)
handler_keys
(
wdata
,
payload
);
if
(
err
)
if
(
wiimote_cmd_pending
(
wdata
,
cmd
,
0
))
{
wdata
->
state
.
cmd_err
=
err
;
wiimote_cmd_complete
(
wdata
);
}
else
if
(
err
)
{
hid_warn
(
wdata
->
hdev
,
"Remote error %hhu on req %hhu
\n
"
,
err
,
cmd
);
}
}
static
void
handler_drm_KA
(
struct
wiimote_data
*
wdata
,
const
__u8
*
payload
)
{
handler_keys
(
wdata
,
payload
);
handler_accel
(
wdata
,
payload
);
}
static
void
handler_drm_KE
(
struct
wiimote_data
*
wdata
,
const
__u8
*
payload
)
{
handler_keys
(
wdata
,
payload
);
}
static
void
handler_drm_KAI
(
struct
wiimote_data
*
wdata
,
const
__u8
*
payload
)
{
handler_keys
(
wdata
,
payload
);
handler_accel
(
wdata
,
payload
);
ir_to_input0
(
wdata
,
&
payload
[
5
],
false
);
ir_to_input1
(
wdata
,
&
payload
[
8
],
false
);
ir_to_input2
(
wdata
,
&
payload
[
11
],
false
);
ir_to_input3
(
wdata
,
&
payload
[
14
],
false
);
input_sync
(
wdata
->
ir
);
}
static
void
handler_drm_KEE
(
struct
wiimote_data
*
wdata
,
const
__u8
*
payload
)
{
handler_keys
(
wdata
,
payload
);
}
static
void
handler_drm_KIE
(
struct
wiimote_data
*
wdata
,
const
__u8
*
payload
)
{
handler_keys
(
wdata
,
payload
);
ir_to_input0
(
wdata
,
&
payload
[
2
],
false
);
ir_to_input1
(
wdata
,
&
payload
[
4
],
true
);
ir_to_input2
(
wdata
,
&
payload
[
7
],
false
);
ir_to_input3
(
wdata
,
&
payload
[
9
],
true
);
input_sync
(
wdata
->
ir
);
}
static
void
handler_drm_KAE
(
struct
wiimote_data
*
wdata
,
const
__u8
*
payload
)
{
handler_keys
(
wdata
,
payload
);
handler_accel
(
wdata
,
payload
);
}
static
void
handler_drm_KAIE
(
struct
wiimote_data
*
wdata
,
const
__u8
*
payload
)
{
handler_keys
(
wdata
,
payload
);
handler_accel
(
wdata
,
payload
);
ir_to_input0
(
wdata
,
&
payload
[
5
],
false
);
ir_to_input1
(
wdata
,
&
payload
[
7
],
true
);
ir_to_input2
(
wdata
,
&
payload
[
10
],
false
);
ir_to_input3
(
wdata
,
&
payload
[
12
],
true
);
input_sync
(
wdata
->
ir
);
}
static
void
handler_drm_E
(
struct
wiimote_data
*
wdata
,
const
__u8
*
payload
)
{
}
static
void
handler_drm_SKAI1
(
struct
wiimote_data
*
wdata
,
const
__u8
*
payload
)
{
handler_keys
(
wdata
,
payload
);
wdata
->
state
.
accel_split
[
0
]
=
payload
[
2
];
wdata
->
state
.
accel_split
[
1
]
=
(
payload
[
0
]
>>
1
)
&
(
0x10
|
0x20
);
wdata
->
state
.
accel_split
[
1
]
|=
(
payload
[
1
]
<<
1
)
&
(
0x40
|
0x80
);
ir_to_input0
(
wdata
,
&
payload
[
3
],
false
);
ir_to_input1
(
wdata
,
&
payload
[
12
],
false
);
input_sync
(
wdata
->
ir
);
}
static
void
handler_drm_SKAI2
(
struct
wiimote_data
*
wdata
,
const
__u8
*
payload
)
{
__u8
buf
[
5
];
handler_keys
(
wdata
,
payload
);
wdata
->
state
.
accel_split
[
1
]
|=
(
payload
[
0
]
>>
5
)
&
(
0x01
|
0x02
);
wdata
->
state
.
accel_split
[
1
]
|=
(
payload
[
1
]
>>
3
)
&
(
0x04
|
0x08
);
buf
[
0
]
=
0
;
buf
[
1
]
=
0
;
buf
[
2
]
=
wdata
->
state
.
accel_split
[
0
];
buf
[
3
]
=
payload
[
2
];
buf
[
4
]
=
wdata
->
state
.
accel_split
[
1
];
handler_accel
(
wdata
,
buf
);
ir_to_input2
(
wdata
,
&
payload
[
3
],
false
);
ir_to_input3
(
wdata
,
&
payload
[
12
],
false
);
input_sync
(
wdata
->
ir
);
}
struct
wiiproto_handler
{
...
...
@@ -343,8 +989,19 @@ struct wiiproto_handler {
static
struct
wiiproto_handler
handlers
[]
=
{
{
.
id
=
WIIPROTO_REQ_STATUS
,
.
size
=
6
,
.
func
=
handler_status
},
{
.
id
=
WIIPROTO_REQ_DATA
,
.
size
=
21
,
.
func
=
handler_data
},
{
.
id
=
WIIPROTO_REQ_RETURN
,
.
size
=
4
,
.
func
=
handler_return
},
{
.
id
=
WIIPROTO_REQ_DRM_K
,
.
size
=
2
,
.
func
=
handler_keys
},
{
.
id
=
WIIPROTO_REQ_DRM_KA
,
.
size
=
5
,
.
func
=
handler_drm_KA
},
{
.
id
=
WIIPROTO_REQ_DRM_KE
,
.
size
=
10
,
.
func
=
handler_drm_KE
},
{
.
id
=
WIIPROTO_REQ_DRM_KAI
,
.
size
=
17
,
.
func
=
handler_drm_KAI
},
{
.
id
=
WIIPROTO_REQ_DRM_KEE
,
.
size
=
21
,
.
func
=
handler_drm_KEE
},
{
.
id
=
WIIPROTO_REQ_DRM_KAE
,
.
size
=
21
,
.
func
=
handler_drm_KAE
},
{
.
id
=
WIIPROTO_REQ_DRM_KIE
,
.
size
=
21
,
.
func
=
handler_drm_KIE
},
{
.
id
=
WIIPROTO_REQ_DRM_KAIE
,
.
size
=
21
,
.
func
=
handler_drm_KAIE
},
{
.
id
=
WIIPROTO_REQ_DRM_E
,
.
size
=
21
,
.
func
=
handler_drm_E
},
{
.
id
=
WIIPROTO_REQ_DRM_SKAI1
,
.
size
=
21
,
.
func
=
handler_drm_SKAI1
},
{
.
id
=
WIIPROTO_REQ_DRM_SKAI2
,
.
size
=
21
,
.
func
=
handler_drm_SKAI2
},
{
.
id
=
0
}
};
...
...
@@ -355,6 +1012,7 @@ static int wiimote_hid_event(struct hid_device *hdev, struct hid_report *report,
struct
wiiproto_handler
*
h
;
int
i
;
unsigned
long
flags
;
bool
handled
=
false
;
if
(
size
<
1
)
return
-
EINVAL
;
...
...
@@ -363,9 +1021,15 @@ static int wiimote_hid_event(struct hid_device *hdev, struct hid_report *report,
for
(
i
=
0
;
handlers
[
i
].
id
;
++
i
)
{
h
=
&
handlers
[
i
];
if
(
h
->
id
==
raw_data
[
0
]
&&
h
->
size
<
size
)
if
(
h
->
id
==
raw_data
[
0
]
&&
h
->
size
<
size
)
{
h
->
func
(
wdata
,
&
raw_data
[
1
]);
handled
=
true
;
}
}
if
(
!
handled
)
hid_warn
(
hdev
,
"Unhandled report %hhu size %d
\n
"
,
raw_data
[
0
],
size
);
spin_unlock_irqrestore
(
&
wdata
->
state
.
lock
,
flags
);
...
...
@@ -434,16 +1098,13 @@ static struct wiimote_data *wiimote_create(struct hid_device *hdev)
return
NULL
;
wdata
->
input
=
input_allocate_device
();
if
(
!
wdata
->
input
)
{
kfree
(
wdata
);
return
NULL
;
}
if
(
!
wdata
->
input
)
goto
err
;
wdata
->
hdev
=
hdev
;
hid_set_drvdata
(
hdev
,
wdata
);
input_set_drvdata
(
wdata
->
input
,
wdata
);
wdata
->
input
->
event
=
wiimote_input_event
;
wdata
->
input
->
open
=
wiimote_input_open
;
wdata
->
input
->
close
=
wiimote_input_close
;
wdata
->
input
->
dev
.
parent
=
&
wdata
->
hdev
->
dev
;
...
...
@@ -457,18 +1118,89 @@ static struct wiimote_data *wiimote_create(struct hid_device *hdev)
for
(
i
=
0
;
i
<
WIIPROTO_KEY_COUNT
;
++
i
)
set_bit
(
wiiproto_keymap
[
i
],
wdata
->
input
->
keybit
);
set_bit
(
FF_RUMBLE
,
wdata
->
input
->
ffbit
);
if
(
input_ff_create_memless
(
wdata
->
input
,
NULL
,
wiimote_ff_play
))
goto
err_input
;
wdata
->
accel
=
input_allocate_device
();
if
(
!
wdata
->
accel
)
goto
err_input
;
input_set_drvdata
(
wdata
->
accel
,
wdata
);
wdata
->
accel
->
open
=
wiimote_accel_open
;
wdata
->
accel
->
close
=
wiimote_accel_close
;
wdata
->
accel
->
dev
.
parent
=
&
wdata
->
hdev
->
dev
;
wdata
->
accel
->
id
.
bustype
=
wdata
->
hdev
->
bus
;
wdata
->
accel
->
id
.
vendor
=
wdata
->
hdev
->
vendor
;
wdata
->
accel
->
id
.
product
=
wdata
->
hdev
->
product
;
wdata
->
accel
->
id
.
version
=
wdata
->
hdev
->
version
;
wdata
->
accel
->
name
=
WIIMOTE_NAME
" Accelerometer"
;
set_bit
(
EV_ABS
,
wdata
->
accel
->
evbit
);
set_bit
(
ABS_RX
,
wdata
->
accel
->
absbit
);
set_bit
(
ABS_RY
,
wdata
->
accel
->
absbit
);
set_bit
(
ABS_RZ
,
wdata
->
accel
->
absbit
);
input_set_abs_params
(
wdata
->
accel
,
ABS_RX
,
-
500
,
500
,
2
,
4
);
input_set_abs_params
(
wdata
->
accel
,
ABS_RY
,
-
500
,
500
,
2
,
4
);
input_set_abs_params
(
wdata
->
accel
,
ABS_RZ
,
-
500
,
500
,
2
,
4
);
wdata
->
ir
=
input_allocate_device
();
if
(
!
wdata
->
ir
)
goto
err_ir
;
input_set_drvdata
(
wdata
->
ir
,
wdata
);
wdata
->
ir
->
open
=
wiimote_ir_open
;
wdata
->
ir
->
close
=
wiimote_ir_close
;
wdata
->
ir
->
dev
.
parent
=
&
wdata
->
hdev
->
dev
;
wdata
->
ir
->
id
.
bustype
=
wdata
->
hdev
->
bus
;
wdata
->
ir
->
id
.
vendor
=
wdata
->
hdev
->
vendor
;
wdata
->
ir
->
id
.
product
=
wdata
->
hdev
->
product
;
wdata
->
ir
->
id
.
version
=
wdata
->
hdev
->
version
;
wdata
->
ir
->
name
=
WIIMOTE_NAME
" IR"
;
set_bit
(
EV_ABS
,
wdata
->
ir
->
evbit
);
set_bit
(
ABS_HAT0X
,
wdata
->
ir
->
absbit
);
set_bit
(
ABS_HAT0Y
,
wdata
->
ir
->
absbit
);
set_bit
(
ABS_HAT1X
,
wdata
->
ir
->
absbit
);
set_bit
(
ABS_HAT1Y
,
wdata
->
ir
->
absbit
);
set_bit
(
ABS_HAT2X
,
wdata
->
ir
->
absbit
);
set_bit
(
ABS_HAT2Y
,
wdata
->
ir
->
absbit
);
set_bit
(
ABS_HAT3X
,
wdata
->
ir
->
absbit
);
set_bit
(
ABS_HAT3Y
,
wdata
->
ir
->
absbit
);
input_set_abs_params
(
wdata
->
ir
,
ABS_HAT0X
,
0
,
1023
,
2
,
4
);
input_set_abs_params
(
wdata
->
ir
,
ABS_HAT0Y
,
0
,
767
,
2
,
4
);
input_set_abs_params
(
wdata
->
ir
,
ABS_HAT1X
,
0
,
1023
,
2
,
4
);
input_set_abs_params
(
wdata
->
ir
,
ABS_HAT1Y
,
0
,
767
,
2
,
4
);
input_set_abs_params
(
wdata
->
ir
,
ABS_HAT2X
,
0
,
1023
,
2
,
4
);
input_set_abs_params
(
wdata
->
ir
,
ABS_HAT2Y
,
0
,
767
,
2
,
4
);
input_set_abs_params
(
wdata
->
ir
,
ABS_HAT3X
,
0
,
1023
,
2
,
4
);
input_set_abs_params
(
wdata
->
ir
,
ABS_HAT3Y
,
0
,
767
,
2
,
4
);
spin_lock_init
(
&
wdata
->
qlock
);
INIT_WORK
(
&
wdata
->
worker
,
wiimote_worker
);
spin_lock_init
(
&
wdata
->
state
.
lock
);
init_completion
(
&
wdata
->
state
.
ready
);
mutex_init
(
&
wdata
->
state
.
sync
);
return
wdata
;
err_ir:
input_free_device
(
wdata
->
accel
);
err_input:
input_free_device
(
wdata
->
input
);
err:
kfree
(
wdata
);
return
NULL
;
}
static
void
wiimote_destroy
(
struct
wiimote_data
*
wdata
)
{
wiimote_leds_destroy
(
wdata
);
power_supply_unregister
(
&
wdata
->
battery
);
input_unregister_device
(
wdata
->
accel
);
input_unregister_device
(
wdata
->
ir
);
input_unregister_device
(
wdata
->
input
);
cancel_work_sync
(
&
wdata
->
worker
);
hid_hw_stop
(
wdata
->
hdev
);
...
...
@@ -500,12 +1232,37 @@ static int wiimote_hid_probe(struct hid_device *hdev,
goto
err
;
}
ret
=
input_register_device
(
wdata
->
input
);
ret
=
input_register_device
(
wdata
->
accel
);
if
(
ret
)
{
hid_err
(
hdev
,
"Cannot register input device
\n
"
);
goto
err_stop
;
}
ret
=
input_register_device
(
wdata
->
ir
);
if
(
ret
)
{
hid_err
(
hdev
,
"Cannot register input device
\n
"
);
goto
err_ir
;
}
ret
=
input_register_device
(
wdata
->
input
);
if
(
ret
)
{
hid_err
(
hdev
,
"Cannot register input device
\n
"
);
goto
err_input
;
}
wdata
->
battery
.
properties
=
wiimote_battery_props
;
wdata
->
battery
.
num_properties
=
ARRAY_SIZE
(
wiimote_battery_props
);
wdata
->
battery
.
get_property
=
wiimote_battery_get_property
;
wdata
->
battery
.
name
=
"wiimote_battery"
;
wdata
->
battery
.
type
=
POWER_SUPPLY_TYPE_BATTERY
;
wdata
->
battery
.
use_for_apm
=
0
;
ret
=
power_supply_register
(
&
wdata
->
hdev
->
dev
,
&
wdata
->
battery
);
if
(
ret
)
{
hid_err
(
hdev
,
"Cannot register battery device
\n
"
);
goto
err_battery
;
}
ret
=
wiimote_leds_create
(
wdata
);
if
(
ret
)
goto
err_free
;
...
...
@@ -523,9 +1280,20 @@ static int wiimote_hid_probe(struct hid_device *hdev,
wiimote_destroy
(
wdata
);
return
ret
;
err_battery:
input_unregister_device
(
wdata
->
input
);
wdata
->
input
=
NULL
;
err_input:
input_unregister_device
(
wdata
->
ir
);
wdata
->
ir
=
NULL
;
err_ir:
input_unregister_device
(
wdata
->
accel
);
wdata
->
accel
=
NULL
;
err_stop:
hid_hw_stop
(
hdev
);
err:
input_free_device
(
wdata
->
ir
);
input_free_device
(
wdata
->
accel
);
input_free_device
(
wdata
->
input
);
kfree
(
wdata
);
return
ret
;
...
...
include/linux/hid.h
View file @
b0eae38c
...
...
@@ -312,6 +312,7 @@ struct hid_item {
#define HID_QUIRK_BADPAD 0x00000020
#define HID_QUIRK_MULTI_INPUT 0x00000040
#define HID_QUIRK_HIDINPUT_FORCE 0x00000080
#define HID_QUIRK_MULTITOUCH 0x00000100
#define HID_QUIRK_SKIP_OUTPUT_REPORTS 0x00010000
#define HID_QUIRK_FULLSPEED_INTERVAL 0x10000000
#define HID_QUIRK_NO_INIT_REPORTS 0x20000000
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment