HID: wacom: generic: Support and use 'Custom HID' mode and usages
[linux-block.git] / drivers / hid / wacom_wac.c
index 1cb79925730d931a0ee00f3ff0f470464731d3b3..6c2f0e4baf7dc3a8b66c86b636b6d291a8758fae 100644 (file)
@@ -1435,11 +1435,37 @@ static int wacom_tpc_irq(struct wacom_wac *wacom, size_t len)
        return 0;
 }
 
+static int wacom_equivalent_usage(int usage)
+{
+       if ((usage & HID_USAGE_PAGE) == WACOM_HID_UP_WACOMDIGITIZER) {
+               int subpage = (usage & 0xFF00) << 8;
+               int subusage = (usage & 0xFF);
+
+               if (subpage == WACOM_HID_SP_DIGITIZER ||
+                   subpage == WACOM_HID_SP_DIGITIZERINFO) {
+                       return usage;
+               }
+
+               if (subpage == HID_UP_UNDEFINED)
+                       subpage = HID_UP_DIGITIZER;
+
+               return subpage | subusage;
+       }
+
+       return usage;
+}
+
 static void wacom_map_usage(struct input_dev *input, struct hid_usage *usage,
                struct hid_field *field, __u8 type, __u16 code, int fuzz)
 {
        int fmin = field->logical_minimum;
        int fmax = field->logical_maximum;
+       unsigned int equivalent_usage = wacom_equivalent_usage(usage->hid);
+       int resolution_code = code;
+
+       if (equivalent_usage == HID_DG_TWIST) {
+               resolution_code = ABS_RZ;
+       }
 
        usage->type = type;
        usage->code = code;
@@ -1450,7 +1476,7 @@ static void wacom_map_usage(struct input_dev *input, struct hid_usage *usage,
        case EV_ABS:
                input_set_abs_params(input, code, fmin, fmax, fuzz, 0);
                input_abs_set_res(input, code,
-                                 hidinput_calc_abs_res(field, code));
+                                 hidinput_calc_abs_res(field, resolution_code));
                break;
        case EV_KEY:
                input_set_capability(input, EV_KEY, code);
@@ -1467,14 +1493,18 @@ static void wacom_wac_pen_usage_mapping(struct hid_device *hdev,
        struct wacom *wacom = hid_get_drvdata(hdev);
        struct wacom_wac *wacom_wac = &wacom->wacom_wac;
        struct input_dev *input = wacom_wac->pen_input;
+       unsigned equivalent_usage = wacom_equivalent_usage(usage->hid);
 
-       switch (usage->hid) {
+       switch (equivalent_usage) {
        case HID_GD_X:
                wacom_map_usage(input, usage, field, EV_ABS, ABS_X, 4);
                break;
        case HID_GD_Y:
                wacom_map_usage(input, usage, field, EV_ABS, ABS_Y, 4);
                break;
+       case HID_GD_Z:
+               wacom_map_usage(input, usage, field, EV_ABS, ABS_DISTANCE, 0);
+               break;
        case HID_DG_TIPPRESSURE:
                wacom_map_usage(input, usage, field, EV_ABS, ABS_PRESSURE, 0);
                break;
@@ -1485,6 +1515,15 @@ static void wacom_wac_pen_usage_mapping(struct hid_device *hdev,
                wacom_map_usage(input, usage, field, EV_KEY,
                                BTN_TOOL_RUBBER, 0);
                break;
+       case HID_DG_TILT_X:
+               wacom_map_usage(input, usage, field, EV_ABS, ABS_TILT_X, 0);
+               break;
+       case HID_DG_TILT_Y:
+               wacom_map_usage(input, usage, field, EV_ABS, ABS_TILT_Y, 0);
+               break;
+       case HID_DG_TWIST:
+               wacom_map_usage(input, usage, field, EV_ABS, ABS_Z, 0);
+               break;
        case HID_DG_ERASER:
        case HID_DG_TIPSWITCH:
                wacom_map_usage(input, usage, field, EV_KEY, BTN_TOUCH, 0);
@@ -1507,9 +1546,17 @@ static int wacom_wac_pen_event(struct hid_device *hdev, struct hid_field *field,
        struct wacom *wacom = hid_get_drvdata(hdev);
        struct wacom_wac *wacom_wac = &wacom->wacom_wac;
        struct input_dev *input = wacom_wac->pen_input;
+       unsigned equivalent_usage = wacom_equivalent_usage(usage->hid);
 
-       /* checking which Tool / tip switch to send */
-       switch (usage->hid) {
+       switch (equivalent_usage) {
+       case HID_GD_Z:
+               /*
+                * HID_GD_Z "should increase as the control's position is
+                * moved from high to low", while ABS_DISTANCE instead
+                * increases in value as the tool moves from low to high.
+                */
+               value = field->logical_maximum - value;
+               break;
        case HID_DG_INRANGE:
                wacom_wac->hid_data.inrange_state = value;
                return 0;
@@ -1573,8 +1620,9 @@ static void wacom_wac_finger_usage_mapping(struct hid_device *hdev,
        struct wacom_wac *wacom_wac = &wacom->wacom_wac;
        struct input_dev *input = wacom_wac->touch_input;
        unsigned touch_max = wacom_wac->features.touch_max;
+       unsigned equivalent_usage = wacom_equivalent_usage(usage->hid);
 
-       switch (usage->hid) {
+       switch (equivalent_usage) {
        case HID_GD_X:
                if (touch_max == 1)
                        wacom_map_usage(input, usage, field, EV_ABS, ABS_X, 4);
@@ -1649,8 +1697,9 @@ static int wacom_wac_finger_event(struct hid_device *hdev,
 {
        struct wacom *wacom = hid_get_drvdata(hdev);
        struct wacom_wac *wacom_wac = &wacom->wacom_wac;
+       unsigned equivalent_usage = wacom_equivalent_usage(usage->hid);
 
-       switch (usage->hid) {
+       switch (equivalent_usage) {
        case HID_GD_X:
                wacom_wac->hid_data.x = value;
                break;
@@ -1673,7 +1722,7 @@ static int wacom_wac_finger_event(struct hid_device *hdev,
 
 
        if (usage->usage_index + 1 == field->report_count) {
-               if (usage->hid == wacom_wac->hid_data.last_slot_field)
+               if (equivalent_usage == wacom_wac->hid_data.last_slot_field)
                        wacom_wac_finger_slot(wacom_wac, wacom_wac->touch_input);
        }
 
@@ -2448,7 +2497,7 @@ void wacom_setup_device_quirks(struct wacom *wacom)
        /*
         * Raw Wacom-mode pen and touch events both come from interface
         * 0, whose HID descriptor has an application usage of 0xFF0D
-        * (i.e., WACOM_VENDORDEFINED_PEN). We route pen packets back
+        * (i.e., WACOM_HID_WD_DIGITIZER). We route pen packets back
         * out through the HID_GENERIC device created for interface 1,
         * so rewrite this one to be of type WACOM_DEVICETYPE_TOUCH.
         */
@@ -2769,17 +2818,29 @@ int wacom_setup_touch_input_capabilities(struct input_dev *input_dev,
        return 0;
 }
 
+static int wacom_numbered_button_to_key(int n)
+{
+       if (n < 10)
+               return BTN_0 + n;
+       else if (n < 16)
+               return BTN_A + (n-10);
+       else if (n < 18)
+               return BTN_BASE + (n-16);
+       else
+               return 0;
+}
+
 static void wacom_setup_numbered_buttons(struct input_dev *input_dev,
                                int button_count)
 {
        int i;
 
-       for (i = 0; i < button_count && i < 10; i++)
-               __set_bit(BTN_0 + i, input_dev->keybit);
-       for (i = 10; i < button_count && i < 16; i++)
-               __set_bit(BTN_A + (i-10), input_dev->keybit);
-       for (i = 16; i < button_count && i < 18; i++)
-               __set_bit(BTN_BASE + (i-16), input_dev->keybit);
+       for (i = 0; i < button_count; i++) {
+               int key = wacom_numbered_button_to_key(i);
+
+               if (key)
+                       __set_bit(key, input_dev->keybit);
+       }
 }
 
 static void wacom_24hd_update_leds(struct wacom *wacom, int mask, int group)
@@ -2881,12 +2942,12 @@ static void wacom_report_numbered_buttons(struct input_dev *input_dev,
        for (i = 0; i < wacom->led.count; i++)
                wacom_update_led(wacom,  button_count, mask, i);
 
-       for (i = 0; i < button_count && i < 10; i++)
-               input_report_key(input_dev, BTN_0 + i, mask & (1 << i));
-       for (i = 10; i < button_count && i < 16; i++)
-               input_report_key(input_dev, BTN_A + (i-10), mask & (1 << i));
-       for (i = 16; i < button_count && i < 18; i++)
-               input_report_key(input_dev, BTN_BASE + (i-16), mask & (1 << i));
+       for (i = 0; i < button_count; i++) {
+               int key = wacom_numbered_button_to_key(i);
+
+               if (key)
+                       input_report_key(input_dev, key, mask & (1 << i));
+       }
 }
 
 int wacom_setup_pad_input_capabilities(struct input_dev *input_dev,