Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
openeuler
raspberrypi-kernel
提交
b0eae38c
R
raspberrypi-kernel
项目概览
openeuler
/
raspberrypi-kernel
通知
13
Star
1
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
R
raspberrypi-kernel
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
提交
b0eae38c
编写于
10月 25, 2011
作者:
J
Jiri Kosina
浏览文件
操作
浏览文件
下载
差异文件
Merge branches 'acrux', 'logitech', 'multitouch', 'roccat' and 'wiimote' into for-linus
上级
3ee72ca9
b55ebc27
e00ddc9b
0db3bfc7
3200a6a5
b22e00f3
变更
20
隐藏空白更改
内联
并排
Showing
20 changed file
with
2474 addition
and
129 deletion
+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
未找到文件。
Documentation/ABI/testing/sysfs-driver-hid-logitech-lg4ff
0 → 100644
浏览文件 @
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
浏览文件 @
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
浏览文件 @
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
浏览文件 @
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
浏览文件 @
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
浏览文件 @
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
浏览文件 @
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
浏览文件 @
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
浏览文件 @
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
浏览文件 @
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
浏览文件 @
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
);
__s32
*
value
=
report
->
field
[
0
]
->
value
;
*
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
]
=
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
);
magnitude
=
magnitude
*
90
/
65535
;
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
浏览文件 @
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
浏览文件 @
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
浏览文件 @
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
浏览文件 @
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
浏览文件 @
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
));
}
mutex_unlock
(
&
kone
->
kone_lock
);
if
(
retval
)
{
mutex_unlock
(
&
kone
->
kone_lock
);
return
retval
;
}
if
(
retval
)
return
retval
;
old_profile
=
kone
->
settings
.
startup_profile
;
memcpy
(
&
kone
->
settings
,
buf
,
sizeof
(
struct
kone_settings
))
;
/*
* 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
;
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
);
mutex_unlock
(
&
kone
->
kone_lock
);
if
(
retval
)
if
(
retval
)
{
mutex_unlock
(
&
kone
->
kone_lock
);
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
浏览文件 @
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
);
if
(
retval
)
{
mutex_unlock
(
&
kovaplus
->
kovaplus_lock
);
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
);
if
(
retval
)
return
retval
;
return
size
;
}
...
...
drivers/hid/hid-roccat-pyra.c
浏览文件 @
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
));
}
mutex_unlock
(
&
pyra
->
pyra_lock
);
if
(
retval
)
{
mutex_unlock
(
&
pyra
->
pyra_lock
);
return
retval
;
}
if
(
retval
)
return
retval
;
memcpy
(
&
pyra
->
settings
,
buf
,
sizeof
(
struct
pyra_settings
))
;
profile_activated
(
pyra
,
pyra
->
settings
.
startup_profile
);
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
浏览文件 @
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
;
...
...
@@ -46,23 +63,47 @@ struct wiimote_data {
struct
wiimote_state
state
;
};
#define WIIPROTO_FLAG_LED1 0x01
#define WIIPROTO_FLAG_LED2 0x02
#define WIIPROTO_FLAG_LED3 0x04
#define WIIPROTO_FLAG_LED4 0x08
#define WIIPROTO_FLAG_LED1 0x01
#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
)
{
return
WIIPROTO_REQ_DRM_K
;
__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,10 +1021,16 @@ 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
);
return
0
;
...
...
@@ -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
浏览文件 @
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
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录