HID: support for Petalynx Maxter remote control
[pandora-kernel.git] / drivers / hid / hid-input.c
index a19b65e..1b8b333 100644 (file)
@@ -240,11 +240,94 @@ static inline void hidinput_pb_setup(struct input_dev *input)
 }
 #endif
 
+static inline int match_scancode(int code, int scancode)
+{
+       if (scancode == 0)
+               return 1;
+       return ((code & (HID_USAGE_PAGE | HID_USAGE)) == scancode);
+}
+
+static inline int match_keycode(int code, int keycode)
+{
+       if (keycode == 0)
+               return 1;
+       return (code == keycode);
+}
+
+static struct hid_usage *hidinput_find_key(struct hid_device *hid,
+               int scancode, int keycode)
+{
+       int i, j, k;
+       struct hid_report *report;
+       struct hid_usage *usage;
+
+       for (k = HID_INPUT_REPORT; k <= HID_OUTPUT_REPORT; k++) {
+               list_for_each_entry(report, &hid->report_enum[k].report_list, list) {
+                       for (i = 0; i < report->maxfield; i++) {
+                               for ( j = 0; j < report->field[i]->maxusage; j++) {
+                                       usage = report->field[i]->usage + j;
+                                       if (usage->type == EV_KEY &&
+                                               match_scancode(usage->hid, scancode) &&
+                                               match_keycode(usage->code, keycode))
+                                               return usage;
+                               }
+                       }
+               }
+       }
+       return NULL;
+}
+
+static int hidinput_getkeycode(struct input_dev *dev, int scancode,
+                               int *keycode)
+{
+       struct hid_device *hid = dev->private;
+       struct hid_usage *usage;
+       
+       usage = hidinput_find_key(hid, scancode, 0);
+       if (usage) {
+               *keycode = usage->code;
+               return 0;
+       }
+       return -EINVAL;
+}
+
+static int hidinput_setkeycode(struct input_dev *dev, int scancode,
+                               int keycode)
+{
+       struct hid_device *hid = dev->private;
+       struct hid_usage *usage;
+       int old_keycode;
+       
+       if (keycode < 0 || keycode > KEY_MAX)
+               return -EINVAL;
+       
+       usage = hidinput_find_key(hid, scancode, 0);
+       if (usage) {
+               old_keycode = usage->code;
+               usage->code = keycode;
+               
+               clear_bit(old_keycode, dev->keybit);
+               set_bit(usage->code, dev->keybit);
+#ifdef CONFIG_HID_DEBUG
+               printk (KERN_DEBUG "Assigned keycode %d to HID usage code %x\n", keycode, scancode);
+#endif
+               /* Set the keybit for the old keycode if the old keycode is used
+                * by another key */
+               if (hidinput_find_key (hid, 0, old_keycode))
+                       set_bit(old_keycode, dev->keybit);
+               
+               return 0;
+       }
+       
+       return -EINVAL;
+}
+
+
 static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_field *field,
                                     struct hid_usage *usage)
 {
        struct input_dev *input = hidinput->input;
-       struct hid_device *device = input->private;
+       struct hid_device *device = input_get_drvdata(input);
        int max = 0, code;
        unsigned long *bit = NULL;
 
@@ -483,6 +566,11 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
                                case 0x0e5: map_key_clear(KEY_BASSBOOST);       break;
                                case 0x0e9: map_key_clear(KEY_VOLUMEUP);        break;
                                case 0x0ea: map_key_clear(KEY_VOLUMEDOWN);      break;
+
+                               /* reserved in HUT 1.12. Reported on Petalynx remote */
+                               case 0x0f6: map_key_clear(KEY_NEXT);            break;
+                               case 0x0fa: map_key_clear(KEY_BACK);            break;
+
                                case 0x183: map_key_clear(KEY_CONFIG);          break;
                                case 0x184: map_key_clear(KEY_WORDPROCESSOR);   break;
                                case 0x185: map_key_clear(KEY_EDITOR);          break;
@@ -515,7 +603,9 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
                                case 0x21b: map_key_clear(KEY_COPY);            break;
                                case 0x21c: map_key_clear(KEY_CUT);             break;
                                case 0x21d: map_key_clear(KEY_PASTE);           break;
-                               case 0x221: map_key_clear(KEY_FIND);            break;
+                               case 0x21f: map_key_clear(KEY_FIND);            break;
+                               case 0x221: map_key_clear(KEY_SEARCH);          break;
+                               case 0x222: map_key_clear(KEY_GOTO);            break;
                                case 0x223: map_key_clear(KEY_HOMEPAGE);        break;
                                case 0x224: map_key_clear(KEY_BACK);            break;
                                case 0x225: map_key_clear(KEY_FORWARD);         break;
@@ -553,6 +643,7 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
                                case 0x1015: map_key_clear(KEY_RECORD);         break;
                                case 0x1016: map_key_clear(KEY_PLAYER);         break;
                                case 0x1017: map_key_clear(KEY_EJECTCD);        break;
+                               case 0x1018: map_key_clear(KEY_MEDIA);          break;
                                case 0x1019: map_key_clear(KEY_PROG1);          break;
                                case 0x101a: map_key_clear(KEY_PROG2);          break;
                                case 0x101b: map_key_clear(KEY_PROG3);          break;
@@ -560,9 +651,12 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
                                case 0x1020: map_key_clear(KEY_ZOOMOUT);        break;
                                case 0x1021: map_key_clear(KEY_ZOOMRESET);      break;
                                case 0x1023: map_key_clear(KEY_CLOSE);          break;
+                               case 0x1027: map_key_clear(KEY_MENU);           break;
                                /* this one is marked as 'Rotate' */
                                case 0x1028: map_key_clear(KEY_ANGLE);          break;
                                case 0x1029: map_key_clear(KEY_SHUFFLE);        break;
+                               case 0x102a: map_key_clear(KEY_BACK);           break;
+                               case 0x102b: map_key_clear(KEY_CYCLEWINDOWS);   break;
                                case 0x1041: map_key_clear(KEY_BATTERY);        break;
                                case 0x1042: map_key_clear(KEY_WORDPROCESSOR);  break;
                                case 0x1043: map_key_clear(KEY_SPREADSHEET);    break;
@@ -601,7 +695,28 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
                        break;
 
                case HID_UP_MSVENDOR:
-                       goto ignore;
+
+                       /* special case - Chicony Chicony KU-0418 tactical pad */
+                       if (device->vendor == 0x04f2 && device->product == 0x0418) {
+                               set_bit(EV_REP, input->evbit);
+                               switch(usage->hid & HID_USAGE) {
+                                       case 0xff01: map_key_clear(BTN_1);              break;
+                                       case 0xff02: map_key_clear(BTN_2);              break;
+                                       case 0xff03: map_key_clear(BTN_3);              break;
+                                       case 0xff04: map_key_clear(BTN_4);              break;
+                                       case 0xff05: map_key_clear(BTN_5);              break;
+                                       case 0xff06: map_key_clear(BTN_6);              break;
+                                       case 0xff07: map_key_clear(BTN_7);              break;
+                                       case 0xff08: map_key_clear(BTN_8);              break;
+                                       case 0xff09: map_key_clear(BTN_9);              break;
+                                       case 0xff0a: map_key_clear(BTN_A);              break;
+                                       case 0xff0b: map_key_clear(BTN_B);              break;
+                                       default:    goto ignore;
+                               }
+                       } else {
+                               goto ignore;
+                       }
+                       break;
 
                case HID_UP_CUSTOM: /* Reported on Logitech and Powerbook USB keyboards */
 
@@ -617,10 +732,10 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
                        }
                        break;
 
