Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v2.6.37-rc2 333 lines 8.5 kB view raw
1/* 2 * HID driver for 3M PCT multitouch panels 3 * 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. 7 * 8 */ 9 10/* 11 * This program is free software; you can redistribute it and/or modify it 12 * under the terms of the GNU General Public License as published by the Free 13 * Software Foundation; either version 2 of the License, or (at your option) 14 * any later version. 15 */ 16 17#include <linux/device.h> 18#include <linux/hid.h> 19#include <linux/module.h> 20#include <linux/slab.h> 21#include <linux/usb.h> 22 23MODULE_AUTHOR("Stephane Chatty <chatty@enac.fr>"); 24MODULE_DESCRIPTION("3M PCT multitouch panels"); 25MODULE_LICENSE("GPL"); 26 27#include "hid-ids.h" 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 37struct mmm_finger { 38 __s32 x, y, w, h; 39 __u16 id; 40 bool prev_touch; 41 bool touch, valid; 42}; 43 44struct mmm_data { 45 struct mmm_finger f[MAX_SLOTS]; 46 __u16 id; 47 __u8 curid; 48 __u8 nexp, nreal; 49 bool touch, valid; 50}; 51 52static int mmm_input_mapping(struct hid_device *hdev, struct hid_input *hi, 53 struct hid_field *field, struct hid_usage *usage, 54 unsigned long **bit, int *max) 55{ 56 int f1 = field->logical_minimum; 57 int f2 = field->logical_maximum; 58 int df = f2 - f1; 59 60 switch (usage->hid & HID_USAGE_PAGE) { 61 62 case HID_UP_BUTTON: 63 return -1; 64 65 case HID_UP_GENDESK: 66 switch (usage->hid) { 67 case HID_GD_X: 68 hid_map_usage(hi, usage, bit, max, 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); 72 /* touchscreen emulation */ 73 input_set_abs_params(hi->input, ABS_X, 74 f1, f2, df / SN_MOVE, 0); 75 return 1; 76 case HID_GD_Y: 77 hid_map_usage(hi, usage, bit, max, 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); 81 /* touchscreen emulation */ 82 input_set_abs_params(hi->input, ABS_Y, 83 f1, f2, df / SN_MOVE, 0); 84 return 1; 85 } 86 return 0; 87 88 case HID_UP_DIGITIZER: 89 switch (usage->hid) { 90 /* we do not want to map these: no input-oriented meaning */ 91 case 0x14: 92 case 0x23: 93 case HID_DG_INPUTMODE: 94 case HID_DG_DEVICEINDEX: 95 case HID_DG_CONTACTCOUNT: 96 case HID_DG_CONTACTMAX: 97 case HID_DG_INRANGE: 98 case HID_DG_CONFIDENCE: 99 return -1; 100 case HID_DG_TIPSWITCH: 101 /* touchscreen emulation */ 102 hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_TOUCH); 103 input_set_capability(hi->input, EV_KEY, BTN_TOUCH); 104 return 1; 105 case HID_DG_WIDTH: 106 hid_map_usage(hi, usage, bit, max, 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); 110 return 1; 111 case HID_DG_HEIGHT: 112 hid_map_usage(hi, usage, bit, max, 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); 116 input_set_abs_params(hi->input, ABS_MT_ORIENTATION, 117 0, 1, 0, 0); 118 return 1; 119 case HID_DG_CONTACTID: 120 field->logical_maximum = MAX_TRKID; 121 hid_map_usage(hi, usage, bit, max, 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); 128 return 1; 129 } 130 /* let hid-input decide for the others */ 131 return 0; 132 133 case 0xff000000: 134 /* we do not want to map these: no input-oriented meaning */ 135 return -1; 136 } 137 138 return 0; 139} 140 141static int mmm_input_mapped(struct hid_device *hdev, struct hid_input *hi, 142 struct hid_field *field, struct hid_usage *usage, 143 unsigned long **bit, int *max) 144{ 145 /* tell hid-input to skip setup of these event types */ 146 if (usage->type == EV_KEY || usage->type == EV_ABS) 147 set_bit(usage->type, hi->input->evbit); 148 return -1; 149} 150 151/* 152 * this function is called when a whole packet has been received and processed, 153 * so that it can decide what to send to the input layer. 154 */ 155static void mmm_filter_event(struct mmm_data *md, struct input_dev *input) 156{ 157 struct mmm_finger *oldest = 0; 158 int i; 159 for (i = 0; i < MAX_SLOTS; ++i) { 160 struct mmm_finger *f = &md->f[i]; 161 if (!f->valid) { 162 /* this finger is just placeholder data, ignore */ 163 continue; 164 } 165 input_mt_slot(input, i); 166 if (f->touch) { 167 /* this finger is on the screen */ 168 int wide = (f->w > f->h); 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); 176 input_event(input, EV_ABS, ABS_MT_POSITION_X, f->x); 177 input_event(input, EV_ABS, ABS_MT_POSITION_Y, f->y); 178 input_event(input, EV_ABS, ABS_MT_ORIENTATION, wide); 179 input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, major); 180 input_event(input, EV_ABS, ABS_MT_TOUCH_MINOR, minor); 181 /* touchscreen emulation: pick the oldest contact */ 182 if (!oldest || ((f->id - oldest->id) & (SHRT_MAX + 1))) 183 oldest = f; 184 } else { 185 /* this finger took off the screen */ 186 input_event(input, EV_ABS, ABS_MT_TRACKING_ID, -1); 187 } 188 f->prev_touch = f->touch; 189 f->valid = 0; 190 } 191 192 /* touchscreen emulation */ 193 if (oldest) { 194 input_event(input, EV_KEY, BTN_TOUCH, 1); 195 input_event(input, EV_ABS, ABS_X, oldest->x); 196 input_event(input, EV_ABS, ABS_Y, oldest->y); 197 } else { 198 input_event(input, EV_KEY, BTN_TOUCH, 0); 199 } 200 input_sync(input); 201} 202 203/* 204 * this function is called upon all reports 205 * so that we can accumulate contact point information, 206 * and call input_mt_sync after each point. 207 */ 208static int mmm_event(struct hid_device *hid, struct hid_field *field, 209 struct hid_usage *usage, __s32 value) 210{ 211 struct mmm_data *md = hid_get_drvdata(hid); 212 /* 213 * strangely, this function can be called before 214 * field->hidinput is initialized! 215 */ 216 if (hid->claimed & HID_CLAIMED_INPUT) { 217 struct input_dev *input = field->hidinput->input; 218 switch (usage->hid) { 219 case HID_DG_TIPSWITCH: 220 md->touch = value; 221 break; 222 case HID_DG_CONFIDENCE: 223 md->valid = value; 224 break; 225 case HID_DG_WIDTH: 226 if (md->valid) 227 md->f[md->curid].w = value; 228 break; 229 case HID_DG_HEIGHT: 230 if (md->valid) 231 md->f[md->curid].h = value; 232 break; 233 case HID_DG_CONTACTID: 234 value = clamp_val(value, 0, MAX_SLOTS - 1); 235 if (md->valid) { 236 md->curid = value; 237 md->f[value].touch = md->touch; 238 md->f[value].valid = 1; 239 md->nreal++; 240 } 241 break; 242 case HID_GD_X: 243 if (md->valid) 244 md->f[md->curid].x = value; 245 break; 246 case HID_GD_Y: 247 if (md->valid) 248 md->f[md->curid].y = value; 249 break; 250 case HID_DG_CONTACTCOUNT: 251 if (value) 252 md->nexp = value; 253 if (md->nreal >= md->nexp) { 254 mmm_filter_event(md, input); 255 md->nreal = 0; 256 } 257 break; 258 } 259 } 260 261 /* we have handled the hidinput part, now remains hiddev */ 262 if (hid->claimed & HID_CLAIMED_HIDDEV && hid->hiddev_hid_event) 263 hid->hiddev_hid_event(hid, field, usage, value); 264 265 return 1; 266} 267 268static int mmm_probe(struct hid_device *hdev, const struct hid_device_id *id) 269{ 270 int ret; 271 struct mmm_data *md; 272 273 hdev->quirks |= HID_QUIRK_NO_INPUT_SYNC; 274 275 md = kzalloc(sizeof(struct mmm_data), GFP_KERNEL); 276 if (!md) { 277 dev_err(&hdev->dev, "cannot allocate 3M data\n"); 278 return -ENOMEM; 279 } 280 hid_set_drvdata(hdev, md); 281 282 ret = hid_parse(hdev); 283 if (!ret) 284 ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); 285 286 if (ret) 287 kfree(md); 288 return ret; 289} 290 291static void mmm_remove(struct hid_device *hdev) 292{ 293 hid_hw_stop(hdev); 294 kfree(hid_get_drvdata(hdev)); 295 hid_set_drvdata(hdev, NULL); 296} 297 298static const struct hid_device_id mmm_devices[] = { 299 { HID_USB_DEVICE(USB_VENDOR_ID_3M, USB_DEVICE_ID_3M1968) }, 300 { HID_USB_DEVICE(USB_VENDOR_ID_3M, USB_DEVICE_ID_3M2256) }, 301 { } 302}; 303MODULE_DEVICE_TABLE(hid, mmm_devices); 304 305static const struct hid_usage_id mmm_grabbed_usages[] = { 306 { HID_ANY_ID, HID_ANY_ID, HID_ANY_ID }, 307 { HID_ANY_ID - 1, HID_ANY_ID - 1, HID_ANY_ID - 1} 308}; 309 310static struct hid_driver mmm_driver = { 311 .name = "3m-pct", 312 .id_table = mmm_devices, 313 .probe = mmm_probe, 314 .remove = mmm_remove, 315 .input_mapping = mmm_input_mapping, 316 .input_mapped = mmm_input_mapped, 317 .usage_table = mmm_grabbed_usages, 318 .event = mmm_event, 319}; 320 321static int __init mmm_init(void) 322{ 323 return hid_register_driver(&mmm_driver); 324} 325 326static void __exit mmm_exit(void) 327{ 328 hid_unregister_driver(&mmm_driver); 329} 330 331module_init(mmm_init); 332module_exit(mmm_exit); 333