Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
OpenHarmony
kernel_linux
提交
817a3125
K
kernel_linux
项目概览
OpenHarmony
/
kernel_linux
上一次同步 4 年多
通知
15
Star
8
Fork
2
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
K
kernel_linux
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
提交
817a3125
编写于
6月 22, 2015
作者:
J
Jiri Kosina
浏览文件
操作
浏览文件
下载
差异文件
Merge branches 'for-4.1/upstream-fixes', 'for-4.2/upstream' and 'for-4.2/logitech' into for-linus
Conflicts: drivers/hid/hid-logitech-hidpp.c
上级
7250dc3f
74a3e0c7
8a09b4fa
变更
18
显示空白变更内容
内联
并排
Showing
18 changed file
with
589 addition
and
248 deletion
+589
-248
Documentation/ABI/testing/sysfs-driver-hid-logitech-lg4ff
Documentation/ABI/testing/sysfs-driver-hid-logitech-lg4ff
+4
-4
drivers/hid/Makefile
drivers/hid/Makefile
+3
-3
drivers/hid/hid-core.c
drivers/hid/hid-core.c
+9
-22
drivers/hid/hid-cypress.c
drivers/hid/hid-cypress.c
+1
-5
drivers/hid/hid-ids.h
drivers/hid/hid-ids.h
+8
-20
drivers/hid/hid-input.c
drivers/hid/hid-input.c
+2
-1
drivers/hid/hid-lenovo.c
drivers/hid/hid-lenovo.c
+7
-2
drivers/hid/hid-lg.c
drivers/hid/hid-lg.c
+14
-10
drivers/hid/hid-lg4ff.c
drivers/hid/hid-lg4ff.c
+300
-158
drivers/hid/hid-lg4ff.h
drivers/hid/hid-lg4ff.h
+2
-2
drivers/hid/hid-logitech-hidpp.c
drivers/hid/hid-logitech-hidpp.c
+224
-3
drivers/hid/hid-microsoft.c
drivers/hid/hid-microsoft.c
+2
-0
drivers/hid/hid-prodikeys.c
drivers/hid/hid-prodikeys.c
+1
-2
drivers/hid/hid-rmi.c
drivers/hid/hid-rmi.c
+3
-3
drivers/hid/i2c-hid/i2c-hid.c
drivers/hid/i2c-hid/i2c-hid.c
+3
-3
drivers/hid/usbhid/hid-quirks.c
drivers/hid/usbhid/hid-quirks.c
+4
-0
drivers/usb/misc/ldusb.c
drivers/usb/misc/ldusb.c
+0
-10
include/linux/hid.h
include/linux/hid.h
+2
-0
未找到文件。
Documentation/ABI/testing/sysfs-driver-hid-logitech-lg4ff
浏览文件 @
817a3125
What: /sys/
module/hid_logitech/drivers/hid:logitech/<dev>/range.
What: /sys/
bus/hid/drivers/logitech/<dev>/range
Date: July 2011
KernelVersion: 3.2
Contact: Michal Malý <madcatxster@
gmail.com
>
Contact: Michal Malý <madcatxster@
devoid-pointer.net
>
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.
...
...
@@ -9,7 +9,7 @@ Description: Display minimum, maximum and current range of the steering
What: /sys/bus/hid/drivers/logitech/<dev>/alternate_modes
Date: Feb 2015
KernelVersion: 4.1
Contact: Michal Malý <madcatxster@
gmail.com
>
Contact: Michal Malý <madcatxster@
devoid-pointer.net
>
Description: Displays a set of alternate modes supported by a wheel. Each
mode is listed as follows:
Tag: Mode Name
...
...
@@ -45,7 +45,7 @@ Description: Displays a set of alternate modes supported by a wheel. Each
What: /sys/bus/hid/drivers/logitech/<dev>/real_id
Date: Feb 2015
KernelVersion: 4.1
Contact: Michal Malý <madcatxster@
gmail.com
>
Contact: Michal Malý <madcatxster@
devoid-pointer.net
>
Description: Displays the real model of the wheel regardless of any
alternate mode the wheel might be switched to.
It is a read-only value.
...
...
drivers/hid/Makefile
浏览文件 @
817a3125
drivers/hid/hid-core.c
浏览文件 @
817a3125
...
...
@@ -706,7 +706,8 @@ static void hid_scan_collection(struct hid_parser *parser, unsigned type)
if
(
hid
->
vendor
==
USB_VENDOR_ID_MICROSOFT
&&
(
hid
->
product
==
USB_DEVICE_ID_MS_TYPE_COVER_3
||
hid
->
product
==
USB_DEVICE_ID_MS_TYPE_COVER_3_JP
)
&&
hid
->
product
==
USB_DEVICE_ID_MS_TYPE_COVER_3_JP
||
hid
->
product
==
USB_DEVICE_ID_MS_POWER_COVER
)
&&
hid
->
group
==
HID_GROUP_MULTITOUCH
)
hid
->
group
=
HID_GROUP_GENERIC
;
...
...
@@ -1061,13 +1062,13 @@ static u32 s32ton(__s32 value, unsigned n)
* Search linux-kernel and linux-usb-devel archives for "hid-core extract".
*/
static
__u32
extract
(
const
struct
hid_device
*
hid
,
__u8
*
report
,
__u32
hid_field_
extract
(
const
struct
hid_device
*
hid
,
__u8
*
report
,
unsigned
offset
,
unsigned
n
)
{
u64
x
;
if
(
n
>
32
)
hid_warn
(
hid
,
"extract() called with n (%d) > 32! (%s)
\n
"
,
hid_warn
(
hid
,
"
hid_field_
extract() called with n (%d) > 32! (%s)
\n
"
,
n
,
current
->
comm
);
report
+=
offset
>>
3
;
/* adjust byte index */
...
...
@@ -1076,6 +1077,7 @@ static __u32 extract(const struct hid_device *hid, __u8 *report,
x
=
(
x
>>
offset
)
&
((
1ULL
<<
n
)
-
1
);
/* extract bit field */
return
(
u32
)
x
;
}
EXPORT_SYMBOL_GPL
(
hid_field_extract
);
/*
* "implement" : set bits in a little endian bit stream.
...
...
@@ -1221,9 +1223,9 @@ static void hid_input_field(struct hid_device *hid, struct hid_field *field,
for
(
n
=
0
;
n
<
count
;
n
++
)
{
value
[
n
]
=
min
<
0
?
snto32
(
extract
(
hid
,
data
,
offset
+
n
*
size
,
size
)
,
size
)
:
extract
(
hid
,
data
,
offset
+
n
*
size
,
size
);
snto32
(
hid_field_extract
(
hid
,
data
,
offset
+
n
*
size
,
size
)
,
size
)
:
hid_field_
extract
(
hid
,
data
,
offset
+
n
*
size
,
size
);
/* Ignore report if ErrorRollOver */
if
(
!
(
field
->
flags
&
HID_MAIN_ITEM_VARIABLE
)
&&
...
...
@@ -1901,6 +1903,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
{
HID_USB_DEVICE
(
USB_VENDOR_ID_MICROSOFT
,
USB_DEVICE_ID_MS_OFFICE_KB
)
},
{
HID_USB_DEVICE
(
USB_VENDOR_ID_MICROSOFT
,
USB_DEVICE_ID_MS_TYPE_COVER_3
)
},
{
HID_USB_DEVICE
(
USB_VENDOR_ID_MICROSOFT
,
USB_DEVICE_ID_MS_TYPE_COVER_3_JP
)
},
{
HID_USB_DEVICE
(
USB_VENDOR_ID_MICROSOFT
,
USB_DEVICE_ID_MS_POWER_COVER
)
},
{
HID_USB_DEVICE
(
USB_VENDOR_ID_MONTEREY
,
USB_DEVICE_ID_GENIUS_KB29E
)
},
{
HID_USB_DEVICE
(
USB_VENDOR_ID_MSI
,
USB_DEVICE_ID_MSI_GT683R_LED_PANEL
)
},
{
HID_USB_DEVICE
(
USB_VENDOR_ID_NTRIG
,
USB_DEVICE_ID_NTRIG_TOUCH_SCREEN
)
},
...
...
@@ -2265,14 +2268,6 @@ static const struct hid_device_id hid_ignore_list[] = {
{
HID_USB_DEVICE
(
USB_VENDOR_ID_GENERAL_TOUCH
,
0x0001
)
},
{
HID_USB_DEVICE
(
USB_VENDOR_ID_GENERAL_TOUCH
,
0x0002
)
},
{
HID_USB_DEVICE
(
USB_VENDOR_ID_GENERAL_TOUCH
,
0x0004
)
},
{
HID_USB_DEVICE
(
USB_VENDOR_ID_GLAB
,
USB_DEVICE_ID_4_PHIDGETSERVO_30
)
},
{
HID_USB_DEVICE
(
USB_VENDOR_ID_GLAB
,
USB_DEVICE_ID_1_PHIDGETSERVO_30
)
},
{
HID_USB_DEVICE
(
USB_VENDOR_ID_GLAB
,
USB_DEVICE_ID_0_0_4_IF_KIT
)
},
{
HID_USB_DEVICE
(
USB_VENDOR_ID_GLAB
,
USB_DEVICE_ID_0_16_16_IF_KIT
)
},
{
HID_USB_DEVICE
(
USB_VENDOR_ID_GLAB
,
USB_DEVICE_ID_8_8_8_IF_KIT
)
},
{
HID_USB_DEVICE
(
USB_VENDOR_ID_GLAB
,
USB_DEVICE_ID_0_8_7_IF_KIT
)
},
{
HID_USB_DEVICE
(
USB_VENDOR_ID_GLAB
,
USB_DEVICE_ID_0_8_8_IF_KIT
)
},
{
HID_USB_DEVICE
(
USB_VENDOR_ID_GLAB
,
USB_DEVICE_ID_PHIDGET_MOTORCONTROL
)
},
{
HID_USB_DEVICE
(
USB_VENDOR_ID_GOTOP
,
USB_DEVICE_ID_SUPER_Q2
)
},
{
HID_USB_DEVICE
(
USB_VENDOR_ID_GOTOP
,
USB_DEVICE_ID_GOGOPEN
)
},
{
HID_USB_DEVICE
(
USB_VENDOR_ID_GOTOP
,
USB_DEVICE_ID_PENPOWER
)
},
...
...
@@ -2399,14 +2394,6 @@ static const struct hid_device_id hid_ignore_list[] = {
{
HID_USB_DEVICE
(
USB_VENDOR_ID_SYNAPTICS
,
USB_DEVICE_ID_SYNAPTICS_WTP
)
},
{
HID_USB_DEVICE
(
USB_VENDOR_ID_SYNAPTICS
,
USB_DEVICE_ID_SYNAPTICS_DPAD
)
},
#endif
{
HID_USB_DEVICE
(
USB_VENDOR_ID_VERNIER
,
USB_DEVICE_ID_VERNIER_LABPRO
)
},
{
HID_USB_DEVICE
(
USB_VENDOR_ID_VERNIER
,
USB_DEVICE_ID_VERNIER_GOTEMP
)
},
{
HID_USB_DEVICE
(
USB_VENDOR_ID_VERNIER
,
USB_DEVICE_ID_VERNIER_SKIP
)
},
{
HID_USB_DEVICE
(
USB_VENDOR_ID_VERNIER
,
USB_DEVICE_ID_VERNIER_CYCLOPS
)
},
{
HID_USB_DEVICE
(
USB_VENDOR_ID_VERNIER
,
USB_DEVICE_ID_VERNIER_LCSPEC
)
},
{
HID_USB_DEVICE
(
USB_VENDOR_ID_WISEGROUP
,
USB_DEVICE_ID_4_PHIDGETSERVO_20
)
},
{
HID_USB_DEVICE
(
USB_VENDOR_ID_WISEGROUP
,
USB_DEVICE_ID_1_PHIDGETSERVO_20
)
},
{
HID_USB_DEVICE
(
USB_VENDOR_ID_WISEGROUP
,
USB_DEVICE_ID_8_8_4_IF_KIT
)
},
{
HID_USB_DEVICE
(
USB_VENDOR_ID_YEALINK
,
USB_DEVICE_ID_YEALINK_P1K_P4K_B2K
)
},
{
HID_USB_DEVICE
(
USB_VENDOR_ID_RISO_KAGAKU
,
USB_DEVICE_ID_RI_KA_WEBMAIL
)
},
{
}
...
...
drivers/hid/hid-cypress.c
浏览文件 @
817a3125
...
...
@@ -41,13 +41,9 @@ static __u8 *cp_report_fixup(struct hid_device *hdev, __u8 *rdesc,
for
(
i
=
0
;
i
<
*
rsize
-
4
;
i
++
)
if
(
rdesc
[
i
]
==
0x29
&&
rdesc
[
i
+
2
]
==
0x19
)
{
__u8
tmp
;
rdesc
[
i
]
=
0x19
;
rdesc
[
i
+
2
]
=
0x29
;
tmp
=
rdesc
[
i
+
3
];
rdesc
[
i
+
3
]
=
rdesc
[
i
+
1
];
rdesc
[
i
+
1
]
=
tmp
;
swap
(
rdesc
[
i
+
3
],
rdesc
[
i
+
1
]);
}
return
rdesc
;
}
...
...
drivers/hid/hid-ids.h
浏览文件 @
817a3125
...
...
@@ -364,16 +364,6 @@
#define USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PIT_010A 0x010a
#define USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PIT_E100 0xe100
#define USB_VENDOR_ID_GLAB 0x06c2
#define USB_DEVICE_ID_4_PHIDGETSERVO_30 0x0038
#define USB_DEVICE_ID_1_PHIDGETSERVO_30 0x0039
#define USB_DEVICE_ID_0_0_4_IF_KIT 0x0040
#define USB_DEVICE_ID_0_16_16_IF_KIT 0x0044
#define USB_DEVICE_ID_8_8_8_IF_KIT 0x0045
#define USB_DEVICE_ID_0_8_7_IF_KIT 0x0051
#define USB_DEVICE_ID_0_8_8_IF_KIT 0x0053
#define USB_DEVICE_ID_PHIDGET_MOTORCONTROL 0x0058
#define USB_VENDOR_ID_GOODTOUCH 0x1aad
#define USB_DEVICE_ID_GOODTOUCH_000f 0x000f
...
...
@@ -674,6 +664,7 @@
#define USB_DEVICE_ID_MS_TYPE_COVER_2 0x07a9
#define USB_DEVICE_ID_MS_TYPE_COVER_3 0x07dc
#define USB_DEVICE_ID_MS_TYPE_COVER_3_JP 0x07dd
#define USB_DEVICE_ID_MS_POWER_COVER 0x07da
#define USB_VENDOR_ID_MOJO 0x8282
#define USB_DEVICE_ID_RETRO_ADAPTER 0x3201
...
...
@@ -959,13 +950,6 @@
#define USB_DEVICE_ID_VELLEMAN_K8061_FIRST 0x8061
#define USB_DEVICE_ID_VELLEMAN_K8061_LAST 0x8068
#define USB_VENDOR_ID_VERNIER 0x08f7
#define USB_DEVICE_ID_VERNIER_LABPRO 0x0001
#define USB_DEVICE_ID_VERNIER_GOTEMP 0x0002
#define USB_DEVICE_ID_VERNIER_SKIP 0x0003
#define USB_DEVICE_ID_VERNIER_CYCLOPS 0x0004
#define USB_DEVICE_ID_VERNIER_LCSPEC 0x0006
#define USB_VENDOR_ID_VTL 0x0306
#define USB_DEVICE_ID_VTL_MULTITOUCH_FF3F 0xff3f
...
...
@@ -984,9 +968,6 @@
#define USB_VENDOR_ID_WISEGROUP 0x0925
#define USB_DEVICE_ID_SMARTJOY_PLUS 0x0005
#define USB_DEVICE_ID_1_PHIDGETSERVO_20 0x8101
#define USB_DEVICE_ID_4_PHIDGETSERVO_20 0x8104
#define USB_DEVICE_ID_8_8_4_IF_KIT 0x8201
#define USB_DEVICE_ID_SUPER_JOY_BOX_3 0x8888
#define USB_DEVICE_ID_QUAD_USB_JOYPAD 0x8800
#define USB_DEVICE_ID_DUAL_USB_JOYPAD 0x8866
...
...
@@ -1040,4 +1021,11 @@
#define USB_VENDOR_ID_RISO_KAGAKU 0x1294
/* Riso Kagaku Corp. */
#define USB_DEVICE_ID_RI_KA_WEBMAIL 0x1320
/* Webmail Notifier */
#define USB_VENDOR_ID_MULTIPLE_1781 0x1781
#define USB_DEVICE_ID_RAPHNET_4NES4SNES_OLD 0x0a8d
#define USB_VENDOR_ID_DRACAL_RAPHNET 0x289b
#define USB_DEVICE_ID_RAPHNET_2NES2SNES 0x0002
#define USB_DEVICE_ID_RAPHNET_4NES4SNES 0x0003
#endif
drivers/hid/hid-input.c
浏览文件 @
817a3125
...
...
@@ -1157,7 +1157,8 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct
return
;
/* report the usage code as scancode if the key status has changed */
if
(
usage
->
type
==
EV_KEY
&&
!!
test_bit
(
usage
->
code
,
input
->
key
)
!=
value
)
if
(
usage
->
type
==
EV_KEY
&&
(
!
test_bit
(
usage
->
code
,
input
->
key
))
==
value
)
input_event
(
input
,
EV_MSC
,
MSC_SCAN
,
usage
->
hid
);
input_event
(
input
,
usage
->
type
,
usage
->
code
,
value
);
...
...
drivers/hid/hid-lenovo.c
浏览文件 @
817a3125
...
...
@@ -599,7 +599,8 @@ static int lenovo_probe_tpkbd(struct hid_device *hdev)
GFP_KERNEL
);
if
(
data_pointer
==
NULL
)
{
hid_err
(
hdev
,
"Could not allocate memory for driver data
\n
"
);
return
-
ENOMEM
;
ret
=
-
ENOMEM
;
goto
err
;
}
// set same default values as windows driver
...
...
@@ -610,7 +611,8 @@ static int lenovo_probe_tpkbd(struct hid_device *hdev)
name_micmute
=
devm_kzalloc
(
&
hdev
->
dev
,
name_sz
,
GFP_KERNEL
);
if
(
name_mute
==
NULL
||
name_micmute
==
NULL
)
{
hid_err
(
hdev
,
"Could not allocate memory for led data
\n
"
);
return
-
ENOMEM
;
ret
=
-
ENOMEM
;
goto
err
;
}
snprintf
(
name_mute
,
name_sz
,
"%s:amber:mute"
,
dev_name
(
dev
));
snprintf
(
name_micmute
,
name_sz
,
"%s:amber:micmute"
,
dev_name
(
dev
));
...
...
@@ -634,6 +636,9 @@ static int lenovo_probe_tpkbd(struct hid_device *hdev)
lenovo_features_set_tpkbd
(
hdev
);
return
0
;
err:
sysfs_remove_group
(
&
hdev
->
dev
.
kobj
,
&
lenovo_attr_group_tpkbd
);
return
ret
;
}
static
int
lenovo_probe_cptkbd
(
struct
hid_device
*
hdev
)
...
...
drivers/hid/hid-lg.c
浏览文件 @
817a3125
...
...
@@ -700,7 +700,8 @@ static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id)
/* insert a little delay of 10 jiffies ~ 40ms */
wait_queue_head_t
wait
;
init_waitqueue_head
(
&
wait
);
wait_event_interruptible_timeout
(
wait
,
0
,
10
);
wait_event_interruptible_timeout
(
wait
,
0
,
msecs_to_jiffies
(
40
));
/* Select random Address */
buf
[
1
]
=
0xB2
;
...
...
@@ -712,13 +713,16 @@ static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id)
}
if
(
drv_data
->
quirks
&
LG_FF
)
lgff_init
(
hdev
);
if
(
drv_data
->
quirks
&
LG_FF2
)
lg2ff_init
(
hdev
);
if
(
drv_data
->
quirks
&
LG_FF3
)
lg3ff_init
(
hdev
);
if
(
drv_data
->
quirks
&
LG_FF4
)
lg4ff_init
(
hdev
);
ret
=
lgff_init
(
hdev
);
else
if
(
drv_data
->
quirks
&
LG_FF2
)
ret
=
lg2ff_init
(
hdev
);
else
if
(
drv_data
->
quirks
&
LG_FF3
)
ret
=
lg3ff_init
(
hdev
);
else
if
(
drv_data
->
quirks
&
LG_FF4
)
ret
=
lg4ff_init
(
hdev
);
if
(
ret
)
goto
err_free
;
return
0
;
err_free:
...
...
@@ -731,7 +735,7 @@ static void lg_remove(struct hid_device *hdev)
struct
lg_drv_data
*
drv_data
=
hid_get_drvdata
(
hdev
);
if
(
drv_data
->
quirks
&
LG_FF4
)
lg4ff_deinit
(
hdev
);
else
hid_hw_stop
(
hdev
);
kfree
(
drv_data
);
}
...
...
drivers/hid/hid-lg4ff.c
浏览文件 @
817a3125
...
...
@@ -68,26 +68,32 @@
#define LG4FF_FFEX_REV_MAJ 0x21
#define LG4FF_FFEX_REV_MIN 0x00
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
);
struct
lg4ff_
device_entry
{
__
u32
product_id
;
__
u16
range
;
__
u16
min_range
;
__
u16
max_range
;
static
void
lg4ff_set_range_dfp
(
struct
hid_device
*
hid
,
u16
range
);
static
void
lg4ff_set_range_g25
(
struct
hid_device
*
hid
,
u16
range
);
struct
lg4ff_
wheel_data
{
const
u32
product_id
;
u16
range
;
const
u16
min_range
;
const
u16
max_range
;
#ifdef CONFIG_LEDS_CLASS
__
u8
led_state
;
u8
led_state
;
struct
led_classdev
*
led
[
5
];
#endif
u32
alternate_modes
;
const
char
*
real_tag
;
const
char
*
real_name
;
u16
real_product_id
;
struct
list_head
list
;
const
u32
alternate_modes
;
const
char
*
const
real_tag
;
const
char
*
const
real_name
;
const
u16
real_product_id
;
void
(
*
set_range
)(
struct
hid_device
*
hid
,
u16
range
);
};
struct
lg4ff_device_entry
{
spinlock_t
report_lock
;
/* Protect output HID report */
struct
hid_report
*
report
;
struct
lg4ff_wheel_data
wdata
;
};
static
const
signed
short
lg4ff_wheel_effects
[]
=
{
FF_CONSTANT
,
FF_AUTOCENTER
,
...
...
@@ -95,16 +101,16 @@ static const signed short lg4ff_wheel_effects[] = {
};
struct
lg4ff_wheel
{
const
__
u32
product_id
;
const
u32
product_id
;
const
signed
short
*
ff_effects
;
const
__
u16
min_range
;
const
__
u16
max_range
;
const
u16
min_range
;
const
u16
max_range
;
void
(
*
set_range
)(
struct
hid_device
*
hid
,
u16
range
);
};
struct
lg4ff_compat_mode_switch
{
const
__
u8
cmd_count
;
/* Number of commands to send */
const
__
u8
cmd
[];
const
u8
cmd_count
;
/* Number of commands to send */
const
u8
cmd
[];
};
struct
lg4ff_wheel_ident_info
{
...
...
@@ -134,10 +140,10 @@ struct lg4ff_alternate_mode {
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_DFP_WHEEL
,
lg4ff_wheel_effects
,
40
,
900
,
lg4ff_set_range_dfp
},
{
USB_DEVICE_ID_LOGITECH_G25_WHEEL
,
lg4ff_wheel_effects
,
40
,
900
,
lg4ff_set_range_g25
},
{
USB_DEVICE_ID_LOGITECH_DFGT_WHEEL
,
lg4ff_wheel_effects
,
40
,
900
,
lg4ff_set_range_g25
},
{
USB_DEVICE_ID_LOGITECH_G27_WHEEL
,
lg4ff_wheel_effects
,
40
,
900
,
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
}
};
...
...
@@ -245,10 +251,10 @@ static const struct lg4ff_compat_mode_switch lg4ff_mode_switch_ext16_g25 = {
};
/* Recalculates X axis value accordingly to currently selected range */
static
__s32
lg4ff_adjust_dfp_x_axis
(
__s32
value
,
__
u16
range
)
static
s32
lg4ff_adjust_dfp_x_axis
(
s32
value
,
u16
range
)
{
__
u16
max_range
;
__
s32
new_value
;
u16
max_range
;
s32
new_value
;
if
(
range
==
900
)
return
value
;
...
...
@@ -269,21 +275,21 @@ static __s32 lg4ff_adjust_dfp_x_axis(__s32 value, __u16 range)
}
int
lg4ff_adjust_input_event
(
struct
hid_device
*
hid
,
struct
hid_field
*
field
,
struct
hid_usage
*
usage
,
__
s32
value
,
struct
lg_drv_data
*
drv_data
)
struct
hid_usage
*
usage
,
s32
value
,
struct
lg_drv_data
*
drv_data
)
{
struct
lg4ff_device_entry
*
entry
=
drv_data
->
device_props
;
__
s32
new_value
=
0
;
s32
new_value
=
0
;
if
(
!
entry
)
{
hid_err
(
hid
,
"Device properties not found"
);
return
0
;
}
switch
(
entry
->
product_id
)
{
switch
(
entry
->
wdata
.
product_id
)
{
case
USB_DEVICE_ID_LOGITECH_DFP_WHEEL
:
switch
(
usage
->
code
)
{
case
ABS_X
:
new_value
=
lg4ff_adjust_dfp_x_axis
(
value
,
entry
->
range
);
new_value
=
lg4ff_adjust_dfp_x_axis
(
value
,
entry
->
wdata
.
range
);
input_event
(
field
->
hidinput
->
input
,
usage
->
type
,
usage
->
code
,
new_value
);
return
1
;
default:
...
...
@@ -294,14 +300,56 @@ int lg4ff_adjust_input_event(struct hid_device *hid, struct hid_field *field,
}
}
static
int
hid_lg4ff_play
(
struct
input_dev
*
dev
,
void
*
data
,
struct
ff_effect
*
effect
)
static
void
lg4ff_init_wheel_data
(
struct
lg4ff_wheel_data
*
const
wdata
,
const
struct
lg4ff_wheel
*
wheel
,
const
struct
lg4ff_multimode_wheel
*
mmode_wheel
,
const
u16
real_product_id
)
{
u32
alternate_modes
=
0
;
const
char
*
real_tag
=
NULL
;
const
char
*
real_name
=
NULL
;
if
(
mmode_wheel
)
{
alternate_modes
=
mmode_wheel
->
alternate_modes
;
real_tag
=
mmode_wheel
->
real_tag
;
real_name
=
mmode_wheel
->
real_name
;
}
{
struct
lg4ff_wheel_data
t_wdata
=
{
.
product_id
=
wheel
->
product_id
,
.
real_product_id
=
real_product_id
,
.
min_range
=
wheel
->
min_range
,
.
max_range
=
wheel
->
max_range
,
.
set_range
=
wheel
->
set_range
,
.
alternate_modes
=
alternate_modes
,
.
real_tag
=
real_tag
,
.
real_name
=
real_name
};
memcpy
(
wdata
,
&
t_wdata
,
sizeof
(
t_wdata
));
}
}
static
int
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
;
struct
hid_report
*
report
=
list_entry
(
report_list
->
next
,
struct
hid_report
,
list
);
__s32
*
value
=
report
->
field
[
0
]
->
value
;
struct
lg4ff_device_entry
*
entry
;
struct
lg_drv_data
*
drv_data
;
unsigned
long
flags
;
s32
*
value
;
int
x
;
drv_data
=
hid_get_drvdata
(
hid
);
if
(
!
drv_data
)
{
hid_err
(
hid
,
"Private driver data not found!
\n
"
);
return
-
EINVAL
;
}
entry
=
drv_data
->
device_props
;
if
(
!
entry
)
{
hid_err
(
hid
,
"Device properties not found!
\n
"
);
return
-
EINVAL
;
}
value
=
entry
->
report
->
field
[
0
]
->
value
;
#define CLAMP(x) do { if (x < 0) x = 0; else if (x > 0xff) x = 0xff; } while (0)
switch
(
effect
->
type
)
{
...
...
@@ -309,6 +357,7 @@ static int hid_lg4ff_play(struct input_dev *dev, void *data, struct ff_effect *e
x
=
effect
->
u
.
ramp
.
start_level
+
0x80
;
/* 0x80 is no force */
CLAMP
(
x
);
spin_lock_irqsave
(
&
entry
->
report_lock
,
flags
);
if
(
x
==
0x80
)
{
/* De-activate force in slot-1*/
value
[
0
]
=
0x13
;
...
...
@@ -319,7 +368,8 @@ static int hid_lg4ff_play(struct input_dev *dev, void *data, struct ff_effect *e
value
[
5
]
=
0x00
;
value
[
6
]
=
0x00
;
hid_hw_request
(
hid
,
report
,
HID_REQ_SET_REPORT
);
hid_hw_request
(
hid
,
entry
->
report
,
HID_REQ_SET_REPORT
);
spin_unlock_irqrestore
(
&
entry
->
report_lock
,
flags
);
return
0
;
}
...
...
@@ -331,7 +381,8 @@ static int hid_lg4ff_play(struct input_dev *dev, void *data, struct ff_effect *e
value
[
5
]
=
0x00
;
value
[
6
]
=
0x00
;
hid_hw_request
(
hid
,
report
,
HID_REQ_SET_REPORT
);
hid_hw_request
(
hid
,
entry
->
report
,
HID_REQ_SET_REPORT
);
spin_unlock_irqrestore
(
&
entry
->
report_lock
,
flags
);
break
;
}
return
0
;
...
...
@@ -339,15 +390,16 @@ static int hid_lg4ff_play(struct input_dev *dev, void *data, struct ff_effect *e
/* 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
)
static
void
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
;
__
u32
expand_a
,
expand_b
;
s32
*
value
=
report
->
field
[
0
]
->
value
;
u32
expand_a
,
expand_b
;
struct
lg4ff_device_entry
*
entry
;
struct
lg_drv_data
*
drv_data
;
unsigned
long
flags
;
drv_data
=
hid_get_drvdata
(
hid
);
if
(
!
drv_data
)
{
...
...
@@ -360,8 +412,10 @@ static void hid_lg4ff_set_autocenter_default(struct input_dev *dev, u16 magnitud
hid_err
(
hid
,
"Device properties not found!
\n
"
);
return
;
}
value
=
entry
->
report
->
field
[
0
]
->
value
;
/* De-activate Auto-Center */
spin_lock_irqsave
(
&
entry
->
report_lock
,
flags
);
if
(
magnitude
==
0
)
{
value
[
0
]
=
0xf5
;
value
[
1
]
=
0x00
;
...
...
@@ -371,7 +425,8 @@ static void hid_lg4ff_set_autocenter_default(struct input_dev *dev, u16 magnitud
value
[
5
]
=
0x00
;
value
[
6
]
=
0x00
;
hid_hw_request
(
hid
,
report
,
HID_REQ_SET_REPORT
);
hid_hw_request
(
hid
,
entry
->
report
,
HID_REQ_SET_REPORT
);
spin_unlock_irqrestore
(
&
entry
->
report_lock
,
flags
);
return
;
}
...
...
@@ -384,7 +439,7 @@ static void hid_lg4ff_set_autocenter_default(struct input_dev *dev, u16 magnitud
}
/* Adjust for non-MOMO wheels */
switch
(
entry
->
product_id
)
{
switch
(
entry
->
wdata
.
product_id
)
{
case
USB_DEVICE_ID_LOGITECH_MOMO_WHEEL
:
case
USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2
:
break
;
...
...
@@ -401,7 +456,7 @@ static void hid_lg4ff_set_autocenter_default(struct input_dev *dev, u16 magnitud
value
[
5
]
=
0x00
;
value
[
6
]
=
0x00
;
hid_hw_request
(
hid
,
report
,
HID_REQ_SET_REPORT
);
hid_hw_request
(
hid
,
entry
->
report
,
HID_REQ_SET_REPORT
);
/* Activate Auto-Center */
value
[
0
]
=
0x14
;
...
...
@@ -412,18 +467,34 @@ static void hid_lg4ff_set_autocenter_default(struct input_dev *dev, u16 magnitud
value
[
5
]
=
0x00
;
value
[
6
]
=
0x00
;
hid_hw_request
(
hid
,
report
,
HID_REQ_SET_REPORT
);
hid_hw_request
(
hid
,
entry
->
report
,
HID_REQ_SET_REPORT
);
spin_unlock_irqrestore
(
&
entry
->
report_lock
,
flags
);
}
/* Sends autocentering command compatible with Formula Force EX */
static
void
hid_
lg4ff_set_autocenter_ffex
(
struct
input_dev
*
dev
,
u16
magnitude
)
static
void
lg4ff_set_autocenter_ffex
(
struct
input_dev
*
dev
,
u16
magnitude
)
{
struct
hid_device
*
hid
=
input_get_drvdata
(
dev
);
struct
list_head
*
report_list
=
&
hid
->
report_enum
[
HID_OUTPUT_REPORT
].
report_list
;
struct
hid_report
*
report
=
list_entry
(
report_list
->
next
,
struct
hid_report
,
list
);
__s32
*
value
=
report
->
field
[
0
]
->
value
;
struct
lg4ff_device_entry
*
entry
;
struct
lg_drv_data
*
drv_data
;
unsigned
long
flags
;
s32
*
value
;
magnitude
=
magnitude
*
90
/
65535
;
drv_data
=
hid_get_drvdata
(
hid
);
if
(
!
drv_data
)
{
hid_err
(
hid
,
"Private driver data not found!
\n
"
);
return
;
}
entry
=
drv_data
->
device_props
;
if
(
!
entry
)
{
hid_err
(
hid
,
"Device properties not found!
\n
"
);
return
;
}
value
=
entry
->
report
->
field
[
0
]
->
value
;
spin_lock_irqsave
(
&
entry
->
report_lock
,
flags
);
value
[
0
]
=
0xfe
;
value
[
1
]
=
0x03
;
value
[
2
]
=
magnitude
>>
14
;
...
...
@@ -432,18 +503,33 @@ static void hid_lg4ff_set_autocenter_ffex(struct input_dev *dev, u16 magnitude)
value
[
5
]
=
0x00
;
value
[
6
]
=
0x00
;
hid_hw_request
(
hid
,
report
,
HID_REQ_SET_REPORT
);
hid_hw_request
(
hid
,
entry
->
report
,
HID_REQ_SET_REPORT
);
spin_unlock_irqrestore
(
&
entry
->
report_lock
,
flags
);
}
/* 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
)
static
void
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
);
__s32
*
value
=
report
->
field
[
0
]
->
value
;
struct
lg4ff_device_entry
*
entry
;
struct
lg_drv_data
*
drv_data
;
unsigned
long
flags
;
s32
*
value
;
drv_data
=
hid_get_drvdata
(
hid
);
if
(
!
drv_data
)
{
hid_err
(
hid
,
"Private driver data not found!
\n
"
);
return
;
}
entry
=
drv_data
->
device_props
;
if
(
!
entry
)
{
hid_err
(
hid
,
"Device properties not found!
\n
"
);
return
;
}
value
=
entry
->
report
->
field
[
0
]
->
value
;
dbg_hid
(
"G25/G27/DFGT: setting range to %u
\n
"
,
range
);
spin_lock_irqsave
(
&
entry
->
report_lock
,
flags
);
value
[
0
]
=
0xf8
;
value
[
1
]
=
0x81
;
value
[
2
]
=
range
&
0x00ff
;
...
...
@@ -452,20 +538,35 @@ static void hid_lg4ff_set_range_g25(struct hid_device *hid, u16 range)
value
[
5
]
=
0x00
;
value
[
6
]
=
0x00
;
hid_hw_request
(
hid
,
report
,
HID_REQ_SET_REPORT
);
hid_hw_request
(
hid
,
entry
->
report
,
HID_REQ_SET_REPORT
);
spin_unlock_irqrestore
(
&
entry
->
report_lock
,
flags
);
}
/* Sends commands to set range compatible with Driving Force Pro wheel */
static
void
hid_lg4ff_set_range_dfp
(
struct
hid_device
*
hid
,
__
u16
range
)
static
void
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
);
struct
lg4ff_device_entry
*
entry
;
struct
lg_drv_data
*
drv_data
;
unsigned
long
flags
;
int
start_left
,
start_right
,
full_range
;
__s32
*
value
=
report
->
field
[
0
]
->
value
;
s32
*
value
;
drv_data
=
hid_get_drvdata
(
hid
);
if
(
!
drv_data
)
{
hid_err
(
hid
,
"Private driver data not found!
\n
"
);
return
;
}
entry
=
drv_data
->
device_props
;
if
(
!
entry
)
{
hid_err
(
hid
,
"Device properties not found!
\n
"
);
return
;
}
value
=
entry
->
report
->
field
[
0
]
->
value
;
dbg_hid
(
"Driving Force Pro: setting range to %u
\n
"
,
range
);
/* Prepare "coarse" limit command */
spin_lock_irqsave
(
&
entry
->
report_lock
,
flags
);
value
[
0
]
=
0xf8
;
value
[
1
]
=
0x00
;
/* Set later */
value
[
2
]
=
0x00
;
...
...
@@ -475,13 +576,13 @@ static void hid_lg4ff_set_range_dfp(struct hid_device *hid, __u16 range)
value
[
6
]
=
0x00
;
if
(
range
>
200
)
{
report
->
field
[
0
]
->
value
[
1
]
=
0x03
;
value
[
1
]
=
0x03
;
full_range
=
900
;
}
else
{
report
->
field
[
0
]
->
value
[
1
]
=
0x02
;
value
[
1
]
=
0x02
;
full_range
=
200
;
}
hid_hw_request
(
hid
,
report
,
HID_REQ_SET_REPORT
);
hid_hw_request
(
hid
,
entry
->
report
,
HID_REQ_SET_REPORT
);
/* Prepare "fine" limit command */
value
[
0
]
=
0x81
;
...
...
@@ -493,7 +594,8 @@ static void hid_lg4ff_set_range_dfp(struct hid_device *hid, __u16 range)
value
[
6
]
=
0x00
;
if
(
range
==
200
||
range
==
900
)
{
/* Do not apply any fine limit */
hid_hw_request
(
hid
,
report
,
HID_REQ_SET_REPORT
);
hid_hw_request
(
hid
,
entry
->
report
,
HID_REQ_SET_REPORT
);
spin_unlock_irqrestore
(
&
entry
->
report_lock
,
flags
);
return
;
}
...
...
@@ -507,7 +609,8 @@ static void hid_lg4ff_set_range_dfp(struct hid_device *hid, __u16 range)
value
[
5
]
=
(
start_right
&
0xe
)
<<
4
|
(
start_left
&
0xe
);
value
[
6
]
=
0xff
;
hid_hw_request
(
hid
,
report
,
HID_REQ_SET_REPORT
);
hid_hw_request
(
hid
,
entry
->
report
,
HID_REQ_SET_REPORT
);
spin_unlock_irqrestore
(
&
entry
->
report_lock
,
flags
);
}
static
const
struct
lg4ff_compat_mode_switch
*
lg4ff_get_mode_switch_command
(
const
u16
real_product_id
,
const
u16
target_product_id
)
...
...
@@ -569,19 +672,35 @@ static const struct lg4ff_compat_mode_switch *lg4ff_get_mode_switch_command(cons
static
int
lg4ff_switch_compatibility_mode
(
struct
hid_device
*
hid
,
const
struct
lg4ff_compat_mode_switch
*
s
)
{
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
;
struct
lg4ff_device_entry
*
entry
;
struct
lg_drv_data
*
drv_data
;
unsigned
long
flags
;
s32
*
value
;
u8
i
;
drv_data
=
hid_get_drvdata
(
hid
);
if
(
!
drv_data
)
{
hid_err
(
hid
,
"Private driver data not found!
\n
"
);
return
-
EINVAL
;
}
entry
=
drv_data
->
device_props
;
if
(
!
entry
)
{
hid_err
(
hid
,
"Device properties not found!
\n
"
);
return
-
EINVAL
;
}
value
=
entry
->
report
->
field
[
0
]
->
value
;
spin_lock_irqsave
(
&
entry
->
report_lock
,
flags
);
for
(
i
=
0
;
i
<
s
->
cmd_count
;
i
++
)
{
u8
j
;
for
(
j
=
0
;
j
<
7
;
j
++
)
value
[
j
]
=
s
->
cmd
[
j
+
(
7
*
i
)];
hid_hw_request
(
hid
,
report
,
HID_REQ_SET_REPORT
);
hid_hw_request
(
hid
,
entry
->
report
,
HID_REQ_SET_REPORT
);
}
spin_unlock_irqrestore
(
&
entry
->
report_lock
,
flags
);
hid_hw_wait
(
hid
);
return
0
;
}
...
...
@@ -606,23 +725,23 @@ static ssize_t lg4ff_alternate_modes_show(struct device *dev, struct device_attr
return
0
;
}
if
(
!
entry
->
real_name
)
{
if
(
!
entry
->
wdata
.
real_name
)
{
hid_err
(
hid
,
"NULL pointer to string
\n
"
);
return
0
;
}
for
(
i
=
0
;
i
<
LG4FF_MODE_MAX_IDX
;
i
++
)
{
if
(
entry
->
alternate_modes
&
BIT
(
i
))
{
if
(
entry
->
wdata
.
alternate_modes
&
BIT
(
i
))
{
/* Print tag and full name */
count
+=
scnprintf
(
buf
+
count
,
PAGE_SIZE
-
count
,
"%s: %s"
,
lg4ff_alternate_modes
[
i
].
tag
,
!
lg4ff_alternate_modes
[
i
].
product_id
?
entry
->
real_name
:
lg4ff_alternate_modes
[
i
].
name
);
!
lg4ff_alternate_modes
[
i
].
product_id
?
entry
->
wdata
.
real_name
:
lg4ff_alternate_modes
[
i
].
name
);
if
(
count
>=
PAGE_SIZE
-
1
)
return
count
;
/* Mark the currently active mode with an asterisk */
if
(
lg4ff_alternate_modes
[
i
].
product_id
==
entry
->
product_id
||
(
lg4ff_alternate_modes
[
i
].
product_id
==
0
&&
entry
->
product_id
==
entry
->
real_product_id
))
if
(
lg4ff_alternate_modes
[
i
].
product_id
==
entry
->
wdata
.
product_id
||
(
lg4ff_alternate_modes
[
i
].
product_id
==
0
&&
entry
->
wdata
.
product_id
==
entry
->
wdata
.
real_product_id
))
count
+=
scnprintf
(
buf
+
count
,
PAGE_SIZE
-
count
,
" *
\n
"
);
else
count
+=
scnprintf
(
buf
+
count
,
PAGE_SIZE
-
count
,
"
\n
"
);
...
...
@@ -675,10 +794,10 @@ static ssize_t lg4ff_alternate_modes_store(struct device *dev, struct device_att
const
u16
mode_product_id
=
lg4ff_alternate_modes
[
i
].
product_id
;
const
char
*
tag
=
lg4ff_alternate_modes
[
i
].
tag
;
if
(
entry
->
alternate_modes
&
BIT
(
i
))
{
if
(
entry
->
wdata
.
alternate_modes
&
BIT
(
i
))
{
if
(
!
strcmp
(
tag
,
lbuf
))
{
if
(
!
mode_product_id
)
target_product_id
=
entry
->
real_product_id
;
target_product_id
=
entry
->
wdata
.
real_product_id
;
else
target_product_id
=
mode_product_id
;
break
;
...
...
@@ -693,24 +812,24 @@ static ssize_t lg4ff_alternate_modes_store(struct device *dev, struct device_att
}
kfree
(
lbuf
);
/* Not needed anymore */
if
(
target_product_id
==
entry
->
product_id
)
/* Nothing to do */
if
(
target_product_id
==
entry
->
wdata
.
product_id
)
/* Nothing to do */
return
count
;
/* Automatic switching has to be disabled for the switch to DF-EX mode to work correctly */
if
(
target_product_id
==
USB_DEVICE_ID_LOGITECH_WHEEL
&&
!
lg4ff_no_autoswitch
)
{
hid_info
(
hid
,
"
\"
%s
\"
cannot be switched to
\"
DF-EX
\"
mode. Load the
\"
hid_logitech
\"
module with
\"
lg4ff_no_autoswitch=1
\"
parameter set and try again
\n
"
,
entry
->
real_name
);
entry
->
wdata
.
real_name
);
return
-
EINVAL
;
}
/* Take care of hardware limitations */
if
((
entry
->
real_product_id
==
USB_DEVICE_ID_LOGITECH_DFP_WHEEL
||
entry
->
real_product_id
==
USB_DEVICE_ID_LOGITECH_G25_WHEEL
)
&&
entry
->
product_id
>
target_product_id
)
{
hid_info
(
hid
,
"
\"
%s
\"
cannot be switched back into
\"
%s
\"
mode
\n
"
,
entry
->
real_name
,
lg4ff_alternate_modes
[
i
].
name
);
if
((
entry
->
wdata
.
real_product_id
==
USB_DEVICE_ID_LOGITECH_DFP_WHEEL
||
entry
->
wdata
.
real_product_id
==
USB_DEVICE_ID_LOGITECH_G25_WHEEL
)
&&
entry
->
wdata
.
product_id
>
target_product_id
)
{
hid_info
(
hid
,
"
\"
%s
\"
cannot be switched back into
\"
%s
\"
mode
\n
"
,
entry
->
wdata
.
real_name
,
lg4ff_alternate_modes
[
i
].
name
);
return
-
EINVAL
;
}
s
=
lg4ff_get_mode_switch_command
(
entry
->
real_product_id
,
target_product_id
);
s
=
lg4ff_get_mode_switch_command
(
entry
->
wdata
.
real_product_id
,
target_product_id
);
if
(
!
s
)
{
hid_err
(
hid
,
"Invalid target product ID %X
\n
"
,
target_product_id
);
return
-
EINVAL
;
...
...
@@ -721,8 +840,8 @@ static ssize_t lg4ff_alternate_modes_store(struct device *dev, struct device_att
}
static
DEVICE_ATTR
(
alternate_modes
,
S_IRUSR
|
S_IWUSR
|
S_IRGRP
|
S_IWGRP
|
S_IROTH
,
lg4ff_alternate_modes_show
,
lg4ff_alternate_modes_store
);
/*
Read current range and display it in termina
l */
static
ssize_t
range_show
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
/*
Export the currently set range of the whee
l */
static
ssize_t
lg4ff_
range_show
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
char
*
buf
)
{
struct
hid_device
*
hid
=
to_hid_device
(
dev
);
...
...
@@ -742,19 +861,19 @@ static ssize_t range_show(struct device *dev, struct device_attribute *attr,
return
0
;
}
count
=
scnprintf
(
buf
,
PAGE_SIZE
,
"%u
\n
"
,
entry
->
range
);
count
=
scnprintf
(
buf
,
PAGE_SIZE
,
"%u
\n
"
,
entry
->
wdata
.
range
);
return
count
;
}
/* Set range to user specified value, call appropriate function
* according to the type of the wheel */
static
ssize_t
range_store
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
static
ssize_t
lg4ff_
range_store
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
const
char
*
buf
,
size_t
count
)
{
struct
hid_device
*
hid
=
to_hid_device
(
dev
);
struct
lg4ff_device_entry
*
entry
;
struct
lg_drv_data
*
drv_data
;
__
u16
range
=
simple_strtoul
(
buf
,
NULL
,
10
);
u16
range
=
simple_strtoul
(
buf
,
NULL
,
10
);
drv_data
=
hid_get_drvdata
(
hid
);
if
(
!
drv_data
)
{
...
...
@@ -769,18 +888,18 @@ static ssize_t range_store(struct device *dev, struct device_attribute *attr,
}
if
(
range
==
0
)
range
=
entry
->
max_range
;
range
=
entry
->
wdata
.
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
;
if
(
entry
->
wdata
.
set_range
&&
range
>=
entry
->
wdata
.
min_range
&&
range
<=
entry
->
wdata
.
max_range
)
{
entry
->
wdata
.
set_range
(
hid
,
range
);
entry
->
wdata
.
range
=
range
;
}
return
count
;
}
static
DEVICE_ATTR
_RW
(
rang
e
);
static
DEVICE_ATTR
(
range
,
S_IRUSR
|
S_IWUSR
|
S_IRGRP
|
S_IWGRP
|
S_IROTH
,
lg4ff_range_show
,
lg4ff_range_stor
e
);
static
ssize_t
lg4ff_real_id_show
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
char
*
buf
)
{
...
...
@@ -801,12 +920,12 @@ static ssize_t lg4ff_real_id_show(struct device *dev, struct device_attribute *a
return
0
;
}
if
(
!
entry
->
real_tag
||
!
entry
->
real_name
)
{
if
(
!
entry
->
wdata
.
real_tag
||
!
entry
->
wdata
.
real_name
)
{
hid_err
(
hid
,
"NULL pointer to string
\n
"
);
return
0
;
}
count
=
scnprintf
(
buf
,
PAGE_SIZE
,
"%s: %s
\n
"
,
entry
->
real_tag
,
entry
->
real_name
);
count
=
scnprintf
(
buf
,
PAGE_SIZE
,
"%s: %s
\n
"
,
entry
->
wdata
.
real_tag
,
entry
->
wdata
.
real_name
);
return
count
;
}
...
...
@@ -818,12 +937,27 @@ static ssize_t lg4ff_real_id_store(struct device *dev, struct device_attribute *
static
DEVICE_ATTR
(
real_id
,
S_IRUGO
,
lg4ff_real_id_show
,
lg4ff_real_id_store
);
#ifdef CONFIG_LEDS_CLASS
static
void
lg4ff_set_leds
(
struct
hid_device
*
hid
,
__
u8
leds
)
static
void
lg4ff_set_leds
(
struct
hid_device
*
hid
,
u8
leds
)
{
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
;
struct
lg_drv_data
*
drv_data
;
struct
lg4ff_device_entry
*
entry
;
unsigned
long
flags
;
s32
*
value
;
drv_data
=
hid_get_drvdata
(
hid
);
if
(
!
drv_data
)
{
hid_err
(
hid
,
"Private driver data not found!
\n
"
);
return
;
}
entry
=
drv_data
->
device_props
;
if
(
!
entry
)
{
hid_err
(
hid
,
"Device properties not found!
\n
"
);
return
;
}
value
=
entry
->
report
->
field
[
0
]
->
value
;
spin_lock_irqsave
(
&
entry
->
report_lock
,
flags
);
value
[
0
]
=
0xf8
;
value
[
1
]
=
0x12
;
value
[
2
]
=
leds
;
...
...
@@ -831,7 +965,8 @@ static void lg4ff_set_leds(struct hid_device *hid, __u8 leds)
value
[
4
]
=
0x00
;
value
[
5
]
=
0x00
;
value
[
6
]
=
0x00
;
hid_hw_request
(
hid
,
report
,
HID_REQ_SET_REPORT
);
hid_hw_request
(
hid
,
entry
->
report
,
HID_REQ_SET_REPORT
);
spin_unlock_irqrestore
(
&
entry
->
report_lock
,
flags
);
}
static
void
lg4ff_led_set_brightness
(
struct
led_classdev
*
led_cdev
,
...
...
@@ -848,7 +983,7 @@ static void lg4ff_led_set_brightness(struct led_classdev *led_cdev,
return
;
}
entry
=
(
struct
lg4ff_device_entry
*
)
drv_data
->
device_props
;
entry
=
drv_data
->
device_props
;
if
(
!
entry
)
{
hid_err
(
hid
,
"Device properties not found."
);
...
...
@@ -856,15 +991,15 @@ static void lg4ff_led_set_brightness(struct led_classdev *led_cdev,
}
for
(
i
=
0
;
i
<
5
;
i
++
)
{
if
(
led_cdev
!=
entry
->
led
[
i
])
if
(
led_cdev
!=
entry
->
wdata
.
led
[
i
])
continue
;
state
=
(
entry
->
led_state
>>
i
)
&
1
;
state
=
(
entry
->
wdata
.
led_state
>>
i
)
&
1
;
if
(
value
==
LED_OFF
&&
state
)
{
entry
->
led_state
&=
~
(
1
<<
i
);
lg4ff_set_leds
(
hid
,
entry
->
led_state
);
entry
->
wdata
.
led_state
&=
~
(
1
<<
i
);
lg4ff_set_leds
(
hid
,
entry
->
wdata
.
led_state
);
}
else
if
(
value
!=
LED_OFF
&&
!
state
)
{
entry
->
led_state
|=
1
<<
i
;
lg4ff_set_leds
(
hid
,
entry
->
led_state
);
entry
->
wdata
.
led_state
|=
1
<<
i
;
lg4ff_set_leds
(
hid
,
entry
->
wdata
.
led_state
);
}
break
;
}
...
...
@@ -883,7 +1018,7 @@ static enum led_brightness lg4ff_led_get_brightness(struct led_classdev *led_cde
return
LED_OFF
;
}
entry
=
(
struct
lg4ff_device_entry
*
)
drv_data
->
device_props
;
entry
=
drv_data
->
device_props
;
if
(
!
entry
)
{
hid_err
(
hid
,
"Device properties not found."
);
...
...
@@ -891,8 +1026,8 @@ static enum led_brightness lg4ff_led_get_brightness(struct led_classdev *led_cde
}
for
(
i
=
0
;
i
<
5
;
i
++
)
if
(
led_cdev
==
entry
->
led
[
i
])
{
value
=
(
entry
->
led_state
>>
i
)
&
1
;
if
(
led_cdev
==
entry
->
wdata
.
led
[
i
])
{
value
=
(
entry
->
wdata
.
led_state
>>
i
)
&
1
;
break
;
}
...
...
@@ -991,8 +1126,11 @@ int lg4ff_init(struct hid_device *hid)
{
struct
hid_input
*
hidinput
=
list_entry
(
hid
->
inputs
.
next
,
struct
hid_input
,
list
);
struct
input_dev
*
dev
=
hidinput
->
input
;
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
);
const
struct
usb_device_descriptor
*
udesc
=
&
(
hid_to_usb_dev
(
hid
)
->
descriptor
);
const
u16
bcdDevice
=
le16_to_cpu
(
udesc
->
bcdDevice
);
const
struct
lg4ff_multimode_wheel
*
mmode_wheel
=
NULL
;
struct
lg4ff_device_entry
*
entry
;
struct
lg_drv_data
*
drv_data
;
int
error
,
i
,
j
;
...
...
@@ -1003,6 +1141,18 @@ int lg4ff_init(struct hid_device *hid)
if
(
!
hid_validate_values
(
hid
,
HID_OUTPUT_REPORT
,
0
,
0
,
7
))
return
-
1
;
drv_data
=
hid_get_drvdata
(
hid
);
if
(
!
drv_data
)
{
hid_err
(
hid
,
"Cannot add device, private driver data not allocated
\n
"
);
return
-
1
;
}
entry
=
kzalloc
(
sizeof
(
*
entry
),
GFP_KERNEL
);
if
(
!
entry
)
return
-
ENOMEM
;
spin_lock_init
(
&
entry
->
report_lock
);
entry
->
report
=
report
;
drv_data
->
device_props
=
entry
;
/* Check if a multimode wheel has been connected and
* handle it appropriately */
mmode_ret
=
lg4ff_handle_multimode_wheel
(
hid
,
&
real_product_id
,
bcdDevice
);
...
...
@@ -1012,6 +1162,11 @@ int lg4ff_init(struct hid_device *hid)
*/
if
(
mmode_ret
==
LG4FF_MMODE_SWITCHED
)
return
0
;
else
if
(
mmode_ret
<
0
)
{
hid_err
(
hid
,
"Unable to switch device mode during initialization, errno %d
\n
"
,
mmode_ret
);
error
=
mmode_ret
;
goto
err_init
;
}
/* Check what wheel has been connected */
for
(
i
=
0
;
i
<
ARRAY_SIZE
(
lg4ff_devices
);
i
++
)
{
...
...
@@ -1022,9 +1177,11 @@ int lg4ff_init(struct hid_device *hid)
}
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
;
hid_err
(
hid
,
"This device is flagged to be handled by the lg4ff module but this module does not know how to handle it. "
"Please report this as a bug to LKML, Simon Wood <simon@mungewell.org> or "
"Michal Maly <madcatxster@devoid-pointer.net>
\n
"
);
error
=
-
1
;
goto
err_init
;
}
if
(
mmode_ret
==
LG4FF_MMODE_IS_MULTIMODE
)
{
...
...
@@ -1035,7 +1192,8 @@ int lg4ff_init(struct hid_device *hid)
if
(
mmode_idx
==
ARRAY_SIZE
(
lg4ff_multimode_wheels
))
{
hid_err
(
hid
,
"Device product ID %X is not listed as a multimode wheel"
,
real_product_id
);
return
-
1
;
error
=
-
1
;
goto
err_init
;
}
}
...
...
@@ -1043,37 +1201,17 @@ int lg4ff_init(struct hid_device *hid)
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
);
error
=
input_ff_create_memless
(
dev
,
NULL
,
lg4ff_play
);
if
(
error
)
return
error
;
/* Get private driver data */
drv_data
=
hid_get_drvdata
(
hid
);
if
(
!
drv_data
)
{
hid_err
(
hid
,
"Cannot add device, private driver data not allocated
\n
"
);
return
-
1
;
}
goto
err_init
;
/* Initialize device properties */
entry
=
kzalloc
(
sizeof
(
struct
lg4ff_device_entry
),
GFP_KERNEL
);
if
(
!
entry
)
{
hid_err
(
hid
,
"Cannot add device, insufficient memory to allocate device properties.
\n
"
);
return
-
ENOMEM
;
}
drv_data
->
device_props
=
entry
;
entry
->
product_id
=
lg4ff_devices
[
i
].
product_id
;
entry
->
real_product_id
=
real_product_id
;
entry
->
min_range
=
lg4ff_devices
[
i
].
min_range
;
entry
->
max_range
=
lg4ff_devices
[
i
].
max_range
;
entry
->
set_range
=
lg4ff_devices
[
i
].
set_range
;
if
(
mmode_ret
==
LG4FF_MMODE_IS_MULTIMODE
)
{
BUG_ON
(
mmode_idx
==
-
1
);
entry
->
alternate_modes
=
lg4ff_multimode_wheels
[
mmode_idx
].
alternate_modes
;
entry
->
real_tag
=
lg4ff_multimode_wheels
[
mmode_idx
].
real_tag
;
entry
->
real_name
=
lg4ff_multimode_wheels
[
mmode_idx
].
real_name
;
mmode_wheel
=
&
lg4ff_multimode_wheels
[
mmode_idx
];
}
lg4ff_init_wheel_data
(
&
entry
->
wdata
,
&
lg4ff_devices
[
i
],
mmode_wheel
,
real_product_id
);
/* Check if autocentering is available and
* set the centering force to zero by default */
...
...
@@ -1081,9 +1219,9 @@ int lg4ff_init(struct hid_device *hid)
/* Formula Force EX expects different autocentering command */
if
((
bcdDevice
>>
8
)
==
LG4FF_FFEX_REV_MAJ
&&
(
bcdDevice
&
0xff
)
==
LG4FF_FFEX_REV_MIN
)
dev
->
ff
->
set_autocenter
=
hid_
lg4ff_set_autocenter_ffex
;
dev
->
ff
->
set_autocenter
=
lg4ff_set_autocenter_ffex
;
else
dev
->
ff
->
set_autocenter
=
hid_
lg4ff_set_autocenter_default
;
dev
->
ff
->
set_autocenter
=
lg4ff_set_autocenter_default
;
dev
->
ff
->
set_autocenter
(
dev
,
0
);
}
...
...
@@ -1091,27 +1229,27 @@ int lg4ff_init(struct hid_device *hid)
/* Create sysfs interface */
error
=
device_create_file
(
&
hid
->
dev
,
&
dev_attr_range
);
if
(
error
)
return
error
;
hid_warn
(
hid
,
"Unable to create sysfs interface for
\"
range
\"
, errno %d
\n
"
,
error
)
;
if
(
mmode_ret
==
LG4FF_MMODE_IS_MULTIMODE
)
{
error
=
device_create_file
(
&
hid
->
dev
,
&
dev_attr_real_id
);
if
(
error
)
return
error
;
hid_warn
(
hid
,
"Unable to create sysfs interface for
\"
real_id
\"
, errno %d
\n
"
,
error
)
;
error
=
device_create_file
(
&
hid
->
dev
,
&
dev_attr_alternate_modes
);
if
(
error
)
return
error
;
hid_warn
(
hid
,
"Unable to create sysfs interface for
\"
alternate_modes
\"
, errno %d
\n
"
,
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
);
entry
->
wdata
.
range
=
entry
->
wdata
.
max_range
;
if
(
entry
->
wdata
.
set_range
)
entry
->
wdata
.
set_range
(
hid
,
entry
->
wdata
.
range
);
#ifdef CONFIG_LEDS_CLASS
/* register led subsystem - G27 only */
entry
->
led_state
=
0
;
entry
->
wdata
.
led_state
=
0
;
for
(
j
=
0
;
j
<
5
;
j
++
)
entry
->
led
[
j
]
=
NULL
;
entry
->
wdata
.
led
[
j
]
=
NULL
;
if
(
lg4ff_devices
[
i
].
product_id
==
USB_DEVICE_ID_LOGITECH_G27_WHEEL
)
{
struct
led_classdev
*
led
;
...
...
@@ -1126,7 +1264,7 @@ int lg4ff_init(struct hid_device *hid)
led
=
kzalloc
(
sizeof
(
struct
led_classdev
)
+
name_sz
,
GFP_KERNEL
);
if
(
!
led
)
{
hid_err
(
hid
,
"can't allocate memory for LED %d
\n
"
,
j
);
goto
err
;
goto
err
_leds
;
}
name
=
(
void
*
)(
&
led
[
1
]);
...
...
@@ -1137,16 +1275,16 @@ int lg4ff_init(struct hid_device *hid)
led
->
brightness_get
=
lg4ff_led_get_brightness
;
led
->
brightness_set
=
lg4ff_led_set_brightness
;
entry
->
led
[
j
]
=
led
;
entry
->
wdata
.
led
[
j
]
=
led
;
error
=
led_classdev_register
(
&
hid
->
dev
,
led
);
if
(
error
)
{
hid_err
(
hid
,
"failed to register LED %d. Aborting.
\n
"
,
j
);
err:
err
_leds
:
/* Deregister LEDs (if any) */
for
(
j
=
0
;
j
<
5
;
j
++
)
{
led
=
entry
->
led
[
j
];
entry
->
led
[
j
]
=
NULL
;
led
=
entry
->
wdata
.
led
[
j
];
entry
->
wdata
.
led
[
j
]
=
NULL
;
if
(
!
led
)
continue
;
led_classdev_unregister
(
led
);
...
...
@@ -1160,6 +1298,11 @@ int lg4ff_init(struct hid_device *hid)
#endif
hid_info
(
hid
,
"Force feedback support for Logitech Gaming Wheels
\n
"
);
return
0
;
err_init:
drv_data
->
device_props
=
NULL
;
kfree
(
entry
);
return
error
;
}
int
lg4ff_deinit
(
struct
hid_device
*
hid
)
...
...
@@ -1176,14 +1319,13 @@ int lg4ff_deinit(struct hid_device *hid)
if
(
!
entry
)
goto
out
;
/* Nothing more to do */
device_remove_file
(
&
hid
->
dev
,
&
dev_attr_range
);
/* Multimode devices will have at least the "MODE_NATIVE" bit set */
if
(
entry
->
alternate_modes
)
{
if
(
entry
->
wdata
.
alternate_modes
)
{
device_remove_file
(
&
hid
->
dev
,
&
dev_attr_real_id
);
device_remove_file
(
&
hid
->
dev
,
&
dev_attr_alternate_modes
);
}
device_remove_file
(
&
hid
->
dev
,
&
dev_attr_range
);
#ifdef CONFIG_LEDS_CLASS
{
int
j
;
...
...
@@ -1192,8 +1334,8 @@ int lg4ff_deinit(struct hid_device *hid)
/* Deregister LEDs (if any) */
for
(
j
=
0
;
j
<
5
;
j
++
)
{
led
=
entry
->
led
[
j
];
entry
->
led
[
j
]
=
NULL
;
led
=
entry
->
wdata
.
led
[
j
];
entry
->
wdata
.
led
[
j
]
=
NULL
;
if
(
!
led
)
continue
;
led_classdev_unregister
(
led
);
...
...
@@ -1201,10 +1343,10 @@ int lg4ff_deinit(struct hid_device *hid)
}
}
#endif
hid_hw_stop
(
hid
);
drv_data
->
device_props
=
NULL
;
/* Deallocate memory */
kfree
(
entry
);
out:
dbg_hid
(
"Device successfully unregistered
\n
"
);
return
0
;
...
...
drivers/hid/hid-lg4ff.h
浏览文件 @
817a3125
...
...
@@ -5,12 +5,12 @@
extern
int
lg4ff_no_autoswitch
;
/* From hid-lg.c */
int
lg4ff_adjust_input_event
(
struct
hid_device
*
hid
,
struct
hid_field
*
field
,
struct
hid_usage
*
usage
,
__
s32
value
,
struct
lg_drv_data
*
drv_data
);
struct
hid_usage
*
usage
,
s32
value
,
struct
lg_drv_data
*
drv_data
);
int
lg4ff_init
(
struct
hid_device
*
hdev
);
int
lg4ff_deinit
(
struct
hid_device
*
hdev
);
#else
static
inline
int
lg4ff_adjust_input_event
(
struct
hid_device
*
hid
,
struct
hid_field
*
field
,
struct
hid_usage
*
usage
,
__
s32
value
,
struct
lg_drv_data
*
drv_data
)
{
return
0
;
}
struct
hid_usage
*
usage
,
s32
value
,
struct
lg_drv_data
*
drv_data
)
{
return
0
;
}
static
inline
int
lg4ff_init
(
struct
hid_device
*
hdev
)
{
return
-
1
;
}
static
inline
int
lg4ff_deinit
(
struct
hid_device
*
hdev
)
{
return
-
1
;
}
#endif
...
...
drivers/hid/hid-logitech-hidpp.c
浏览文件 @
817a3125
...
...
@@ -40,8 +40,9 @@ MODULE_PARM_DESC(disable_raw_mode,
#define HIDPP_REPORT_LONG_LENGTH 20
#define HIDPP_QUIRK_CLASS_WTP BIT(0)
#define HIDPP_QUIRK_CLASS_M560 BIT(1)
/* bits
1
..20 are reserved for classes */
/* bits
2
..20 are reserved for classes */
#define HIDPP_QUIRK_DELAYED_INIT BIT(21)
#define HIDPP_QUIRK_WTP_PHYSICAL_BUTTONS BIT(22)
...
...
@@ -930,6 +931,207 @@ static int wtp_connect(struct hid_device *hdev, bool connected)
true
,
true
);
}
/* ------------------------------------------------------------------------- */
/* Logitech M560 devices */
/* ------------------------------------------------------------------------- */
/*
* Logitech M560 protocol overview
*
* The Logitech M560 mouse, is designed for windows 8. When the middle and/or
* the sides buttons are pressed, it sends some keyboard keys events
* instead of buttons ones.
* To complicate things further, the middle button keys sequence
* is different from the odd press and the even press.
*
* forward button -> Super_R
* backward button -> Super_L+'d' (press only)
* middle button -> 1st time: Alt_L+SuperL+XF86TouchpadOff (press only)
* 2nd time: left-click (press only)
* NB: press-only means that when the button is pressed, the
* KeyPress/ButtonPress and KeyRelease/ButtonRelease events are generated
* together sequentially; instead when the button is released, no event is
* generated !
*
* With the command
* 10<xx>0a 3500af03 (where <xx> is the mouse id),
* the mouse reacts differently:
* - it never sends a keyboard key event
* - for the three mouse button it sends:
* middle button press 11<xx>0a 3500af00...
* side 1 button (forward) press 11<xx>0a 3500b000...
* side 2 button (backward) press 11<xx>0a 3500ae00...
* middle/side1/side2 button release 11<xx>0a 35000000...
*/
static
const
u8
m560_config_parameter
[]
=
{
0x00
,
0xaf
,
0x03
};
struct
m560_private_data
{
struct
input_dev
*
input
;
};
/* how buttons are mapped in the report */
#define M560_MOUSE_BTN_LEFT 0x01
#define M560_MOUSE_BTN_RIGHT 0x02
#define M560_MOUSE_BTN_WHEEL_LEFT 0x08
#define M560_MOUSE_BTN_WHEEL_RIGHT 0x10
#define M560_SUB_ID 0x0a
#define M560_BUTTON_MODE_REGISTER 0x35
static
int
m560_send_config_command
(
struct
hid_device
*
hdev
,
bool
connected
)
{
struct
hidpp_report
response
;
struct
hidpp_device
*
hidpp_dev
;
hidpp_dev
=
hid_get_drvdata
(
hdev
);
if
(
!
connected
)
return
-
ENODEV
;
return
hidpp_send_rap_command_sync
(
hidpp_dev
,
REPORT_ID_HIDPP_SHORT
,
M560_SUB_ID
,
M560_BUTTON_MODE_REGISTER
,
(
u8
*
)
m560_config_parameter
,
sizeof
(
m560_config_parameter
),
&
response
);
}
static
int
m560_allocate
(
struct
hid_device
*
hdev
)
{
struct
hidpp_device
*
hidpp
=
hid_get_drvdata
(
hdev
);
struct
m560_private_data
*
d
;
d
=
devm_kzalloc
(
&
hdev
->
dev
,
sizeof
(
struct
m560_private_data
),
GFP_KERNEL
);
if
(
!
d
)
return
-
ENOMEM
;
hidpp
->
private_data
=
d
;
return
0
;
};
static
int
m560_raw_event
(
struct
hid_device
*
hdev
,
u8
*
data
,
int
size
)
{
struct
hidpp_device
*
hidpp
=
hid_get_drvdata
(
hdev
);
struct
m560_private_data
*
mydata
=
hidpp
->
private_data
;
/* sanity check */
if
(
!
mydata
||
!
mydata
->
input
)
{
hid_err
(
hdev
,
"error in parameter
\n
"
);
return
-
EINVAL
;
}
if
(
size
<
7
)
{
hid_err
(
hdev
,
"error in report
\n
"
);
return
0
;
}
if
(
data
[
0
]
==
REPORT_ID_HIDPP_LONG
&&
data
[
2
]
==
M560_SUB_ID
&&
data
[
6
]
==
0x00
)
{
/*
* m560 mouse report for middle, forward and backward button
*
* data[0] = 0x11
* data[1] = device-id
* data[2] = 0x0a
* data[5] = 0xaf -> middle
* 0xb0 -> forward
* 0xae -> backward
* 0x00 -> release all
* data[6] = 0x00
*/
switch
(
data
[
5
])
{
case
0xaf
:
input_report_key
(
mydata
->
input
,
BTN_MIDDLE
,
1
);
break
;
case
0xb0
:
input_report_key
(
mydata
->
input
,
BTN_FORWARD
,
1
);
break
;
case
0xae
:
input_report_key
(
mydata
->
input
,
BTN_BACK
,
1
);
break
;
case
0x00
:
input_report_key
(
mydata
->
input
,
BTN_BACK
,
0
);
input_report_key
(
mydata
->
input
,
BTN_FORWARD
,
0
);
input_report_key
(
mydata
->
input
,
BTN_MIDDLE
,
0
);
break
;
default:
hid_err
(
hdev
,
"error in report
\n
"
);
return
0
;
}
input_sync
(
mydata
->
input
);
}
else
if
(
data
[
0
]
==
0x02
)
{
/*
* Logitech M560 mouse report
*
* data[0] = type (0x02)
* data[1..2] = buttons
* data[3..5] = xy
* data[6] = wheel
*/
int
v
;
input_report_key
(
mydata
->
input
,
BTN_LEFT
,
!!
(
data
[
1
]
&
M560_MOUSE_BTN_LEFT
));
input_report_key
(
mydata
->
input
,
BTN_RIGHT
,
!!
(
data
[
1
]
&
M560_MOUSE_BTN_RIGHT
));
if
(
data
[
1
]
&
M560_MOUSE_BTN_WHEEL_LEFT
)
input_report_rel
(
mydata
->
input
,
REL_HWHEEL
,
-
1
);
else
if
(
data
[
1
]
&
M560_MOUSE_BTN_WHEEL_RIGHT
)
input_report_rel
(
mydata
->
input
,
REL_HWHEEL
,
1
);
v
=
hid_snto32
(
hid_field_extract
(
hdev
,
data
+
3
,
0
,
12
),
12
);
input_report_rel
(
mydata
->
input
,
REL_X
,
v
);
v
=
hid_snto32
(
hid_field_extract
(
hdev
,
data
+
3
,
12
,
12
),
12
);
input_report_rel
(
mydata
->
input
,
REL_Y
,
v
);
v
=
hid_snto32
(
data
[
6
],
8
);
input_report_rel
(
mydata
->
input
,
REL_WHEEL
,
v
);
input_sync
(
mydata
->
input
);
}
return
1
;
}
static
void
m560_populate_input
(
struct
hidpp_device
*
hidpp
,
struct
input_dev
*
input_dev
,
bool
origin_is_hid_core
)
{
struct
m560_private_data
*
mydata
=
hidpp
->
private_data
;
mydata
->
input
=
input_dev
;
__set_bit
(
EV_KEY
,
mydata
->
input
->
evbit
);
__set_bit
(
BTN_MIDDLE
,
mydata
->
input
->
keybit
);
__set_bit
(
BTN_RIGHT
,
mydata
->
input
->
keybit
);
__set_bit
(
BTN_LEFT
,
mydata
->
input
->
keybit
);
__set_bit
(
BTN_BACK
,
mydata
->
input
->
keybit
);
__set_bit
(
BTN_FORWARD
,
mydata
->
input
->
keybit
);
__set_bit
(
EV_REL
,
mydata
->
input
->
evbit
);
__set_bit
(
REL_X
,
mydata
->
input
->
relbit
);
__set_bit
(
REL_Y
,
mydata
->
input
->
relbit
);
__set_bit
(
REL_WHEEL
,
mydata
->
input
->
relbit
);
__set_bit
(
REL_HWHEEL
,
mydata
->
input
->
relbit
);
}
static
int
m560_input_mapping
(
struct
hid_device
*
hdev
,
struct
hid_input
*
hi
,
struct
hid_field
*
field
,
struct
hid_usage
*
usage
,
unsigned
long
**
bit
,
int
*
max
)
{
return
-
1
;
}
/* -------------------------------------------------------------------------- */
/* Generic HID++ devices */
/* -------------------------------------------------------------------------- */
...
...
@@ -942,6 +1144,9 @@ static int hidpp_input_mapping(struct hid_device *hdev, struct hid_input *hi,
if
(
hidpp
->
quirks
&
HIDPP_QUIRK_CLASS_WTP
)
return
wtp_input_mapping
(
hdev
,
hi
,
field
,
usage
,
bit
,
max
);
else
if
(
hidpp
->
quirks
&
HIDPP_QUIRK_CLASS_M560
&&
field
->
application
!=
HID_GD_MOUSE
)
return
m560_input_mapping
(
hdev
,
hi
,
field
,
usage
,
bit
,
max
);
return
0
;
}
...
...
@@ -951,6 +1156,8 @@ static void hidpp_populate_input(struct hidpp_device *hidpp,
{
if
(
hidpp
->
quirks
&
HIDPP_QUIRK_CLASS_WTP
)
wtp_populate_input
(
hidpp
,
input
,
origin_is_hid_core
);
else
if
(
hidpp
->
quirks
&
HIDPP_QUIRK_CLASS_M560
)
m560_populate_input
(
hidpp
,
input
,
origin_is_hid_core
);
}
static
void
hidpp_input_configured
(
struct
hid_device
*
hdev
,
...
...
@@ -1038,6 +1245,8 @@ static int hidpp_raw_event(struct hid_device *hdev, struct hid_report *report,
if
(
hidpp
->
quirks
&
HIDPP_QUIRK_CLASS_WTP
)
return
wtp_raw_event
(
hdev
,
data
,
size
);
else
if
(
hidpp
->
quirks
&
HIDPP_QUIRK_CLASS_M560
)
return
m560_raw_event
(
hdev
,
data
,
size
);
return
0
;
}
...
...
@@ -1115,6 +1324,10 @@ static void hidpp_connect_event(struct hidpp_device *hidpp)
ret
=
wtp_connect
(
hdev
,
connected
);
if
(
ret
)
return
;
}
else
if
(
hidpp
->
quirks
&
HIDPP_QUIRK_CLASS_M560
)
{
ret
=
m560_send_config_command
(
hdev
,
connected
);
if
(
ret
)
return
;
}
if
(
!
connected
||
hidpp
->
delayed_input
)
...
...
@@ -1190,7 +1403,11 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
if
(
hidpp
->
quirks
&
HIDPP_QUIRK_CLASS_WTP
)
{
ret
=
wtp_allocate
(
hdev
,
id
);
if
(
ret
)
goto
wtp_allocate_fail
;
goto
allocate_fail
;
}
else
if
(
hidpp
->
quirks
&
HIDPP_QUIRK_CLASS_M560
)
{
ret
=
m560_allocate
(
hdev
);
if
(
ret
)
goto
allocate_fail
;
}
INIT_WORK
(
&
hidpp
->
work
,
delayed_work_cb
);
...
...
@@ -1253,7 +1470,7 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
hid_parse_fail:
cancel_work_sync
(
&
hidpp
->
work
);
mutex_destroy
(
&
hidpp
->
send_mutex
);
wtp_
allocate_fail:
allocate_fail:
hid_set_drvdata
(
hdev
,
NULL
);
return
ret
;
}
...
...
@@ -1281,6 +1498,10 @@ static const struct hid_device_id hidpp_devices[] = {
HID_BLUETOOTH_DEVICE
(
USB_VENDOR_ID_LOGITECH
,
USB_DEVICE_ID_LOGITECH_T651
),
.
driver_data
=
HIDPP_QUIRK_CLASS_WTP
},
{
/* Mouse logitech M560 */
HID_DEVICE
(
BUS_USB
,
HID_GROUP_LOGITECH_DJ_DEVICE
,
USB_VENDOR_ID_LOGITECH
,
0x402d
),
.
driver_data
=
HIDPP_QUIRK_DELAYED_INIT
|
HIDPP_QUIRK_CLASS_M560
},
{
HID_DEVICE
(
BUS_USB
,
HID_GROUP_LOGITECH_DJ_DEVICE
,
USB_VENDOR_ID_LOGITECH
,
HID_ANY_ID
)},
...
...
drivers/hid/hid-microsoft.c
浏览文件 @
817a3125
...
...
@@ -280,6 +280,8 @@ static const struct hid_device_id ms_devices[] = {
.
driver_data
=
MS_HIDINPUT
},
{
HID_USB_DEVICE
(
USB_VENDOR_ID_MICROSOFT
,
USB_DEVICE_ID_MS_TYPE_COVER_3_JP
),
.
driver_data
=
MS_HIDINPUT
},
{
HID_USB_DEVICE
(
USB_VENDOR_ID_MICROSOFT
,
USB_DEVICE_ID_MS_POWER_COVER
),
.
driver_data
=
MS_HIDINPUT
},
{
HID_BLUETOOTH_DEVICE
(
USB_VENDOR_ID_MICROSOFT
,
USB_DEVICE_ID_MS_PRESENTER_8K_BT
),
.
driver_data
=
MS_PRESENTER
},
...
...
drivers/hid/hid-prodikeys.c
浏览文件 @
817a3125
...
...
@@ -395,7 +395,6 @@ static int pcmidi_handle_report4(struct pcmidi_snd *pm, u8 *data)
/* break keys */
for
(
bit_index
=
0
;
bit_index
<
24
;
bit_index
++
)
{
key
=
pm
->
last_key
[
bit_index
];
if
(
!
((
0x01
<<
bit_index
)
&
bit_mask
))
{
input_event
(
pm
->
input_ep82
,
EV_KEY
,
pm
->
last_key
[
bit_index
],
0
);
...
...
drivers/hid/hid-rmi.c
浏览文件 @
817a3125
...
...
@@ -29,9 +29,9 @@
#define RMI_SET_RMI_MODE_REPORT_ID 0x0f
/* Feature Report */
/* flags */
#define RMI_READ_REQUEST_PENDING
BIT(0)
#define RMI_READ_DATA_PENDING
BIT(1)
#define RMI_STARTED
BIT(2)
#define RMI_READ_REQUEST_PENDING
0
#define RMI_READ_DATA_PENDING
1
#define RMI_STARTED
2
/* device flags */
#define RMI_DEVICE BIT(0)
...
...
drivers/hid/i2c-hid/i2c-hid.c
浏览文件 @
817a3125
...
...
@@ -42,9 +42,9 @@
#include <linux/i2c/i2c-hid.h>
/* flags */
#define I2C_HID_STARTED
(1 << 0)
#define I2C_HID_RESET_PENDING
(1 << 1)
#define I2C_HID_READ_PENDING
(1 << 2)
#define I2C_HID_STARTED
0
#define I2C_HID_RESET_PENDING
1
#define I2C_HID_READ_PENDING
2
#define I2C_HID_PWR_ON 0x00
#define I2C_HID_PWR_SLEEP 0x01
...
...
drivers/hid/usbhid/hid-quirks.c
浏览文件 @
817a3125
...
...
@@ -90,6 +90,7 @@ static const struct hid_blacklist {
{
USB_VENDOR_ID_MGE
,
USB_DEVICE_ID_MGE_UPS
,
HID_QUIRK_NOGET
},
{
USB_VENDOR_ID_MICROSOFT
,
USB_DEVICE_ID_MS_TYPE_COVER_3
,
HID_QUIRK_NO_INIT_REPORTS
},
{
USB_VENDOR_ID_MICROSOFT
,
USB_DEVICE_ID_MS_TYPE_COVER_3_JP
,
HID_QUIRK_NO_INIT_REPORTS
},
{
USB_VENDOR_ID_MICROSOFT
,
USB_DEVICE_ID_MS_POWER_COVER
,
HID_QUIRK_NO_INIT_REPORTS
},
{
USB_VENDOR_ID_MSI
,
USB_DEVICE_ID_MSI_GT683R_LED_PANEL
,
HID_QUIRK_NO_INIT_REPORTS
},
{
USB_VENDOR_ID_NEXIO
,
USB_DEVICE_ID_NEXIO_MULTITOUCH_PTI0750
,
HID_QUIRK_NO_INIT_REPORTS
},
{
USB_VENDOR_ID_NOVATEK
,
USB_DEVICE_ID_NOVATEK_MOUSE
,
HID_QUIRK_NO_INIT_REPORTS
},
...
...
@@ -142,6 +143,9 @@ static const struct hid_blacklist {
{
USB_VENDOR_ID_SYNAPTICS
,
USB_DEVICE_ID_SYNAPTICS_QUAD_HD
,
HID_QUIRK_NO_INIT_REPORTS
},
{
USB_VENDOR_ID_SYNAPTICS
,
USB_DEVICE_ID_SYNAPTICS_TP_V103
,
HID_QUIRK_NO_INIT_REPORTS
},
{
USB_VENDOR_ID_HOLTEK_ALT
,
USB_DEVICE_ID_HOLTEK_ALT_KEYBOARD_A096
,
HID_QUIRK_NO_INIT_INPUT_REPORTS
},
{
USB_VENDOR_ID_MULTIPLE_1781
,
USB_DEVICE_ID_RAPHNET_4NES4SNES_OLD
,
HID_QUIRK_MULTI_INPUT
},
{
USB_VENDOR_ID_DRACAL_RAPHNET
,
USB_DEVICE_ID_RAPHNET_2NES2SNES
,
HID_QUIRK_MULTI_INPUT
},
{
USB_VENDOR_ID_DRACAL_RAPHNET
,
USB_DEVICE_ID_RAPHNET_4NES4SNES
,
HID_QUIRK_MULTI_INPUT
},
{
0
,
0
}
};
...
...
drivers/usb/misc/ldusb.c
浏览文件 @
817a3125
...
...
@@ -69,12 +69,6 @@
#define USB_DEVICE_ID_LD_HYBRID 0x2090
/* USB Product ID of Automotive Hybrid */
#define USB_DEVICE_ID_LD_HEATCONTROL 0x20A0
/* USB Product ID of Heat control */
#define USB_VENDOR_ID_VERNIER 0x08f7
#define USB_DEVICE_ID_VERNIER_GOTEMP 0x0002
#define USB_DEVICE_ID_VERNIER_SKIP 0x0003
#define USB_DEVICE_ID_VERNIER_CYCLOPS 0x0004
#define USB_DEVICE_ID_VERNIER_LCSPEC 0x0006
#ifdef CONFIG_USB_DYNAMIC_MINORS
#define USB_LD_MINOR_BASE 0
#else
...
...
@@ -115,10 +109,6 @@ static const struct usb_device_id ld_usb_table[] = {
{
USB_DEVICE
(
USB_VENDOR_ID_LD
,
USB_DEVICE_ID_LD_MCT
)
},
{
USB_DEVICE
(
USB_VENDOR_ID_LD
,
USB_DEVICE_ID_LD_HYBRID
)
},
{
USB_DEVICE
(
USB_VENDOR_ID_LD
,
USB_DEVICE_ID_LD_HEATCONTROL
)
},
{
USB_DEVICE
(
USB_VENDOR_ID_VERNIER
,
USB_DEVICE_ID_VERNIER_GOTEMP
)
},
{
USB_DEVICE
(
USB_VENDOR_ID_VERNIER
,
USB_DEVICE_ID_VERNIER_SKIP
)
},
{
USB_DEVICE
(
USB_VENDOR_ID_VERNIER
,
USB_DEVICE_ID_VERNIER_CYCLOPS
)
},
{
USB_DEVICE
(
USB_VENDOR_ID_VERNIER
,
USB_DEVICE_ID_VERNIER_LCSPEC
)
},
{
}
/* Terminating entry */
};
MODULE_DEVICE_TABLE
(
usb
,
ld_usb_table
);
...
...
include/linux/hid.h
浏览文件 @
817a3125
...
...
@@ -815,6 +815,8 @@ void hid_disconnect(struct hid_device *hid);
const
struct
hid_device_id
*
hid_match_id
(
struct
hid_device
*
hdev
,
const
struct
hid_device_id
*
id
);
s32
hid_snto32
(
__u32
value
,
unsigned
n
);
__u32
hid_field_extract
(
const
struct
hid_device
*
hid
,
__u8
*
report
,
unsigned
offset
,
unsigned
n
);
/**
* hid_device_io_start - enable HID input during probe, remove
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录