linux/drivers/hid/hid-lg.c
<<
>>
Prefs
   1/*
   2 *  HID driver for some logitech "special" devices
   3 *
   4 *  Copyright (c) 1999 Andreas Gal
   5 *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
   6 *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
   7 *  Copyright (c) 2006-2007 Jiri Kosina
   8 *  Copyright (c) 2007 Paul Walmsley
   9 *  Copyright (c) 2008 Jiri Slaby
  10 */
  11
  12/*
  13 * This program is free software; you can redistribute it and/or modify it
  14 * under the terms of the GNU General Public License as published by the Free
  15 * Software Foundation; either version 2 of the License, or (at your option)
  16 * any later version.
  17 */
  18
  19#include <linux/device.h>
  20#include <linux/hid.h>
  21#include <linux/module.h>
  22
  23#include "hid-ids.h"
  24#include "hid-lg.h"
  25
  26#define LG_RDESC                0x001
  27#define LG_BAD_RELATIVE_KEYS    0x002
  28#define LG_DUPLICATE_USAGES     0x004
  29#define LG_EXPANDED_KEYMAP      0x010
  30#define LG_IGNORE_DOUBLED_WHEEL 0x020
  31#define LG_WIRELESS             0x040
  32#define LG_INVERT_HWHEEL        0x080
  33#define LG_NOGET                0x100
  34#define LG_FF                   0x200
  35#define LG_FF2                  0x400
  36#define LG_RDESC_REL_ABS        0x800
  37#define LG_FF3                  0x1000
  38
  39/*
  40 * Certain Logitech keyboards send in report #3 keys which are far
  41 * above the logical maximum described in descriptor. This extends
  42 * the original value of 0x28c of logical maximum to 0x104d
  43 */
  44static void lg_report_fixup(struct hid_device *hdev, __u8 *rdesc,
  45                unsigned int rsize)
  46{
  47        unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
  48
  49        if ((quirks & LG_RDESC) && rsize >= 90 && rdesc[83] == 0x26 &&
  50                        rdesc[84] == 0x8c && rdesc[85] == 0x02) {
  51                dev_info(&hdev->dev, "fixing up Logitech keyboard report "
  52                                "descriptor\n");
  53                rdesc[84] = rdesc[89] = 0x4d;
  54                rdesc[85] = rdesc[90] = 0x10;
  55        }
  56        if ((quirks & LG_RDESC_REL_ABS) && rsize >= 50 &&
  57                        rdesc[32] == 0x81 && rdesc[33] == 0x06 &&
  58                        rdesc[49] == 0x81 && rdesc[50] == 0x06) {
  59                dev_info(&hdev->dev, "fixing up rel/abs in Logitech "
  60                                "report descriptor\n");
  61                rdesc[33] = rdesc[50] = 0x02;
  62        }
  63}
  64
  65#define lg_map_key_clear(c)     hid_map_usage_clear(hi, usage, bit, max, \
  66                EV_KEY, (c))
  67
  68static int lg_ultrax_remote_mapping(struct hid_input *hi,
  69                struct hid_usage *usage, unsigned long **bit, int *max)
  70{
  71        if ((usage->hid & HID_USAGE_PAGE) != HID_UP_LOGIVENDOR)
  72                return 0;
  73
  74        set_bit(EV_REP, hi->input->evbit);
  75        switch (usage->hid & HID_USAGE) {
  76        /* Reported on Logitech Ultra X Media Remote */
  77        case 0x004: lg_map_key_clear(KEY_AGAIN);        break;
  78        case 0x00d: lg_map_key_clear(KEY_HOME);         break;
  79        case 0x024: lg_map_key_clear(KEY_SHUFFLE);      break;
  80        case 0x025: lg_map_key_clear(KEY_TV);           break;
  81        case 0x026: lg_map_key_clear(KEY_MENU);         break;
  82        case 0x031: lg_map_key_clear(KEY_AUDIO);        break;
  83        case 0x032: lg_map_key_clear(KEY_TEXT);         break;
  84        case 0x033: lg_map_key_clear(KEY_LAST);         break;
  85        case 0x047: lg_map_key_clear(KEY_MP3);          break;
  86        case 0x048: lg_map_key_clear(KEY_DVD);          break;
  87        case 0x049: lg_map_key_clear(KEY_MEDIA);        break;
  88        case 0x04a: lg_map_key_clear(KEY_VIDEO);        break;
  89        case 0x04b: lg_map_key_clear(KEY_ANGLE);        break;
  90        case 0x04c: lg_map_key_clear(KEY_LANGUAGE);     break;
  91        case 0x04d: lg_map_key_clear(KEY_SUBTITLE);     break;
  92        case 0x051: lg_map_key_clear(KEY_RED);          break;
  93        case 0x052: lg_map_key_clear(KEY_CLOSE);        break;
  94
  95        default:
  96                return 0;
  97        }
  98        return 1;
  99}
 100
 101static int lg_dinovo_mapping(struct hid_input *hi, struct hid_usage *usage,
 102                unsigned long **bit, int *max)
 103{
 104        if ((usage->hid & HID_USAGE_PAGE) != HID_UP_LOGIVENDOR)
 105                return 0;
 106
 107        switch (usage->hid & HID_USAGE) {
 108
 109        case 0x00d: lg_map_key_clear(KEY_MEDIA);        break;
 110        default:
 111                return 0;
 112
 113        }
 114        return 1;
 115}
 116
 117static int lg_wireless_mapping(struct hid_input *hi, struct hid_usage *usage,
 118                unsigned long **bit, int *max)
 119{
 120        if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER)
 121                return 0;
 122
 123        switch (usage->hid & HID_USAGE) {
 124        case 0x1001: lg_map_key_clear(KEY_MESSENGER);           break;
 125        case 0x1003: lg_map_key_clear(KEY_SOUND);               break;
 126        case 0x1004: lg_map_key_clear(KEY_VIDEO);               break;
 127        case 0x1005: lg_map_key_clear(KEY_AUDIO);               break;
 128        case 0x100a: lg_map_key_clear(KEY_DOCUMENTS);           break;
 129        /* The following two entries are Playlist 1 and 2 on the MX3200 */
 130        case 0x100f: lg_map_key_clear(KEY_FN_1);                break;
 131        case 0x1010: lg_map_key_clear(KEY_FN_2);                break;
 132        case 0x1011: lg_map_key_clear(KEY_PREVIOUSSONG);        break;
 133        case 0x1012: lg_map_key_clear(KEY_NEXTSONG);            break;
 134        case 0x1013: lg_map_key_clear(KEY_CAMERA);              break;
 135        case 0x1014: lg_map_key_clear(KEY_MESSENGER);           break;
 136        case 0x1015: lg_map_key_clear(KEY_RECORD);              break;
 137        case 0x1016: lg_map_key_clear(KEY_PLAYER);              break;
 138        case 0x1017: lg_map_key_clear(KEY_EJECTCD);             break;
 139        case 0x1018: lg_map_key_clear(KEY_MEDIA);               break;
 140        case 0x1019: lg_map_key_clear(KEY_PROG1);               break;
 141        case 0x101a: lg_map_key_clear(KEY_PROG2);               break;
 142        case 0x101b: lg_map_key_clear(KEY_PROG3);               break;
 143        case 0x101c: lg_map_key_clear(KEY_CYCLEWINDOWS);        break;
 144        case 0x101f: lg_map_key_clear(KEY_ZOOMIN);              break;
 145        case 0x1020: lg_map_key_clear(KEY_ZOOMOUT);             break;
 146        case 0x1021: lg_map_key_clear(KEY_ZOOMRESET);           break;
 147        case 0x1023: lg_map_key_clear(KEY_CLOSE);               break;
 148        case 0x1027: lg_map_key_clear(KEY_MENU);                break;
 149        /* this one is marked as 'Rotate' */
 150        case 0x1028: lg_map_key_clear(KEY_ANGLE);               break;
 151        case 0x1029: lg_map_key_clear(KEY_SHUFFLE);             break;
 152        case 0x102a: lg_map_key_clear(KEY_BACK);                break;
 153        case 0x102b: lg_map_key_clear(KEY_CYCLEWINDOWS);        break;
 154        case 0x102d: lg_map_key_clear(KEY_WWW);                 break;
 155        /* The following two are 'Start/answer call' and 'End/reject call'
 156           on the MX3200 */
 157        case 0x1031: lg_map_key_clear(KEY_OK);                  break;
 158        case 0x1032: lg_map_key_clear(KEY_CANCEL);              break;
 159        case 0x1041: lg_map_key_clear(KEY_BATTERY);             break;
 160        case 0x1042: lg_map_key_clear(KEY_WORDPROCESSOR);       break;
 161        case 0x1043: lg_map_key_clear(KEY_SPREADSHEET);         break;
 162        case 0x1044: lg_map_key_clear(KEY_PRESENTATION);        break;
 163        case 0x1045: lg_map_key_clear(KEY_UNDO);                break;
 164        case 0x1046: lg_map_key_clear(KEY_REDO);                break;
 165        case 0x1047: lg_map_key_clear(KEY_PRINT);               break;
 166        case 0x1048: lg_map_key_clear(KEY_SAVE);                break;
 167        case 0x1049: lg_map_key_clear(KEY_PROG1);               break;
 168        case 0x104a: lg_map_key_clear(KEY_PROG2);               break;
 169        case 0x104b: lg_map_key_clear(KEY_PROG3);               break;
 170        case 0x104c: lg_map_key_clear(KEY_PROG4);               break;
 171
 172        default:
 173                return 0;
 174        }
 175        return 1;
 176}
 177
 178static int lg_input_mapping(struct hid_device *hdev, struct hid_input *hi,
 179                struct hid_field *field, struct hid_usage *usage,
 180                unsigned long **bit, int *max)
 181{
 182        /* extended mapping for certain Logitech hardware (Logitech cordless
 183           desktop LX500) */
 184        static const u8 e_keymap[] = {
 185                  0,216,  0,213,175,156,  0,  0,  0,  0,
 186                144,  0,  0,  0,  0,  0,  0,  0,  0,212,
 187                174,167,152,161,112,  0,  0,  0,154,  0,
 188                  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
 189                  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
 190                  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
 191                  0,  0,  0,  0,  0,183,184,185,186,187,
 192                188,189,190,191,192,193,194,  0,  0,  0
 193        };
 194        unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
 195        unsigned int hid = usage->hid;
 196
 197        if (hdev->product == USB_DEVICE_ID_LOGITECH_RECEIVER &&
 198                        lg_ultrax_remote_mapping(hi, usage, bit, max))
 199                return 1;
 200
 201        if (hdev->product == USB_DEVICE_ID_DINOVO_MINI &&
 202                        lg_dinovo_mapping(hi, usage, bit, max))
 203                return 1;
 204
 205        if ((quirks & LG_WIRELESS) && lg_wireless_mapping(hi, usage, bit, max))
 206                return 1;
 207
 208        if ((hid & HID_USAGE_PAGE) != HID_UP_BUTTON)
 209                return 0;
 210
 211        hid &= HID_USAGE;
 212
 213        /* Special handling for Logitech Cordless Desktop */
 214        if (field->application == HID_GD_MOUSE) {
 215                if ((quirks & LG_IGNORE_DOUBLED_WHEEL) &&
 216                                (hid == 7 || hid == 8))
 217                        return -1;
 218        } else {
 219                if ((quirks & LG_EXPANDED_KEYMAP) &&
 220                                hid < ARRAY_SIZE(e_keymap) &&
 221                                e_keymap[hid] != 0) {
 222                        hid_map_usage(hi, usage, bit, max, EV_KEY,
 223                                        e_keymap[hid]);
 224                        return 1;
 225                }
 226        }
 227
 228        return 0;
 229}
 230
 231static int lg_input_mapped(struct hid_device *hdev, struct hid_input *hi,
 232                struct hid_field *field, struct hid_usage *usage,
 233                unsigned long **bit, int *max)
 234{
 235        unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
 236
 237        if ((quirks & LG_BAD_RELATIVE_KEYS) && usage->type == EV_KEY &&
 238                        (field->flags & HID_MAIN_ITEM_RELATIVE))
 239                field->flags &= ~HID_MAIN_ITEM_RELATIVE;
 240
 241        if ((quirks & LG_DUPLICATE_USAGES) && (usage->type == EV_KEY ||
 242                         usage->type == EV_REL || usage->type == EV_ABS))
 243                clear_bit(usage->code, *bit);
 244
 245        return 0;
 246}
 247
 248static int lg_event(struct hid_device *hdev, struct hid_field *field,
 249                struct hid_usage *usage, __s32 value)
 250{
 251        unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
 252
 253        if ((quirks & LG_INVERT_HWHEEL) && usage->code == REL_HWHEEL) {
 254                input_event(field->hidinput->input, usage->type, usage->code,
 255                                -value);
 256                return 1;
 257        }
 258
 259        return 0;
 260}
 261
 262static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id)
 263{
 264        unsigned long quirks = id->driver_data;
 265        unsigned int connect_mask = HID_CONNECT_DEFAULT;
 266        int ret;
 267
 268        hid_set_drvdata(hdev, (void *)quirks);
 269
 270        if (quirks & LG_NOGET)
 271                hdev->quirks |= HID_QUIRK_NOGET;
 272
 273        ret = hid_parse(hdev);
 274        if (ret) {
 275                dev_err(&hdev->dev, "parse failed\n");
 276                goto err_free;
 277        }
 278
 279        if (quirks & (LG_FF | LG_FF2 | LG_FF3))
 280                connect_mask &= ~HID_CONNECT_FF;
 281
 282        ret = hid_hw_start(hdev, connect_mask);
 283        if (ret) {
 284                dev_err(&hdev->dev, "hw start failed\n");
 285                goto err_free;
 286        }
 287
 288        if (quirks & LG_FF)
 289                lgff_init(hdev);
 290        if (quirks & LG_FF2)
 291                lg2ff_init(hdev);
 292        if (quirks & LG_FF3)
 293                lg3ff_init(hdev);
 294
 295        return 0;
 296err_free:
 297        return ret;
 298}
 299
 300static const struct hid_device_id lg_devices[] = {
 301        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER),
 302                .driver_data = LG_RDESC | LG_WIRELESS },
 303        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER),
 304                .driver_data = LG_RDESC | LG_WIRELESS },
 305        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER_2),
 306                .driver_data = LG_RDESC | LG_WIRELESS },
 307
 308        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RECEIVER),
 309                .driver_data = LG_BAD_RELATIVE_KEYS },
 310
 311        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_DESKTOP),
 312                .driver_data = LG_DUPLICATE_USAGES },
 313        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_EDGE),
 314                .driver_data = LG_DUPLICATE_USAGES },
 315        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_MINI),
 316                .driver_data = LG_DUPLICATE_USAGES },
 317
 318        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_ELITE_KBD),
 319                .driver_data = LG_IGNORE_DOUBLED_WHEEL | LG_EXPANDED_KEYMAP },
 320        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500),
 321                .driver_data = LG_IGNORE_DOUBLED_WHEEL | LG_EXPANDED_KEYMAP },
 322
 323        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_EXTREME_3D),
 324                .driver_data = LG_NOGET },
 325        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WHEEL),
 326                .driver_data = LG_NOGET | LG_FF },
 327
 328        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD),
 329                .driver_data = LG_FF },
 330        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2),
 331                .driver_data = LG_FF },
 332        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_F3D),
 333                .driver_data = LG_FF },
 334        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FORCE3D_PRO),
 335                .driver_data = LG_FF },
 336        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL),
 337                .driver_data = LG_FF },
 338        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2),
 339                .driver_data = LG_FF },
 340        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G25_WHEEL),
 341                .driver_data = LG_FF },
 342        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG ),
 343                .driver_data = LG_FF },
 344        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2),
 345                .driver_data = LG_FF2 },
 346        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FLIGHT_SYSTEM_G940),
 347                .driver_data = LG_FF3 },
 348        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACENAVIGATOR),
 349                .driver_data = LG_RDESC_REL_ABS },
 350        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACETRAVELLER),
 351                .driver_data = LG_RDESC_REL_ABS },
 352        { }
 353};
 354
 355MODULE_DEVICE_TABLE(hid, lg_devices);
 356
 357static struct hid_driver lg_driver = {
 358        .name = "logitech",
 359        .id_table = lg_devices,
 360        .report_fixup = lg_report_fixup,
 361        .input_mapping = lg_input_mapping,
 362        .input_mapped = lg_input_mapped,
 363        .event = lg_event,
 364        .probe = lg_probe,
 365};
 366
 367static int __init lg_init(void)
 368{
 369        return hid_register_driver(&lg_driver);
 370}
 371
 372static void __exit lg_exit(void)
 373{
 374        hid_unregister_driver(&lg_driver);
 375}
 376
 377module_init(lg_init);
 378module_exit(lg_exit);
 379MODULE_LICENSE("GPL");
 380
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.