-               case HID_UP_LOGIVENDOR: /* Reported on Logitech Ultra X Media Remote */
-
+               case HID_UP_LOGIVENDOR:
                        set_bit(EV_REP, input->evbit);
                        switch(usage->hid & HID_USAGE) {
+                               /* Reported on Logitech Ultra X Media Remote */
                                case 0x004: map_key_clear(KEY_AGAIN);           break;
                                case 0x00d: map_key_clear(KEY_HOME);            break;
                                case 0x024: map_key_clear(KEY_SHUFFLE);         break;
@@ -638,6 +753,14 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
                                case 0x04d: map_key_clear(KEY_SUBTITLE);        break;
                                case 0x051: map_key_clear(KEY_RED);             break;
                                case 0x052: map_key_clear(KEY_CLOSE);           break;
+
+                               /* Reported on Petalynx Maxter remote */
+                               case 0x05a: map_key_clear(KEY_TEXT);            break;
+                               case 0x05b: map_key_clear(KEY_RED);             break;
+                               case 0x05c: map_key_clear(KEY_GREEN);           break;
+                               case 0x05d: map_key_clear(KEY_YELLOW);          break;
+                               case 0x05e: map_key_clear(KEY_BLUE);            break;
+
                                default:    goto ignore;
                        }
                        break;
@@ -855,13 +978,15 @@ EXPORT_SYMBOL_GPL(hidinput_find_field);
 
 static int hidinput_open(struct input_dev *dev)
 {
-       struct hid_device *hid = dev->private;
+       struct hid_device *hid = input_get_drvdata(dev);
+
        return hid->hid_open(hid);
 }
 
 static void hidinput_close(struct input_dev *dev)
 {
-       struct hid_device *hid = dev->private;
+       struct hid_device *hid = input_get_drvdata(dev);
+
        hid->hid_close(hid);
 }
 
@@ -887,7 +1012,7 @@ int hidinput_connect(struct hid_device *hid)
                        if (IS_INPUT_APPLICATION(hid->collection[i].usage))
                                break;
 
-       if (i == hid->maxcollection)
+       if (i == hid->maxcollection && (hid->quirks & HID_QUIRK_HIDINPUT) == 0)
                return -1;
 
        if (hid->quirks & HID_QUIRK_SKIP_OUTPUT_REPORTS)
@@ -909,10 +1034,12 @@ int hidinput_connect(struct hid_device *hid)
                                        return -1;
                                }
 
-                               input_dev->private = hid;
+                               input_set_drvdata(input_dev, hid);
                                input_dev->event = hid->hidinput_input_event;
                                input_dev->open = hidinput_open;
                                input_dev->close = hidinput_close;
+                               input_dev->setkeycode = hidinput_setkeycode;
+                               input_dev->getkeycode = hidinput_getkeycode;
 
                                input_dev->name = hid->name;
                                input_dev->phys = hid->phys;
@@ -921,7 +1048,7 @@ int hidinput_connect(struct hid_device *hid)
                                input_dev->id.vendor  = hid->vendor;
                                input_dev->id.product = hid->product;
                                input_dev->id.version = hid->version;
-                               input_dev->cdev.dev = hid->dev;
+                               input_dev->dev.parent = hid->dev;
                                hidinput->input = input_dev;
                                list_add_tail(&hidinput->list, &hid->inputs);
                        }