diff options
Diffstat (limited to 'drivers/hid/hid-3m-pct.c')
| -rw-r--r-- | drivers/hid/hid-3m-pct.c | 127 |
1 files changed, 72 insertions, 55 deletions
diff --git a/drivers/hid/hid-3m-pct.c b/drivers/hid/hid-3m-pct.c index 2a0d56b7a02b..02d8cd3b1b1b 100644 --- a/drivers/hid/hid-3m-pct.c +++ b/drivers/hid/hid-3m-pct.c | |||
| @@ -2,6 +2,8 @@ | |||
| 2 | * HID driver for 3M PCT multitouch panels | 2 | * HID driver for 3M PCT multitouch panels |
| 3 | * | 3 | * |
| 4 | * Copyright (c) 2009-2010 Stephane Chatty <chatty@enac.fr> | 4 | * Copyright (c) 2009-2010 Stephane Chatty <chatty@enac.fr> |
| 5 | * Copyright (c) 2010 Henrik Rydberg <rydberg@euromail.se> | ||
| 6 | * Copyright (c) 2010 Canonical, Ltd. | ||
| 5 | * | 7 | * |
| 6 | */ | 8 | */ |
| 7 | 9 | ||
| @@ -24,15 +26,26 @@ MODULE_LICENSE("GPL"); | |||
| 24 | 26 | ||
| 25 | #include "hid-ids.h" | 27 | #include "hid-ids.h" |
| 26 | 28 | ||
| 29 | #define MAX_SLOTS 60 | ||
| 30 | #define MAX_TRKID USHRT_MAX | ||
| 31 | #define MAX_EVENTS 360 | ||
| 32 | |||
| 33 | /* estimated signal-to-noise ratios */ | ||
| 34 | #define SN_MOVE 2048 | ||
| 35 | #define SN_WIDTH 128 | ||
| 36 | |||
| 27 | struct mmm_finger { | 37 | struct mmm_finger { |
| 28 | __s32 x, y, w, h; | 38 | __s32 x, y, w, h; |
| 29 | __u8 rank; | 39 | __u16 id; |
| 40 | bool prev_touch; | ||
| 30 | bool touch, valid; | 41 | bool touch, valid; |
| 31 | }; | 42 | }; |
| 32 | 43 | ||
| 33 | struct mmm_data { | 44 | struct mmm_data { |
| 34 | struct mmm_finger f[10]; | 45 | struct mmm_finger f[MAX_SLOTS]; |
| 35 | __u8 curid, num; | 46 | __u16 id; |
| 47 | __u8 curid; | ||
| 48 | __u8 nexp, nreal; | ||
| 36 | bool touch, valid; | 49 | bool touch, valid; |
| 37 | }; | 50 | }; |
| 38 | 51 | ||
| @@ -40,6 +53,10 @@ static int mmm_input_mapping(struct hid_device *hdev, struct hid_input *hi, | |||
| 40 | struct hid_field *field, struct hid_usage *usage, | 53 | struct hid_field *field, struct hid_usage *usage, |
| 41 | unsigned long **bit, int *max) | 54 | unsigned long **bit, int *max) |
| 42 | { | 55 | { |
| 56 | int f1 = field->logical_minimum; | ||
| 57 | int f2 = field->logical_maximum; | ||
| 58 | int df = f2 - f1; | ||
| 59 | |||
| 43 | switch (usage->hid & HID_USAGE_PAGE) { | 60 | switch (usage->hid & HID_USAGE_PAGE) { |
| 44 | 61 | ||
| 45 | case HID_UP_BUTTON: | 62 | case HID_UP_BUTTON: |
| @@ -50,18 +67,20 @@ static int mmm_input_mapping(struct hid_device *hdev, struct hid_input *hi, | |||
| 50 | case HID_GD_X: | 67 | case HID_GD_X: |
| 51 | hid_map_usage(hi, usage, bit, max, | 68 | hid_map_usage(hi, usage, bit, max, |
| 52 | EV_ABS, ABS_MT_POSITION_X); | 69 | EV_ABS, ABS_MT_POSITION_X); |
| 70 | input_set_abs_params(hi->input, ABS_MT_POSITION_X, | ||
| 71 | f1, f2, df / SN_MOVE, 0); | ||
| 53 | /* touchscreen emulation */ | 72 | /* touchscreen emulation */ |
| 54 | input_set_abs_params(hi->input, ABS_X, | 73 | input_set_abs_params(hi->input, ABS_X, |
| 55 | field->logical_minimum, | 74 | f1, f2, df / SN_MOVE, 0); |
| 56 | field->logical_maximum, 0, 0); | ||
| 57 | return 1; | 75 | return 1; |
| 58 | case HID_GD_Y: | 76 | case HID_GD_Y: |
| 59 | hid_map_usage(hi, usage, bit, max, | 77 | hid_map_usage(hi, usage, bit, max, |
| 60 | EV_ABS, ABS_MT_POSITION_Y); | 78 | EV_ABS, ABS_MT_POSITION_Y); |
| 79 | input_set_abs_params(hi->input, ABS_MT_POSITION_Y, | ||
| 80 | f1, f2, df / SN_MOVE, 0); | ||
| 61 | /* touchscreen emulation */ | 81 | /* touchscreen emulation */ |
| 62 | input_set_abs_params(hi->input, ABS_Y, | 82 | input_set_abs_params(hi->input, ABS_Y, |
| 63 | field->logical_minimum, | 83 | f1, f2, df / SN_MOVE, 0); |
| 64 | field->logical_maximum, 0, 0); | ||
| 65 | return 1; | 84 | return 1; |
| 66 | } | 85 | } |
| 67 | return 0; | 86 | return 0; |
| @@ -81,21 +100,31 @@ static int mmm_input_mapping(struct hid_device *hdev, struct hid_input *hi, | |||
| 81 | case HID_DG_TIPSWITCH: | 100 | case HID_DG_TIPSWITCH: |
| 82 | /* touchscreen emulation */ | 101 | /* touchscreen emulation */ |
| 83 | hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_TOUCH); | 102 | hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_TOUCH); |
| 103 | input_set_capability(hi->input, EV_KEY, BTN_TOUCH); | ||
| 84 | return 1; | 104 | return 1; |
| 85 | case HID_DG_WIDTH: | 105 | case HID_DG_WIDTH: |
| 86 | hid_map_usage(hi, usage, bit, max, | 106 | hid_map_usage(hi, usage, bit, max, |
| 87 | EV_ABS, ABS_MT_TOUCH_MAJOR); | 107 | EV_ABS, ABS_MT_TOUCH_MAJOR); |
| 108 | input_set_abs_params(hi->input, ABS_MT_TOUCH_MAJOR, | ||
| 109 | f1, f2, df / SN_WIDTH, 0); | ||
| 88 | return 1; | 110 | return 1; |
| 89 | case HID_DG_HEIGHT: | 111 | case HID_DG_HEIGHT: |
| 90 | hid_map_usage(hi, usage, bit, max, | 112 | hid_map_usage(hi, usage, bit, max, |
| 91 | EV_ABS, ABS_MT_TOUCH_MINOR); | 113 | EV_ABS, ABS_MT_TOUCH_MINOR); |
| 114 | input_set_abs_params(hi->input, ABS_MT_TOUCH_MINOR, | ||
| 115 | f1, f2, df / SN_WIDTH, 0); | ||
| 92 | input_set_abs_params(hi->input, ABS_MT_ORIENTATION, | 116 | input_set_abs_params(hi->input, ABS_MT_ORIENTATION, |
| 93 | 1, 1, 0, 0); | 117 | 0, 1, 0, 0); |
| 94 | return 1; | 118 | return 1; |
| 95 | case HID_DG_CONTACTID: | 119 | case HID_DG_CONTACTID: |
| 96 | field->logical_maximum = 59; | 120 | field->logical_maximum = MAX_TRKID; |
| 97 | hid_map_usage(hi, usage, bit, max, | 121 | hid_map_usage(hi, usage, bit, max, |
| 98 | EV_ABS, ABS_MT_TRACKING_ID); | 122 | EV_ABS, ABS_MT_TRACKING_ID); |
| 123 | input_set_abs_params(hi->input, ABS_MT_TRACKING_ID, | ||
| 124 | 0, MAX_TRKID, 0, 0); | ||
| 125 | if (!hi->input->mt) | ||
| 126 | input_mt_create_slots(hi->input, MAX_SLOTS); | ||
| 127 | input_set_events_per_packet(hi->input, MAX_EVENTS); | ||
| 99 | return 1; | 128 | return 1; |
| 100 | } | 129 | } |
| 101 | /* let hid-input decide for the others */ | 130 | /* let hid-input decide for the others */ |
| @@ -113,10 +142,10 @@ static int mmm_input_mapped(struct hid_device *hdev, struct hid_input *hi, | |||
| 113 | struct hid_field *field, struct hid_usage *usage, | 142 | struct hid_field *field, struct hid_usage *usage, |
| 114 | unsigned long **bit, int *max) | 143 | unsigned long **bit, int *max) |
| 115 | { | 144 | { |
| 145 | /* tell hid-input to skip setup of these event types */ | ||
| 116 | if (usage->type == EV_KEY || usage->type == EV_ABS) | 146 | if (usage->type == EV_KEY || usage->type == EV_ABS) |
| 117 | clear_bit(usage->code, *bit); | 147 | set_bit(usage->type, hi->input->evbit); |
| 118 | 148 | return -1; | |
| 119 | return 0; | ||
| 120 | } | 149 | } |
| 121 | 150 | ||
| 122 | /* | 151 | /* |
| @@ -126,70 +155,49 @@ static int mmm_input_mapped(struct hid_device *hdev, struct hid_input *hi, | |||
| 126 | static void mmm_filter_event(struct mmm_data *md, struct input_dev *input) | 155 | static void mmm_filter_event(struct mmm_data *md, struct input_dev *input) |
| 127 | { | 156 | { |
| 128 | struct mmm_finger *oldest = 0; | 157 | struct mmm_finger *oldest = 0; |
| 129 | bool pressed = false, released = false; | ||
| 130 | int i; | 158 | int i; |
| 131 | 159 | for (i = 0; i < MAX_SLOTS; ++i) { | |
| 132 | /* | ||
| 133 | * we need to iterate on all fingers to decide if we have a press | ||
| 134 | * or a release event in our touchscreen emulation. | ||
| 135 | */ | ||
| 136 | for (i = 0; i < 10; ++i) { | ||
| 137 | struct mmm_finger *f = &md->f[i]; | 160 | struct mmm_finger *f = &md->f[i]; |
| 138 | if (!f->valid) { | 161 | if (!f->valid) { |
| 139 | /* this finger is just placeholder data, ignore */ | 162 | /* this finger is just placeholder data, ignore */ |
| 140 | } else if (f->touch) { | 163 | continue; |
| 164 | } | ||
| 165 | input_mt_slot(input, i); | ||
| 166 | if (f->touch) { | ||
| 141 | /* this finger is on the screen */ | 167 | /* this finger is on the screen */ |
| 142 | int wide = (f->w > f->h); | 168 | int wide = (f->w > f->h); |
| 143 | input_event(input, EV_ABS, ABS_MT_TRACKING_ID, i); | 169 | /* divided by two to match visual scale of touch */ |
| 170 | int major = max(f->w, f->h) >> 1; | ||
| 171 | int minor = min(f->w, f->h) >> 1; | ||
| 172 | |||
| 173 | if (!f->prev_touch) | ||
| 174 | f->id = md->id++; | ||
| 175 | input_event(input, EV_ABS, ABS_MT_TRACKING_ID, f->id); | ||
| 144 | input_event(input, EV_ABS, ABS_MT_POSITION_X, f->x); | 176 | input_event(input, EV_ABS, ABS_MT_POSITION_X, f->x); |
| 145 | input_event(input, EV_ABS, ABS_MT_POSITION_Y, f->y); | 177 | input_event(input, EV_ABS, ABS_MT_POSITION_Y, f->y); |
| 146 | input_event(input, EV_ABS, ABS_MT_ORIENTATION, wide); | 178 | input_event(input, EV_ABS, ABS_MT_ORIENTATION, wide); |
| 147 | input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, | 179 | input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, major); |
| 148 | wide ? f->w : f->h); | 180 | input_event(input, EV_ABS, ABS_MT_TOUCH_MINOR, minor); |
| 149 | input_event(input, EV_ABS, ABS_MT_TOUCH_MINOR, | 181 | /* touchscreen emulation: pick the oldest contact */ |
| 150 | wide ? f->h : f->w); | 182 | if (!oldest || ((f->id - oldest->id) & (SHRT_MAX + 1))) |
| 151 | input_mt_sync(input); | ||
| 152 | /* | ||
| 153 | * touchscreen emulation: maintain the age rank | ||
| 154 | * of this finger, decide if we have a press | ||
| 155 | */ | ||
| 156 | if (f->rank == 0) { | ||
| 157 | f->rank = ++(md->num); | ||
| 158 | if (f->rank == 1) | ||
| 159 | pressed = true; | ||
| 160 | } | ||
| 161 | if (f->rank == 1) | ||
| 162 | oldest = f; | 183 | oldest = f; |
| 163 | } else { | 184 | } else { |
| 164 | /* this finger took off the screen */ | 185 | /* this finger took off the screen */ |
| 165 | /* touchscreen emulation: maintain age rank of others */ | 186 | input_event(input, EV_ABS, ABS_MT_TRACKING_ID, -1); |
| 166 | int j; | ||
| 167 | |||
| 168 | for (j = 0; j < 10; ++j) { | ||
| 169 | struct mmm_finger *g = &md->f[j]; | ||
| 170 | if (g->rank > f->rank) { | ||
| 171 | g->rank--; | ||
| 172 | if (g->rank == 1) | ||
| 173 | oldest = g; | ||
| 174 | } | ||
| 175 | } | ||
| 176 | f->rank = 0; | ||
| 177 | --(md->num); | ||
| 178 | if (md->num == 0) | ||
| 179 | released = true; | ||
| 180 | } | 187 | } |
| 188 | f->prev_touch = f->touch; | ||
| 181 | f->valid = 0; | 189 | f->valid = 0; |
| 182 | } | 190 | } |
| 183 | 191 | ||
| 184 | /* touchscreen emulation */ | 192 | /* touchscreen emulation */ |
| 185 | if (oldest) { | 193 | if (oldest) { |
| 186 | if (pressed) | 194 | input_event(input, EV_KEY, BTN_TOUCH, 1); |
| 187 | input_event(input, EV_KEY, BTN_TOUCH, 1); | ||
| 188 | input_event(input, EV_ABS, ABS_X, oldest->x); | 195 | input_event(input, EV_ABS, ABS_X, oldest->x); |
| 189 | input_event(input, EV_ABS, ABS_Y, oldest->y); | 196 | input_event(input, EV_ABS, ABS_Y, oldest->y); |
| 190 | } else if (released) { | 197 | } else { |
| 191 | input_event(input, EV_KEY, BTN_TOUCH, 0); | 198 | input_event(input, EV_KEY, BTN_TOUCH, 0); |
| 192 | } | 199 | } |
| 200 | input_sync(input); | ||
| 193 | } | 201 | } |
| 194 | 202 | ||
| 195 | /* | 203 | /* |
| @@ -223,10 +231,12 @@ static int mmm_event(struct hid_device *hid, struct hid_field *field, | |||
| 223 | md->f[md->curid].h = value; | 231 | md->f[md->curid].h = value; |
| 224 | break; | 232 | break; |
| 225 | case HID_DG_CONTACTID: | 233 | case HID_DG_CONTACTID: |
| 234 | value = clamp_val(value, 0, MAX_SLOTS - 1); | ||
| 226 | if (md->valid) { | 235 | if (md->valid) { |
| 227 | md->curid = value; | 236 | md->curid = value; |
| 228 | md->f[value].touch = md->touch; | 237 | md->f[value].touch = md->touch; |
| 229 | md->f[value].valid = 1; | 238 | md->f[value].valid = 1; |
| 239 | md->nreal++; | ||
| 230 | } | 240 | } |
| 231 | break; | 241 | break; |
| 232 | case HID_GD_X: | 242 | case HID_GD_X: |
| @@ -238,7 +248,12 @@ static int mmm_event(struct hid_device *hid, struct hid_field *field, | |||
| 238 | md->f[md->curid].y = value; | 248 | md->f[md->curid].y = value; |
| 239 | break; | 249 | break; |
| 240 | case HID_DG_CONTACTCOUNT: | 250 | case HID_DG_CONTACTCOUNT: |
| 241 | mmm_filter_event(md, input); | 251 | if (value) |
| 252 | md->nexp = value; | ||
| 253 | if (md->nreal >= md->nexp) { | ||
| 254 | mmm_filter_event(md, input); | ||
| 255 | md->nreal = 0; | ||
| 256 | } | ||
| 242 | break; | 257 | break; |
| 243 | } | 258 | } |
| 244 | } | 259 | } |
| @@ -255,6 +270,8 @@ static int mmm_probe(struct hid_device *hdev, const struct hid_device_id *id) | |||
| 255 | int ret; | 270 | int ret; |
| 256 | struct mmm_data *md; | 271 | struct mmm_data *md; |
| 257 | 272 | ||
| 273 | hdev->quirks |= HID_QUIRK_NO_INPUT_SYNC; | ||
| 274 | |||
| 258 | md = kzalloc(sizeof(struct mmm_data), GFP_KERNEL); | 275 | md = kzalloc(sizeof(struct mmm_data), GFP_KERNEL); |
| 259 | if (!md) { | 276 | if (!md) { |
| 260 | dev_err(&hdev->dev, "cannot allocate 3M data\n"); | 277 | dev_err(&hdev->dev, "cannot allocate 3M data\n"); |
