summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/hid/hid-uclogic-core.c54
-rw-r--r--drivers/hid/hid-uclogic-params.c2
-rw-r--r--drivers/hid/hid-uclogic-params.h2
3 files changed, 58 insertions, 0 deletions
diff --git a/drivers/hid/hid-uclogic-core.c b/drivers/hid/hid-uclogic-core.c
index 8f8e445d77aa..206642802ca5 100644
--- a/drivers/hid/hid-uclogic-core.c
+++ b/drivers/hid/hid-uclogic-core.c
@@ -16,6 +16,7 @@
#include <linux/device.h>
#include <linux/hid.h>
#include <linux/module.h>
+#include <linux/timer.h>
#include "usbhid/usbhid.h"
#include "hid-uclogic-params.h"
@@ -32,8 +33,40 @@ struct uclogic_drvdata {
* Only valid if desc_ptr is not NULL
*/
unsigned int desc_size;
+ /* Pen input device */
+ struct input_dev *pen_input;
+ /* In-range timer */
+ struct timer_list inrange_timer;
};
+/**
+ * uclogic_inrange_timeout - handle pen in-range state timeout.
+ * Emulate input events normally generated when pen goes out of range for
+ * tablets which don't report that.
+ *
+ * @t: The timer the timeout handler is attached to, stored in a struct
+ * uclogic_drvdata.
+ */
+static void uclogic_inrange_timeout(struct timer_list *t)
+{
+ struct uclogic_drvdata *drvdata = from_timer(drvdata, t,
+ inrange_timer);
+ struct input_dev *input = drvdata->pen_input;
+
+ if (input == NULL)
+ return;
+ input_report_abs(input, ABS_PRESSURE, 0);
+ /* If BTN_TOUCH state is changing */
+ if (test_bit(BTN_TOUCH, input->key)) {
+ input_event(input, EV_MSC, MSC_SCAN,
+ /* Digitizer Tip Switch usage */
+ 0xd0042);
+ input_report_key(input, BTN_TOUCH, 0);
+ }
+ input_report_key(input, BTN_TOOL_PEN, 0);
+ input_sync(input);
+}
+
static __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc,
unsigned int *rsize)
{
@@ -67,6 +100,8 @@ static int uclogic_input_mapping(struct hid_device *hdev,
static int uclogic_input_configured(struct hid_device *hdev,
struct hid_input *hi)
{
+ struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev);
+ struct uclogic_params *params = &drvdata->params;
char *name;
const char *suffix = NULL;
struct hid_field *field;
@@ -76,6 +111,15 @@ static int uclogic_input_configured(struct hid_device *hdev,
if (!hi->report)
return 0;
+ /*
+ * If this is the input corresponding to the pen report
+ * in need of tweaking.
+ */
+ if (hi->report->id == params->pen.id) {
+ /* Remember the input device so we can simulate events */
+ drvdata->pen_input = hi->input;
+ }
+
field = hi->report->field[0];
switch (field->application) {
@@ -130,6 +174,7 @@ static int uclogic_probe(struct hid_device *hdev,
rc = -ENOMEM;
goto failure;
}
+ timer_setup(&drvdata->inrange_timer, uclogic_inrange_timeout, 0);
hid_set_drvdata(hdev, drvdata);
/* Initialize the device and retrieve interface parameters */
@@ -220,6 +265,14 @@ static int uclogic_raw_event(struct hid_device *hdev,
/* Invert the in-range bit */
data[1] ^= 0x40;
}
+ /* If we need to emulate in-range detection */
+ if (params->pen.inrange == UCLOGIC_PARAMS_PEN_INRANGE_NONE) {
+ /* Set in-range bit */
+ data[1] |= 0x40;
+ /* (Re-)start in-range timeout */
+ mod_timer(&drvdata->inrange_timer,
+ jiffies + msecs_to_jiffies(100));
+ }
}
return 0;
@@ -229,6 +282,7 @@ static void uclogic_remove(struct hid_device *hdev)
{
struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev);
+ del_timer_sync(&drvdata->inrange_timer);
hid_hw_stop(hdev);
kfree(drvdata->desc_ptr);
uclogic_params_cleanup(&drvdata->params);
diff --git a/drivers/hid/hid-uclogic-params.c b/drivers/hid/hid-uclogic-params.c
index f555db120baa..b5e4d99c6771 100644
--- a/drivers/hid/hid-uclogic-params.c
+++ b/drivers/hid/hid-uclogic-params.c
@@ -36,6 +36,8 @@ const char *uclogic_params_pen_inrange_to_str(
return "normal";
case UCLOGIC_PARAMS_PEN_INRANGE_INVERTED:
return "inverted";
+ case UCLOGIC_PARAMS_PEN_INRANGE_NONE:
+ return "none";
default:
return NULL;
}
diff --git a/drivers/hid/hid-uclogic-params.h b/drivers/hid/hid-uclogic-params.h
index 4c78d9dd0576..665954d6ba57 100644
--- a/drivers/hid/hid-uclogic-params.h
+++ b/drivers/hid/hid-uclogic-params.h
@@ -25,6 +25,8 @@ enum uclogic_params_pen_inrange {
UCLOGIC_PARAMS_PEN_INRANGE_NORMAL = 0,
/* Inverted reports: zero - in proximity, one - out of proximity */
UCLOGIC_PARAMS_PEN_INRANGE_INVERTED,
+ /* No reports */
+ UCLOGIC_PARAMS_PEN_INRANGE_NONE,
};
/* Convert a pen in-range reporting type to a string */