29
29
#include <linux/hid.h>
30
30
#include <linux/module.h>
31
31
#include <linux/input/mt.h>
32
+ #include <linux/usb.h> /* For to_usb_interface for T100 touchpad intf check */
32
33
33
34
#include "hid-ids.h"
34
35
38
39
MODULE_AUTHOR (
"Frederik Wenigwieser <[email protected] >" );
39
40
MODULE_DESCRIPTION ("Asus HID Keyboard and TouchPad" );
40
41
42
+ #define T100_TPAD_INTF 2
43
+
44
+ #define T100CHI_MOUSE_REPORT_ID 0x06
41
45
#define FEATURE_REPORT_ID 0x0d
42
46
#define INPUT_REPORT_ID 0x5d
43
47
#define FEATURE_KBD_REPORT_ID 0x5a
44
-
45
- #define INPUT_REPORT_SIZE 28
46
48
#define FEATURE_KBD_REPORT_SIZE 16
47
49
48
50
#define SUPPORT_KBD_BACKLIGHT BIT(0)
49
51
50
- #define MAX_CONTACTS 5
51
-
52
- #define MAX_X 2794
53
- #define MAX_Y 1758
54
52
#define MAX_TOUCH_MAJOR 8
55
53
#define MAX_PRESSURE 128
56
54
57
- #define CONTACT_DATA_SIZE 5
58
-
59
55
#define BTN_LEFT_MASK 0x01
60
56
#define CONTACT_TOOL_TYPE_MASK 0x80
61
57
#define CONTACT_X_MSB_MASK 0xf0
@@ -70,6 +66,7 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad");
70
66
#define QUIRK_NO_CONSUMER_USAGES BIT(4)
71
67
#define QUIRK_USE_KBD_BACKLIGHT BIT(5)
72
68
#define QUIRK_T100_KEYBOARD BIT(6)
69
+ #define QUIRK_T100CHI BIT(7)
73
70
74
71
#define I2C_KEYBOARD_QUIRKS (QUIRK_FIX_NOTEBOOK_REPORT | \
75
72
QUIRK_NO_INIT_REPORTS | \
@@ -88,19 +85,62 @@ struct asus_kbd_leds {
88
85
bool removed ;
89
86
};
90
87
88
+ struct asus_touchpad_info {
89
+ int max_x ;
90
+ int max_y ;
91
+ int res_x ;
92
+ int res_y ;
93
+ int contact_size ;
94
+ int max_contacts ;
95
+ };
96
+
91
97
struct asus_drvdata {
92
98
unsigned long quirks ;
93
99
struct input_dev * input ;
94
100
struct asus_kbd_leds * kbd_backlight ;
101
+ const struct asus_touchpad_info * tp ;
95
102
bool enable_backlight ;
96
103
};
97
104
98
- static void asus_report_contact_down (struct input_dev * input ,
105
+ static const struct asus_touchpad_info asus_i2c_tp = {
106
+ .max_x = 2794 ,
107
+ .max_y = 1758 ,
108
+ .contact_size = 5 ,
109
+ .max_contacts = 5 ,
110
+ };
111
+
112
+ static const struct asus_touchpad_info asus_t100ta_tp = {
113
+ .max_x = 2240 ,
114
+ .max_y = 1120 ,
115
+ .res_x = 30 , /* units/mm */
116
+ .res_y = 27 , /* units/mm */
117
+ .contact_size = 5 ,
118
+ .max_contacts = 5 ,
119
+ };
120
+
121
+ static const struct asus_touchpad_info asus_t100chi_tp = {
122
+ .max_x = 2640 ,
123
+ .max_y = 1320 ,
124
+ .res_x = 31 , /* units/mm */
125
+ .res_y = 29 , /* units/mm */
126
+ .contact_size = 3 ,
127
+ .max_contacts = 4 ,
128
+ };
129
+
130
+ static void asus_report_contact_down (struct asus_drvdata * drvdat ,
99
131
int toolType , u8 * data )
100
132
{
101
- int touch_major , pressure ;
102
- int x = (data [0 ] & CONTACT_X_MSB_MASK ) << 4 | data [1 ];
103
- int y = MAX_Y - ((data [0 ] & CONTACT_Y_MSB_MASK ) << 8 | data [2 ]);
133
+ struct input_dev * input = drvdat -> input ;
134
+ int touch_major , pressure , x , y ;
135
+
136
+ x = (data [0 ] & CONTACT_X_MSB_MASK ) << 4 | data [1 ];
137
+ y = drvdat -> tp -> max_y - ((data [0 ] & CONTACT_Y_MSB_MASK ) << 8 | data [2 ]);
138
+
139
+ input_report_abs (input , ABS_MT_POSITION_X , x );
140
+ input_report_abs (input , ABS_MT_POSITION_Y , y );
141
+
142
+ if (drvdat -> tp -> contact_size < 5 )
143
+ return ;
104
144
105
145
if (toolType == MT_TOOL_PALM ) {
106
146
touch_major = MAX_TOUCH_MAJOR ;
@@ -110,19 +150,20 @@ static void asus_report_contact_down(struct input_dev *input,
110
150
pressure = data [4 ] & CONTACT_PRESSURE_MASK ;
111
151
}
112
152
113
- input_report_abs (input , ABS_MT_POSITION_X , x );
114
- input_report_abs (input , ABS_MT_POSITION_Y , y );
115
153
input_report_abs (input , ABS_MT_TOUCH_MAJOR , touch_major );
116
154
input_report_abs (input , ABS_MT_PRESSURE , pressure );
117
155
}
118
156
119
157
/* Required for Synaptics Palm Detection */
120
- static void asus_report_tool_width (struct input_dev * input )
158
+ static void asus_report_tool_width (struct asus_drvdata * drvdat )
121
159
{
122
- struct input_mt * mt = input -> mt ;
160
+ struct input_mt * mt = drvdat -> input -> mt ;
123
161
struct input_mt_slot * oldest ;
124
162
int oldid , count , i ;
125
163
164
+ if (drvdat -> tp -> contact_size < 5 )
165
+ return ;
166
+
126
167
oldest = NULL ;
127
168
oldid = mt -> trkid ;
128
169
count = 0 ;
@@ -141,48 +182,51 @@ static void asus_report_tool_width(struct input_dev *input)
141
182
}
142
183
143
184
if (oldest ) {
144
- input_report_abs (input , ABS_TOOL_WIDTH ,
185
+ input_report_abs (drvdat -> input , ABS_TOOL_WIDTH ,
145
186
input_mt_get_value (oldest , ABS_MT_TOUCH_MAJOR ));
146
187
}
147
188
}
148
189
149
- static void asus_report_input (struct input_dev * input , u8 * data )
190
+ static int asus_report_input (struct asus_drvdata * drvdat , u8 * data , int size )
150
191
{
151
- int i ;
192
+ int i , toolType = MT_TOOL_FINGER ;
152
193
u8 * contactData = data + 2 ;
153
194
154
- for (i = 0 ; i < MAX_CONTACTS ; i ++ ) {
195
+ if (size != 3 + drvdat -> tp -> contact_size * drvdat -> tp -> max_contacts )
196
+ return 0 ;
197
+
198
+ for (i = 0 ; i < drvdat -> tp -> max_contacts ; i ++ ) {
155
199
bool down = !!(data [1 ] & BIT (i + 3 ));
156
- int toolType = contactData [3 ] & CONTACT_TOOL_TYPE_MASK ?
200
+
201
+ if (drvdat -> tp -> contact_size >= 5 )
202
+ toolType = contactData [3 ] & CONTACT_TOOL_TYPE_MASK ?
157
203
MT_TOOL_PALM : MT_TOOL_FINGER ;
158
204
159
- input_mt_slot (input , i );
160
- input_mt_report_slot_state (input , toolType , down );
205
+ input_mt_slot (drvdat -> input , i );
206
+ input_mt_report_slot_state (drvdat -> input , toolType , down );
161
207
162
208
if (down ) {
163
- asus_report_contact_down (input , toolType , contactData );
164
- contactData += CONTACT_DATA_SIZE ;
209
+ asus_report_contact_down (drvdat , toolType , contactData );
210
+ contactData += drvdat -> tp -> contact_size ;
165
211
}
166
212
}
167
213
168
- input_report_key (input , BTN_LEFT , data [1 ] & BTN_LEFT_MASK );
169
- asus_report_tool_width (input );
214
+ input_report_key (drvdat -> input , BTN_LEFT , data [1 ] & BTN_LEFT_MASK );
215
+ asus_report_tool_width (drvdat );
216
+
217
+ input_mt_sync_frame (drvdat -> input );
218
+ input_sync (drvdat -> input );
170
219
171
- input_mt_sync_frame (input );
172
- input_sync (input );
220
+ return 1 ;
173
221
}
174
222
175
223
static int asus_raw_event (struct hid_device * hdev ,
176
224
struct hid_report * report , u8 * data , int size )
177
225
{
178
226
struct asus_drvdata * drvdata = hid_get_drvdata (hdev );
179
227
180
- if (drvdata -> quirks & QUIRK_IS_MULTITOUCH &&
181
- data [0 ] == INPUT_REPORT_ID &&
182
- size == INPUT_REPORT_SIZE ) {
183
- asus_report_input (drvdata -> input , data );
184
- return 1 ;
185
- }
228
+ if (drvdata -> tp && data [0 ] == INPUT_REPORT_ID )
229
+ return asus_report_input (drvdata , data , size );
186
230
187
231
return 0 ;
188
232
}
@@ -334,19 +378,35 @@ static int asus_input_configured(struct hid_device *hdev, struct hid_input *hi)
334
378
struct input_dev * input = hi -> input ;
335
379
struct asus_drvdata * drvdata = hid_get_drvdata (hdev );
336
380
337
- if (drvdata -> quirks & QUIRK_IS_MULTITOUCH ) {
381
+ /* T100CHI uses MULTI_INPUT, bind the touchpad to the mouse hid_input */
382
+ if (drvdata -> quirks & QUIRK_T100CHI &&
383
+ hi -> report -> id != T100CHI_MOUSE_REPORT_ID )
384
+ return 0 ;
385
+
386
+ if (drvdata -> tp ) {
338
387
int ret ;
339
388
340
- input_set_abs_params (input , ABS_MT_POSITION_X , 0 , MAX_X , 0 , 0 );
341
- input_set_abs_params (input , ABS_MT_POSITION_Y , 0 , MAX_Y , 0 , 0 );
342
- input_set_abs_params (input , ABS_TOOL_WIDTH , 0 , MAX_TOUCH_MAJOR , 0 , 0 );
343
- input_set_abs_params (input , ABS_MT_TOUCH_MAJOR , 0 , MAX_TOUCH_MAJOR , 0 , 0 );
344
- input_set_abs_params (input , ABS_MT_PRESSURE , 0 , MAX_PRESSURE , 0 , 0 );
389
+ input_set_abs_params (input , ABS_MT_POSITION_X , 0 ,
390
+ drvdata -> tp -> max_x , 0 , 0 );
391
+ input_set_abs_params (input , ABS_MT_POSITION_Y , 0 ,
392
+ drvdata -> tp -> max_y , 0 , 0 );
393
+ input_abs_set_res (input , ABS_MT_POSITION_X , drvdata -> tp -> res_x );
394
+ input_abs_set_res (input , ABS_MT_POSITION_Y , drvdata -> tp -> res_y );
395
+
396
+ if (drvdata -> tp -> contact_size >= 5 ) {
397
+ input_set_abs_params (input , ABS_TOOL_WIDTH , 0 ,
398
+ MAX_TOUCH_MAJOR , 0 , 0 );
399
+ input_set_abs_params (input , ABS_MT_TOUCH_MAJOR , 0 ,
400
+ MAX_TOUCH_MAJOR , 0 , 0 );
401
+ input_set_abs_params (input , ABS_MT_PRESSURE , 0 ,
402
+ MAX_PRESSURE , 0 , 0 );
403
+ }
345
404
346
405
__set_bit (BTN_LEFT , input -> keybit );
347
406
__set_bit (INPUT_PROP_BUTTONPAD , input -> propbit );
348
407
349
- ret = input_mt_init_slots (input , MAX_CONTACTS , INPUT_MT_POINTER );
408
+ ret = input_mt_init_slots (input , drvdata -> tp -> max_contacts ,
409
+ INPUT_MT_POINTER );
350
410
351
411
if (ret ) {
352
412
hid_err (hdev , "Asus input mt init slots failed: %d\n" , ret );
@@ -378,6 +438,26 @@ static int asus_input_mapping(struct hid_device *hdev,
378
438
return -1 ;
379
439
}
380
440
441
+ /*
442
+ * Ignore a bunch of bogus collections in the T100CHI descriptor.
443
+ * This avoids a bunch of non-functional hid_input devices getting
444
+ * created because of the T100CHI using HID_QUIRK_MULTI_INPUT.
445
+ */
446
+ if (drvdata -> quirks & QUIRK_T100CHI ) {
447
+ if (field -> application == (HID_UP_GENDESK | 0x0080 ) ||
448
+ usage -> hid == (HID_UP_GENDEVCTRLS | 0x0024 ) ||
449
+ usage -> hid == (HID_UP_GENDEVCTRLS | 0x0025 ) ||
450
+ usage -> hid == (HID_UP_GENDEVCTRLS | 0x0026 ))
451
+ return -1 ;
452
+ /*
453
+ * We use the hid_input for the mouse report for the touchpad,
454
+ * keep the left button, to avoid the core removing it.
455
+ */
456
+ if (field -> application == HID_GD_MOUSE &&
457
+ usage -> hid != (HID_UP_BUTTON | 1 ))
458
+ return -1 ;
459
+ }
460
+
381
461
/* ASUS-specific keyboard hotkeys */
382
462
if ((usage -> hid & HID_USAGE_PAGE ) == 0xff310000 ) {
383
463
set_bit (EV_REP , hi -> input -> evbit );
@@ -496,7 +576,7 @@ static int __maybe_unused asus_reset_resume(struct hid_device *hdev)
496
576
{
497
577
struct asus_drvdata * drvdata = hid_get_drvdata (hdev );
498
578
499
- if (drvdata -> quirks & QUIRK_IS_MULTITOUCH )
579
+ if (drvdata -> tp )
500
580
return asus_start_multitouch (hdev );
501
581
502
582
return 0 ;
@@ -517,6 +597,28 @@ static int asus_probe(struct hid_device *hdev, const struct hid_device_id *id)
517
597
518
598
drvdata -> quirks = id -> driver_data ;
519
599
600
+ if (drvdata -> quirks & QUIRK_IS_MULTITOUCH )
601
+ drvdata -> tp = & asus_i2c_tp ;
602
+
603
+ if (drvdata -> quirks & QUIRK_T100_KEYBOARD ) {
604
+ struct usb_interface * intf = to_usb_interface (hdev -> dev .parent );
605
+
606
+ if (intf -> altsetting -> desc .bInterfaceNumber == T100_TPAD_INTF ) {
607
+ drvdata -> quirks = QUIRK_SKIP_INPUT_MAPPING ;
608
+ drvdata -> tp = & asus_t100ta_tp ;
609
+ }
610
+ }
611
+
612
+ if (drvdata -> quirks & QUIRK_T100CHI ) {
613
+ /*
614
+ * All functionality is on a single HID interface and for
615
+ * userspace the touchpad must be a separate input_dev.
616
+ */
617
+ hdev -> quirks |= HID_QUIRK_MULTI_INPUT |
618
+ HID_QUIRK_NO_EMPTY_INPUT ;
619
+ drvdata -> tp = & asus_t100chi_tp ;
620
+ }
621
+
520
622
if (drvdata -> quirks & QUIRK_NO_INIT_REPORTS )
521
623
hdev -> quirks |= HID_QUIRK_NO_INIT_REPORTS ;
522
624
@@ -538,13 +640,13 @@ static int asus_probe(struct hid_device *hdev, const struct hid_device_id *id)
538
640
goto err_stop_hw ;
539
641
}
540
642
541
- if (drvdata -> quirks & QUIRK_IS_MULTITOUCH ) {
643
+ if (drvdata -> tp ) {
542
644
drvdata -> input -> name = "Asus TouchPad" ;
543
645
} else {
544
646
drvdata -> input -> name = "Asus Keyboard" ;
545
647
}
546
648
547
- if (drvdata -> quirks & QUIRK_IS_MULTITOUCH ) {
649
+ if (drvdata -> tp ) {
548
650
ret = asus_start_multitouch (hdev );
549
651
if (ret )
550
652
goto err_stop_hw ;
@@ -578,11 +680,34 @@ static __u8 *asus_report_fixup(struct hid_device *hdev, __u8 *rdesc,
578
680
hid_info (hdev , "Fixing up Asus notebook report descriptor\n" );
579
681
rdesc [55 ] = 0xdd ;
580
682
}
683
+ /* For the T100TA keyboard dock */
581
684
if (drvdata -> quirks & QUIRK_T100_KEYBOARD &&
582
685
* rsize == 76 && rdesc [73 ] == 0x81 && rdesc [74 ] == 0x01 ) {
583
686
hid_info (hdev , "Fixing up Asus T100 keyb report descriptor\n" );
584
687
rdesc [74 ] &= ~HID_MAIN_ITEM_CONSTANT ;
585
688
}
689
+ /* For the T100CHI keyboard dock */
690
+ if (drvdata -> quirks & QUIRK_T100CHI &&
691
+ * rsize == 403 && rdesc [388 ] == 0x09 && rdesc [389 ] == 0x76 ) {
692
+ /*
693
+ * Change Usage (76h) to Usage Minimum (00h), Usage Maximum
694
+ * (FFh) and clear the flags in the Input() byte.
695
+ * Note the descriptor has a bogus 0 byte at the end so we
696
+ * only need 1 extra byte.
697
+ */
698
+ * rsize = 404 ;
699
+ rdesc = kmemdup (rdesc , * rsize , GFP_KERNEL );
700
+ if (!rdesc )
701
+ return NULL ;
702
+
703
+ hid_info (hdev , "Fixing up T100CHI keyb report descriptor\n" );
704
+ memmove (rdesc + 392 , rdesc + 390 , 12 );
705
+ rdesc [388 ] = 0x19 ;
706
+ rdesc [389 ] = 0x00 ;
707
+ rdesc [390 ] = 0x29 ;
708
+ rdesc [391 ] = 0xff ;
709
+ rdesc [402 ] = 0x00 ;
710
+ }
586
711
587
712
return rdesc ;
588
713
}
@@ -602,6 +727,9 @@ static const struct hid_device_id asus_devices[] = {
602
727
{ HID_USB_DEVICE (USB_VENDOR_ID_CHICONY , USB_DEVICE_ID_ASUS_AK1D ) },
603
728
{ HID_USB_DEVICE (USB_VENDOR_ID_TURBOX , USB_DEVICE_ID_ASUS_MD_5110 ) },
604
729
{ HID_USB_DEVICE (USB_VENDOR_ID_JESS , USB_DEVICE_ID_ASUS_MD_5112 ) },
730
+ { HID_BLUETOOTH_DEVICE (USB_VENDOR_ID_ASUSTEK ,
731
+ USB_DEVICE_ID_ASUSTEK_T100CHI_KEYBOARD ), QUIRK_T100CHI },
732
+
605
733
{ }
606
734
};
607
735
MODULE_DEVICE_TABLE (hid , asus_devices );
0 commit comments