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.38-rc1 305 lines 7.6 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#include <linux/input/mt.h> 23 24MODULE_AUTHOR("Stephane Chatty <chatty@enac.fr>"); 25MODULE_DESCRIPTION("3M PCT multitouch panels"); 26MODULE_LICENSE("GPL"); 27 28#include "hid-ids.h" 29 30#define MAX_SLOTS 60 31 32/* estimated signal-to-noise ratios */ 33#define SN_MOVE 2048 34#define SN_WIDTH 128 35 36struct mmm_finger { 37 __s32 x, y, w, h; 38 bool touch, valid; 39}; 40 41struct mmm_data { 42 struct mmm_finger f[MAX_SLOTS]; 43 __u8 curid; 44 __u8 nexp, nreal; 45 bool touch, valid; 46}; 47 48static int mmm_input_mapping(struct hid_device *hdev, struct hid_input *hi, 49 struct hid_field *field, struct hid_usage *usage, 50 unsigned long **bit, int *max) 51{ 52 int f1 = field->logical_minimum; 53 int f2 = field->logical_maximum; 54 int df = f2 - f1; 55 56 switch (usage->hid & HID_USAGE_PAGE) { 57 58 case HID_UP_BUTTON: 59 return -1; 60 61 case HID_UP_GENDESK: 62 switch (usage->hid) { 63 case HID_GD_X: 64 hid_map_usage(hi, usage, bit, max, 65 EV_ABS, ABS_MT_POSITION_X); 66 input_set_abs_params(hi->input, ABS_MT_POSITION_X, 67 f1, f2, df / SN_MOVE, 0); 68 /* touchscreen emulation */ 69 input_set_abs_params(hi->input, ABS_X, 70 f1, f2, df / SN_MOVE, 0); 71 return 1; 72 case HID_GD_Y: 73 hid_map_usage(hi, usage, bit, max, 74 EV_ABS, ABS_MT_POSITION_Y); 75 input_set_abs_params(hi->input, ABS_MT_POSITION_Y, 76 f1, f2, df / SN_MOVE, 0); 77 /* touchscreen emulation */ 78 input_set_abs_params(hi->input, ABS_Y, 79 f1, f2, df / SN_MOVE, 0); 80 return 1; 81 } 82 return 0; 83 84 case HID_UP_DIGITIZER: 85 switch (usage->hid) { 86 /* we do not want to map these: no input-oriented meaning */ 87 case 0x14: 88 case 0x23: 89 case HID_DG_INPUTMODE: 90 case HID_DG_DEVICEINDEX: 91 case HID_DG_CONTACTCOUNT: 92 case HID_DG_CONTACTMAX: 93 case HID_DG_INRANGE: 94 case HID_DG_CONFIDENCE: 95 return -1; 96 case HID_DG_TIPSWITCH: 97 /* touchscreen emulation */ 98 hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_TOUCH); 99 input_set_capability(hi->input, EV_KEY, BTN_TOUCH); 100 return 1; 101 case HID_DG_WIDTH: 102 hid_map_usage(hi, usage, bit, max, 103 EV_ABS, ABS_MT_TOUCH_MAJOR); 104 input_set_abs_params(hi->input, ABS_MT_TOUCH_MAJOR, 105 f1, f2, df / SN_WIDTH, 0); 106 return 1; 107 case HID_DG_HEIGHT: 108 hid_map_usage(hi, usage, bit, max, 109 EV_ABS, ABS_MT_TOUCH_MINOR); 110 input_set_abs_params(hi->input, ABS_MT_TOUCH_MINOR, 111 f1, f2, df / SN_WIDTH, 0); 112 input_set_abs_params(hi->input, ABS_MT_ORIENTATION, 113 0, 1, 0, 0); 114 return 1; 115 case HID_DG_CONTACTID: 116 input_mt_init_slots(hi->input, MAX_SLOTS); 117 return 1; 118 } 119 /* let hid-input decide for the others */ 120 return 0; 121 122 case 0xff000000: 123 /* we do not want to map these: no input-oriented meaning */ 124 return -1; 125 } 126 127 return 0; 128} 129 130static int mmm_input_mapped(struct hid_device *hdev, struct hid_input *hi, 131 struct hid_field *field, struct hid_usage *usage, 132 unsigned long **bit, int *max) 133{ 134 /* tell hid-input to skip setup of these event types */ 135 if (usage->type == EV_KEY || usage->type == EV_ABS) 136 set_bit(usage->type, hi->input->evbit); 137 return -1; 138} 139 140/* 141 * this function is called when a whole packet has been received and processed, 142 * so that it can decide what to send to the input layer. 143 */ 144static void mmm_filter_event(struct mmm_data *md, struct input_dev *input) 145{ 146 int i; 147 for (i = 0; i < MAX_SLOTS; ++i) { 148 struct mmm_finger *f = &md->f[i]; 149 if (!f->valid) { 150 /* this finger is just placeholder data, ignore */ 151 continue; 152 } 153 input_mt_slot(input, i); 154 input_mt_report_slot_state(input, MT_TOOL_FINGER, f->touch); 155 if (f->touch) { 156 /* this finger is on the screen */ 157 int wide = (f->w > f->h); 158 /* divided by two to match visual scale of touch */ 159 int major = max(f->w, f->h) >> 1; 160 int minor = min(f->w, f->h) >> 1; 161 162 input_event(input, EV_ABS, ABS_MT_POSITION_X, f->x); 163 input_event(input, EV_ABS, ABS_MT_POSITION_Y, f->y); 164 input_event(input, EV_ABS, ABS_MT_ORIENTATION, wide); 165 input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, major); 166 input_event(input, EV_ABS, ABS_MT_TOUCH_MINOR, minor); 167 } 168 f->valid = 0; 169 } 170 171 input_mt_report_pointer_emulation(input, true); 172 input_sync(input); 173} 174 175/* 176 * this function is called upon all reports 177 * so that we can accumulate contact point information, 178 * and call input_mt_sync after each point. 179 */ 180static int mmm_event(struct hid_device *hid, struct hid_field *field, 181 struct hid_usage *usage, __s32 value) 182{ 183 struct mmm_data *md = hid_get_drvdata(hid); 184 /* 185 * strangely, this function can be called before 186 * field->hidinput is initialized! 187 */ 188 if (hid->claimed & HID_CLAIMED_INPUT) { 189 struct input_dev *input = field->hidinput->input; 190 switch (usage->hid) { 191 case HID_DG_TIPSWITCH: 192 md->touch = value; 193 break; 194 case HID_DG_CONFIDENCE: 195 md->valid = value; 196 break; 197 case HID_DG_WIDTH: 198 if (md->valid) 199 md->f[md->curid].w = value; 200 break; 201 case HID_DG_HEIGHT: 202 if (md->valid) 203 md->f[md->curid].h = value; 204 break; 205 case HID_DG_CONTACTID: 206 value = clamp_val(value, 0, MAX_SLOTS - 1); 207 if (md->valid) { 208 md->curid = value; 209 md->f[value].touch = md->touch; 210 md->f[value].valid = 1; 211 md->nreal++; 212 } 213 break; 214 case HID_GD_X: 215 if (md->valid) 216 md->f[md->curid].x = value; 217 break; 218 case HID_GD_Y: 219 if (md->valid) 220 md->f[md->curid].y = value; 221 break; 222 case HID_DG_CONTACTCOUNT: 223 if (value) 224 md->nexp = value; 225 if (md->nreal >= md->nexp) { 226 mmm_filter_event(md, input); 227 md->nreal = 0; 228 } 229 break; 230 } 231 } 232 233 /* we have handled the hidinput part, now remains hiddev */ 234 if (hid->claimed & HID_CLAIMED_HIDDEV && hid->hiddev_hid_event) 235 hid->hiddev_hid_event(hid, field, usage, value); 236 237 return 1; 238} 239 240static int mmm_probe(struct hid_device *hdev, const struct hid_device_id *id) 241{ 242 int ret; 243 struct mmm_data *md; 244 245 hdev->quirks |= HID_QUIRK_NO_INPUT_SYNC; 246 247 md = kzalloc(sizeof(struct mmm_data), GFP_KERNEL); 248 if (!md) { 249 hid_err(hdev, "cannot allocate 3M data\n"); 250 return -ENOMEM; 251 } 252 hid_set_drvdata(hdev, md); 253 254 ret = hid_parse(hdev); 255 if (!ret) 256 ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); 257 258 if (ret) 259 kfree(md); 260 return ret; 261} 262 263static void mmm_remove(struct hid_device *hdev) 264{ 265 hid_hw_stop(hdev); 266 kfree(hid_get_drvdata(hdev)); 267 hid_set_drvdata(hdev, NULL); 268} 269 270static const struct hid_device_id mmm_devices[] = { 271 { HID_USB_DEVICE(USB_VENDOR_ID_3M, USB_DEVICE_ID_3M1968) }, 272 { HID_USB_DEVICE(USB_VENDOR_ID_3M, USB_DEVICE_ID_3M2256) }, 273 { } 274}; 275MODULE_DEVICE_TABLE(hid, mmm_devices); 276 277static const struct hid_usage_id mmm_grabbed_usages[] = { 278 { HID_ANY_ID, HID_ANY_ID, HID_ANY_ID }, 279 { HID_ANY_ID - 1, HID_ANY_ID - 1, HID_ANY_ID - 1} 280}; 281 282static struct hid_driver mmm_driver = { 283 .name = "3m-pct", 284 .id_table = mmm_devices, 285 .probe = mmm_probe, 286 .remove = mmm_remove, 287 .input_mapping = mmm_input_mapping, 288 .input_mapped = mmm_input_mapped, 289 .usage_table = mmm_grabbed_usages, 290 .event = mmm_event, 291}; 292 293static int __init mmm_init(void) 294{ 295 return hid_register_driver(&mmm_driver); 296} 297 298static void __exit mmm_exit(void) 299{ 300 hid_unregister_driver(&mmm_driver); 301} 302 303module_init(mmm_init); 304module_exit(mmm_exit); 305