Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
linux
Commits
5a12d86c
Commit
5a12d86c
authored
Aug 20, 2018
by
Jiri Kosina
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'for-4.19/elan' into for-linus
Resolution/pressure fixes and new device support for hid-elan
parents
415d2b33
e7ad3dc9
Changes
2
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
184 additions
and
53 deletions
+184
-53
drivers/hid/hid-elan.c
drivers/hid/hid-elan.c
+182
-53
drivers/hid/hid-ids.h
drivers/hid/hid-ids.h
+2
-0
No files found.
drivers/hid/hid-elan.c
View file @
5a12d86c
...
...
@@ -19,38 +19,49 @@
#include "hid-ids.h"
#define ELAN_MT_I2C 0x5d
#define ELAN_SINGLE_FINGER 0x81
#define ELAN_MT_FIRST_FINGER 0x82
#define ELAN_MT_SECOND_FINGER 0x83
#define ELAN_INPUT_REPORT_SIZE 8
#define ELAN_I2C_REPORT_SIZE 32
#define ELAN_FINGER_DATA_LEN 5
#define ELAN_MAX_FINGERS 5
#define ELAN_MAX_PRESSURE 255
#define ELAN_TP_USB_INTF 1
#define ELAN_FEATURE_REPORT 0x0d
#define ELAN_FEATURE_SIZE 5
#define ELAN_PARAM_MAX_X 6
#define ELAN_PARAM_MAX_Y 7
#define ELAN_PARAM_RES 8
#define ELAN_MUTE_LED_REPORT 0xBC
#define ELAN_LED_REPORT_SIZE 8
struct
elan_touchpad_settings
{
u8
max_fingers
;
u16
max_x
;
u16
max_y
;
u8
max_area_x
;
u8
max_area_y
;
u8
max_w
;
int
usb_bInterfaceNumber
;
};
#define ELAN_HAS_LED BIT(0)
struct
elan_drvdata
{
struct
input_dev
*
input
;
u8
prev_report
[
ELAN_INPUT_REPORT_SIZE
];
struct
led_classdev
mute_led
;
u8
mute_led_state
;
struct
elan_touchpad_settings
*
settings
;
u16
max_x
;
u16
max_y
;
u16
res_x
;
u16
res_y
;
};
static
int
is_not_elan_touchpad
(
struct
hid_device
*
hdev
)
{
if
(
hdev
->
bus
==
BUS_USB
)
{
struct
usb_interface
*
intf
=
to_usb_interface
(
hdev
->
dev
.
parent
);
struct
elan_drvdata
*
drvdata
=
hid_get_drvdata
(
hdev
);
return
(
intf
->
altsetting
->
desc
.
bInterfaceNumber
!=
drvdata
->
settings
->
usb_bInterfaceNumber
);
return
(
intf
->
altsetting
->
desc
.
bInterfaceNumber
!=
ELAN_TP_USB_INTF
);
}
return
0
;
}
static
int
elan_input_mapping
(
struct
hid_device
*
hdev
,
struct
hid_input
*
hi
,
...
...
@@ -62,12 +73,86 @@ static int elan_input_mapping(struct hid_device *hdev, struct hid_input *hi,
if
(
field
->
report
->
id
==
ELAN_SINGLE_FINGER
||
field
->
report
->
id
==
ELAN_MT_FIRST_FINGER
||
field
->
report
->
id
==
ELAN_MT_SECOND_FINGER
)
field
->
report
->
id
==
ELAN_MT_SECOND_FINGER
||
field
->
report
->
id
==
ELAN_MT_I2C
)
return
-
1
;
return
0
;
}
static
int
elan_get_device_param
(
struct
hid_device
*
hdev
,
unsigned
char
*
dmabuf
,
unsigned
char
param
)
{
int
ret
;
dmabuf
[
0
]
=
ELAN_FEATURE_REPORT
;
dmabuf
[
1
]
=
0x05
;
dmabuf
[
2
]
=
0x03
;
dmabuf
[
3
]
=
param
;
dmabuf
[
4
]
=
0x01
;
ret
=
hid_hw_raw_request
(
hdev
,
ELAN_FEATURE_REPORT
,
dmabuf
,
ELAN_FEATURE_SIZE
,
HID_FEATURE_REPORT
,
HID_REQ_SET_REPORT
);
if
(
ret
!=
ELAN_FEATURE_SIZE
)
{
hid_err
(
hdev
,
"Set report error for parm %d: %d
\n
"
,
param
,
ret
);
return
ret
;
}
ret
=
hid_hw_raw_request
(
hdev
,
ELAN_FEATURE_REPORT
,
dmabuf
,
ELAN_FEATURE_SIZE
,
HID_FEATURE_REPORT
,
HID_REQ_GET_REPORT
);
if
(
ret
!=
ELAN_FEATURE_SIZE
)
{
hid_err
(
hdev
,
"Get report error for parm %d: %d
\n
"
,
param
,
ret
);
return
ret
;
}
return
0
;
}
static
unsigned
int
elan_convert_res
(
char
val
)
{
/*
* (value from firmware) * 10 + 790 = dpi
* dpi * 10 / 254 = dots/mm
*/
return
(
val
*
10
+
790
)
*
10
/
254
;
}
static
int
elan_get_device_params
(
struct
hid_device
*
hdev
)
{
struct
elan_drvdata
*
drvdata
=
hid_get_drvdata
(
hdev
);
unsigned
char
*
dmabuf
;
int
ret
;
dmabuf
=
kmalloc
(
ELAN_FEATURE_SIZE
,
GFP_KERNEL
);
if
(
!
dmabuf
)
return
-
ENOMEM
;
ret
=
elan_get_device_param
(
hdev
,
dmabuf
,
ELAN_PARAM_MAX_X
);
if
(
ret
)
goto
err
;
drvdata
->
max_x
=
(
dmabuf
[
4
]
<<
8
)
|
dmabuf
[
3
];
ret
=
elan_get_device_param
(
hdev
,
dmabuf
,
ELAN_PARAM_MAX_Y
);
if
(
ret
)
goto
err
;
drvdata
->
max_y
=
(
dmabuf
[
4
]
<<
8
)
|
dmabuf
[
3
];
ret
=
elan_get_device_param
(
hdev
,
dmabuf
,
ELAN_PARAM_RES
);
if
(
ret
)
goto
err
;
drvdata
->
res_x
=
elan_convert_res
(
dmabuf
[
3
]);
drvdata
->
res_y
=
elan_convert_res
(
dmabuf
[
4
]);
err:
kfree
(
dmabuf
);
return
ret
;
}
static
int
elan_input_configured
(
struct
hid_device
*
hdev
,
struct
hid_input
*
hi
)
{
int
ret
;
...
...
@@ -77,6 +162,10 @@ static int elan_input_configured(struct hid_device *hdev, struct hid_input *hi)
if
(
is_not_elan_touchpad
(
hdev
))
return
0
;
ret
=
elan_get_device_params
(
hdev
);
if
(
ret
)
return
ret
;
input
=
devm_input_allocate_device
(
&
hdev
->
dev
);
if
(
!
input
)
return
-
ENOMEM
;
...
...
@@ -90,25 +179,25 @@ static int elan_input_configured(struct hid_device *hdev, struct hid_input *hi)
input
->
id
.
version
=
hdev
->
version
;
input
->
dev
.
parent
=
&
hdev
->
dev
;
input_set_abs_params
(
input
,
ABS_MT_POSITION_X
,
0
,
drvdata
->
settings
->
max_x
,
0
,
0
);
input_set_abs_params
(
input
,
ABS_MT_POSITION_Y
,
0
,
drvdata
->
settings
->
max_y
,
0
,
0
);
input_set_abs_params
(
input
,
ABS_MT_TOUCH_MAJOR
,
0
,
drvdata
->
settings
->
max_fingers
,
0
,
0
);
input_set_abs_params
(
input
,
ABS_TOOL_WIDTH
,
0
,
drvdata
->
settings
->
max_w
,
0
,
0
);
input_set_abs_params
(
input
,
ABS_MT_POSITION_X
,
0
,
drvdata
->
max_x
,
0
,
0
);
input_set_abs_params
(
input
,
ABS_MT_POSITION_Y
,
0
,
drvdata
->
max_y
,
0
,
0
);
input_set_abs_params
(
input
,
ABS_MT_PRESSURE
,
0
,
ELAN_MAX_PRESSURE
,
0
,
0
);
__set_bit
(
BTN_LEFT
,
input
->
keybit
);
__set_bit
(
INPUT_PROP_BUTTONPAD
,
input
->
propbit
);
ret
=
input_mt_init_slots
(
input
,
drvdata
->
settings
->
max_fingers
,
INPUT_MT_POINTER
);
ret
=
input_mt_init_slots
(
input
,
ELAN_MAX_FINGERS
,
INPUT_MT_POINTER
);
if
(
ret
)
{
hid_err
(
hdev
,
"Failed to init elan MT slots: %d
\n
"
,
ret
);
return
ret
;
}
input_abs_set_res
(
input
,
ABS_X
,
drvdata
->
res_x
);
input_abs_set_res
(
input
,
ABS_Y
,
drvdata
->
res_y
);
ret
=
input_register_device
(
input
);
if
(
ret
)
{
hid_err
(
hdev
,
"Failed to register elan input device: %d
\n
"
,
...
...
@@ -126,7 +215,7 @@ static void elan_report_mt_slot(struct elan_drvdata *drvdata, u8 *data,
unsigned
int
slot_num
)
{
struct
input_dev
*
input
=
drvdata
->
input
;
int
x
,
y
,
w
;
int
x
,
y
,
p
;
bool
active
=
!!
data
;
...
...
@@ -134,17 +223,17 @@ static void elan_report_mt_slot(struct elan_drvdata *drvdata, u8 *data,
input_mt_report_slot_state
(
input
,
MT_TOOL_FINGER
,
active
);
if
(
active
)
{
x
=
((
data
[
0
]
&
0xF0
)
<<
4
)
|
data
[
1
];
y
=
drvdata
->
settings
->
max_y
-
y
=
drvdata
->
max_y
-
(((
data
[
0
]
&
0x07
)
<<
8
)
|
data
[
2
]);
w
=
data
[
4
];
p
=
data
[
4
];
input_report_abs
(
input
,
ABS_MT_POSITION_X
,
x
);
input_report_abs
(
input
,
ABS_MT_POSITION_Y
,
y
);
input_report_abs
(
input
,
ABS_
TOOL_WIDTH
,
w
);
input_report_abs
(
input
,
ABS_
MT_PRESSURE
,
p
);
}
}
static
void
elan_report_input
(
struct
elan_drvdata
*
drvdata
,
u8
*
data
)
static
void
elan_
usb_
report_input
(
struct
elan_drvdata
*
drvdata
,
u8
*
data
)
{
int
i
;
struct
input_dev
*
input
=
drvdata
->
input
;
...
...
@@ -162,7 +251,7 @@ static void elan_report_input(struct elan_drvdata *drvdata, u8 *data)
* byte 5: x8 x7 x6 x5 x4 x3 x2 x1
* byte 6: y8 y7 y6 y5 y4 y3 y2 y1
* byte 7: sy4 sy3 sy2 sy1 sx4 sx3 sx2 sx1
* byte 8:
w8 w7 w6 w5 w4 w3 w2 w
1
* byte 8:
p8 p7 p6 p5 p4 p3 p2 p
1
*
* packet structure for ELAN_MT_SECOND_FINGER:
*
...
...
@@ -171,19 +260,21 @@ static void elan_report_input(struct elan_drvdata *drvdata, u8 *data)
* byte 3: x8 x7 x6 x5 x4 x3 x2 x1
* byte 4: y8 y7 y6 y5 y4 y3 y2 y1
* byte 5: sy4 sy3 sy2 sy1 sx4 sx3 sx2 sx1
* byte 6:
w8 w7 w6 w5 w4 w3 w2 w
1
* byte 6:
p8 p7 p6 p5 p4 p3 p2 p
1
* byte 7: 0 0 0 0 0 0 0 0
* byte 8: 0 0 0 0 0 0 0 0
*
* f5-f1: finger touch bits
* L: clickpad button
* sy / sx: not sure yet, but this looks like rectangular
* area for finger
* w: looks like finger width
* sy / sx: finger width / height expressed in traces, the total number
* of traces can be queried by doing a HID_REQ_SET_REPORT
* { 0x0d, 0x05, 0x03, 0x05, 0x01 } followed by a GET, in the
* returned buf, buf[3]=no-x-traces, buf[4]=no-y-traces.
* p: pressure
*/
if
(
data
[
0
]
==
ELAN_SINGLE_FINGER
)
{
for
(
i
=
0
;
i
<
drvdata
->
settings
->
max_fingers
;
i
++
)
{
for
(
i
=
0
;
i
<
ELAN_MAX_FINGERS
;
i
++
)
{
if
(
data
[
2
]
&
BIT
(
i
+
3
))
elan_report_mt_slot
(
drvdata
,
data
+
3
,
i
);
else
...
...
@@ -210,7 +301,7 @@ static void elan_report_input(struct elan_drvdata *drvdata, u8 *data)
if
(
prev_report
[
0
]
!=
ELAN_MT_FIRST_FINGER
)
return
;
for
(
i
=
0
;
i
<
drvdata
->
settings
->
max_fingers
;
i
++
)
{
for
(
i
=
0
;
i
<
ELAN_MAX_FINGERS
;
i
++
)
{
if
(
prev_report
[
2
]
&
BIT
(
i
+
3
))
{
if
(
!
first
)
{
first
=
1
;
...
...
@@ -229,6 +320,46 @@ static void elan_report_input(struct elan_drvdata *drvdata, u8 *data)
input_sync
(
input
);
}
static
void
elan_i2c_report_input
(
struct
elan_drvdata
*
drvdata
,
u8
*
data
)
{
struct
input_dev
*
input
=
drvdata
->
input
;
u8
*
finger_data
;
int
i
;
/*
* Elan MT touchpads in i2c mode send finger data in the same format
* as in USB mode, but then with all fingers in a single packet.
*
* packet structure for ELAN_MT_I2C:
*
* byte 1: 1 0 0 1 1 1 0 1 // 0x5d
* byte 2: f5 f4 f3 f2 f1 0 0 L
* byte 3: x12 x11 x10 x9 0? y11 y10 y9
* byte 4: x8 x7 x6 x5 x4 x3 x2 x1
* byte 5: y8 y7 y6 y5 y4 y3 y2 y1
* byte 6: sy4 sy3 sy2 sy1 sx4 sx3 sx2 sx1
* byte 7: p8 p7 p6 p5 p4 p3 p2 p1
* byte 8-12: Same as byte 3-7 for second finger down
* byte 13-17: Same as byte 3-7 for third finger down
* byte 18-22: Same as byte 3-7 for fourth finger down
* byte 23-27: Same as byte 3-7 for fifth finger down
*/
finger_data
=
data
+
2
;
for
(
i
=
0
;
i
<
ELAN_MAX_FINGERS
;
i
++
)
{
if
(
data
[
1
]
&
BIT
(
i
+
3
))
{
elan_report_mt_slot
(
drvdata
,
finger_data
,
i
);
finger_data
+=
ELAN_FINGER_DATA_LEN
;
}
else
{
elan_report_mt_slot
(
drvdata
,
NULL
,
i
);
}
}
input_report_key
(
input
,
BTN_LEFT
,
data
[
1
]
&
0x01
);
input_mt_sync_frame
(
input
);
input_sync
(
input
);
}
static
int
elan_raw_event
(
struct
hid_device
*
hdev
,
struct
hid_report
*
report
,
u8
*
data
,
int
size
)
{
...
...
@@ -241,11 +372,16 @@ static int elan_raw_event(struct hid_device *hdev,
data
[
0
]
==
ELAN_MT_FIRST_FINGER
||
data
[
0
]
==
ELAN_MT_SECOND_FINGER
)
{
if
(
size
==
ELAN_INPUT_REPORT_SIZE
)
{
elan_report_input
(
drvdata
,
data
);
elan_
usb_
report_input
(
drvdata
,
data
);
return
1
;
}
}
if
(
data
[
0
]
==
ELAN_MT_I2C
&&
size
==
ELAN_I2C_REPORT_SIZE
)
{
elan_i2c_report_input
(
drvdata
,
data
);
return
1
;
}
return
0
;
}
...
...
@@ -343,7 +479,6 @@ static int elan_probe(struct hid_device *hdev, const struct hid_device_id *id)
if
(
!
drvdata
)
return
-
ENOMEM
;
drvdata
->
settings
=
(
struct
elan_touchpad_settings
*
)
id
->
driver_data
;
hid_set_drvdata
(
hdev
,
drvdata
);
ret
=
hid_parse
(
hdev
);
...
...
@@ -371,9 +506,11 @@ static int elan_probe(struct hid_device *hdev, const struct hid_device_id *id)
if
(
ret
)
goto
err
;
if
(
id
->
driver_data
&
ELAN_HAS_LED
)
{
ret
=
elan_init_mute_led
(
hdev
);
if
(
ret
)
goto
err
;
}
return
0
;
err:
...
...
@@ -386,22 +523,14 @@ static void elan_remove(struct hid_device *hdev)
hid_hw_stop
(
hdev
);
}
static
const
struct
elan_touchpad_settings
hp_x2_10_touchpad_data
=
{
.
max_fingers
=
5
,
.
max_x
=
2930
,
.
max_y
=
1250
,
.
max_area_x
=
15
,
.
max_area_y
=
15
,
.
max_w
=
255
,
.
usb_bInterfaceNumber
=
1
,
};
static
const
struct
hid_device_id
elan_devices
[]
=
{
{
HID_USB_DEVICE
(
USB_VENDOR_ID_ELAN
,
USB_DEVICE_ID_HP_X2
),
.
driver_data
=
ELAN_HAS_LED
},
{
HID_USB_DEVICE
(
USB_VENDOR_ID_ELAN
,
USB_DEVICE_ID_HP_X2_10_COVER
),
(
kernel_ulong_t
)
&
hp_x2_10_touchpad_data
},
.
driver_data
=
ELAN_HAS_LED
},
{
HID_I2C_DEVICE
(
USB_VENDOR_ID_ELAN
,
USB_DEVICE_ID_TOSHIBA_CLICK_L9W
)
},
{
}
};
MODULE_DEVICE_TABLE
(
hid
,
elan_devices
);
static
struct
hid_driver
elan_driver
=
{
...
...
drivers/hid/hid-ids.h
View file @
5a12d86c
...
...
@@ -369,6 +369,8 @@
#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_A001 0xa001
#define USB_VENDOR_ID_ELAN 0x04f3
#define USB_DEVICE_ID_TOSHIBA_CLICK_L9W 0x0401
#define USB_DEVICE_ID_HP_X2 0x074d
#define USB_DEVICE_ID_HP_X2_10_COVER 0x0755
#define USB_VENDOR_ID_ELECOM 0x056e
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment