From 2d7007153f0c9b1dd00c01894df7d26ddc32b79f Mon Sep 17 00:00:00 2001 From: Junghak Sung Date: Tue, 22 Sep 2015 10:30:30 -0300 Subject: [media] media: videobuf2: Restructure vb2_buffer Remove v4l2 stuff - v4l2_buf, v4l2_plane - from struct vb2_buffer. Add new member variables - bytesused, length, offset, userptr, fd, data_offset - to struct vb2_plane in order to cover all information of v4l2_plane. struct vb2_plane { unsigned int bytesused; unsigned int length; union { unsigned int offset; unsigned long userptr; int fd; } m; unsigned int data_offset; } Replace v4l2_buf with new member variables - index, type, memory - which are common fields for buffer management. struct vb2_buffer { unsigned int index; unsigned int type; unsigned int memory; unsigned int num_planes; struct vb2_plane planes[VIDEO_MAX_PLANES]; }; v4l2 specific fields - flags, field, timestamp, timecode, sequence - are moved to vb2_v4l2_buffer in videobuf2-v4l2.c struct vb2_v4l2_buffer { struct vb2_buffer vb2_buf; __u32 flags; __u32 field; struct timeval timestamp; struct v4l2_timecode timecode; __u32 sequence; }; Signed-off-by: Junghak Sung Signed-off-by: Geunyoung Kim Acked-by: Seung-Woo Kim Acked-by: Inki Dae Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/input/touchscreen/sur40.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/sur40.c b/drivers/input/touchscreen/sur40.c index 3f11763..98d0945 100644 --- a/drivers/input/touchscreen/sur40.c +++ b/drivers/input/touchscreen/sur40.c @@ -38,6 +38,7 @@ #include #include #include +#include #include /* read 512 bytes from endpoint 0x86 -> get header + blobs */ @@ -163,7 +164,7 @@ struct sur40_state { }; struct sur40_buffer { - struct vb2_buffer vb; + struct vb2_v4l2_buffer vb; struct list_head list; }; @@ -420,7 +421,7 @@ static void sur40_process_video(struct sur40_state *sur40) dev_dbg(sur40->dev, "header acquired\n"); - sgt = vb2_dma_sg_plane_desc(&new_buf->vb, 0); + sgt = vb2_dma_sg_plane_desc(&new_buf->vb.vb2_buf, 0); result = usb_sg_init(&sgr, sur40->usbdev, usb_rcvbulkpipe(sur40->usbdev, VIDEO_ENDPOINT), 0, @@ -443,15 +444,15 @@ static void sur40_process_video(struct sur40_state *sur40) goto err_poll; /* mark as finished */ - v4l2_get_timestamp(&new_buf->vb.v4l2_buf.timestamp); - new_buf->vb.v4l2_buf.sequence = sur40->sequence++; - new_buf->vb.v4l2_buf.field = V4L2_FIELD_NONE; - vb2_buffer_done(&new_buf->vb, VB2_BUF_STATE_DONE); + v4l2_get_timestamp(&new_buf->vb.timestamp); + new_buf->vb.sequence = sur40->sequence++; + new_buf->vb.field = V4L2_FIELD_NONE; + vb2_buffer_done(&new_buf->vb.vb2_buf, VB2_BUF_STATE_DONE); dev_dbg(sur40->dev, "buffer marked done\n"); return; err_poll: - vb2_buffer_done(&new_buf->vb, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&new_buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); } /* Initialize input device parameters. */ @@ -701,7 +702,7 @@ static void return_all_buffers(struct sur40_state *sur40, spin_lock(&sur40->qlock); list_for_each_entry_safe(buf, node, &sur40->buf_list, list) { - vb2_buffer_done(&buf->vb, state); + vb2_buffer_done(&buf->vb.vb2_buf, state); list_del(&buf->list); } spin_unlock(&sur40->qlock); -- cgit v1.1 From 1c5dd13459fe7caaeda82f7be878720c978d26f2 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 7 Oct 2015 03:03:57 +0200 Subject: input: i8042: Avoid resetting controller on system suspend/resume If the upcoming system suspend is not going to be handled by the platform firmware, like in the suspend-to-idle case, it is not necessary to reset the controller in i8042_pm_suspend(), so avoid doing that. Moreover, if the system resume currently in progress has not been started by the platform firmware, like in the suspend-to-idle case, i8042_controller_resume() need not be called by i8042_pm_resume(), so avoid doing that too in that case. Additionally, try to catch the event that woke up the system by calling the interrupt handler early during system resume if it has not been started by the platform firmware. Signed-off-by: Rafael J. Wysocki Acked-by: Dmitry Torokhov --- drivers/input/serio/i8042.c | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c index db91de5..4541957 100644 --- a/drivers/input/serio/i8042.c +++ b/drivers/input/serio/i8042.c @@ -24,6 +24,7 @@ #include #include #include +#include #include @@ -1170,7 +1171,8 @@ static int i8042_pm_suspend(struct device *dev) { int i; - i8042_controller_reset(true); + if (pm_suspend_via_firmware()) + i8042_controller_reset(true); /* Set up serio interrupts for system wakeup. */ for (i = 0; i < I8042_NUM_PORTS; i++) { @@ -1183,8 +1185,17 @@ static int i8042_pm_suspend(struct device *dev) return 0; } +static int i8042_pm_resume_noirq(struct device *dev) +{ + if (!pm_resume_via_firmware()) + i8042_interrupt(0, NULL); + + return 0; +} + static int i8042_pm_resume(struct device *dev) { + bool force_reset; int i; for (i = 0; i < I8042_NUM_PORTS; i++) { @@ -1195,11 +1206,21 @@ static int i8042_pm_resume(struct device *dev) } /* - * On resume from S2R we always try to reset the controller - * to bring it in a sane state. (In case of S2D we expect - * BIOS to reset the controller for us.) + * If platform firmware was not going to be involved in suspend, we did + * not restore the controller state to whatever it had been at boot + * time, so we do not need to do anything. */ - return i8042_controller_resume(true); + if (!pm_suspend_via_firmware()) + return 0; + + /* + * We only need to reset the controller if we are resuming after handing + * off control to the platform firmware, otherwise we can simply restore + * the mode. + */ + force_reset = pm_resume_via_firmware(); + + return i8042_controller_resume(force_reset); } static int i8042_pm_thaw(struct device *dev) @@ -1223,6 +1244,7 @@ static int i8042_pm_restore(struct device *dev) static const struct dev_pm_ops i8042_pm_ops = { .suspend = i8042_pm_suspend, + .resume_noirq = i8042_pm_resume_noirq, .resume = i8042_pm_resume, .thaw = i8042_pm_thaw, .poweroff = i8042_pm_reset, -- cgit v1.1 From 33119e80c3b96ac81912677a6e86dc1890a5859f Mon Sep 17 00:00:00 2001 From: Junghak Sung Date: Tue, 6 Oct 2015 06:37:46 -0300 Subject: [media] media: videobuf2: Change queue_setup argument Replace struct v4l2_format * with void * to make queue_setup() for common use. And then, modify all device drivers related with this change. Signed-off-by: Junghak Sung Signed-off-by: Geunyoung Kim Acked-by: Seung-Woo Kim Acked-by: Inki Dae Signed-off-by: Hans Verkuil [hans.verkuil@cisco.com: fix missing const in fimc-lite.c] Signed-off-by: Mauro Carvalho Chehab --- drivers/input/touchscreen/sur40.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/sur40.c b/drivers/input/touchscreen/sur40.c index 98d0945..d214f22 100644 --- a/drivers/input/touchscreen/sur40.c +++ b/drivers/input/touchscreen/sur40.c @@ -644,10 +644,11 @@ static void sur40_disconnect(struct usb_interface *interface) * minimum number: many DMA engines need a minimum of 2 buffers in the * queue and you need to have another available for userspace processing. */ -static int sur40_queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt, +static int sur40_queue_setup(struct vb2_queue *q, const void *parg, unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[]) { + const struct v4l2_format *fmt = parg; struct sur40_state *sur40 = vb2_get_drv_priv(q); if (q->num_buffers + *nbuffers < 3) -- cgit v1.1 From 3821a065f5672c430a088ae68b4da2a2d2b34106 Mon Sep 17 00:00:00 2001 From: "Andrew F. Davis" Date: Fri, 23 Oct 2015 08:59:11 -0500 Subject: spi: Drop owner assignment from spi_drivers An spi_driver does not need to set an owner, it will be populated by the driver core. Signed-off-by: Andrew F. Davis Acked-by: Jonathan Cameron Signed-off-by: Mark Brown --- drivers/input/misc/ad714x-spi.c | 1 - drivers/input/misc/adxl34x-spi.c | 1 - drivers/input/touchscreen/ad7877.c | 1 - drivers/input/touchscreen/ad7879-spi.c | 1 - drivers/input/touchscreen/ads7846.c | 1 - drivers/input/touchscreen/cyttsp4_spi.c | 1 - drivers/input/touchscreen/cyttsp_spi.c | 1 - drivers/input/touchscreen/tsc2005.c | 1 - 8 files changed, 8 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/misc/ad714x-spi.c b/drivers/input/misc/ad714x-spi.c index a79e50b..fea315e 100644 --- a/drivers/input/misc/ad714x-spi.c +++ b/drivers/input/misc/ad714x-spi.c @@ -113,7 +113,6 @@ static int ad714x_spi_remove(struct spi_device *spi) static struct spi_driver ad714x_spi_driver = { .driver = { .name = "ad714x_captouch", - .owner = THIS_MODULE, .pm = &ad714x_spi_pm, }, .probe = ad714x_spi_probe, diff --git a/drivers/input/misc/adxl34x-spi.c b/drivers/input/misc/adxl34x-spi.c index da6e76b..3ec03ad 100644 --- a/drivers/input/misc/adxl34x-spi.c +++ b/drivers/input/misc/adxl34x-spi.c @@ -120,7 +120,6 @@ static SIMPLE_DEV_PM_OPS(adxl34x_spi_pm, adxl34x_spi_suspend, static struct spi_driver adxl34x_driver = { .driver = { .name = "adxl34x", - .owner = THIS_MODULE, .pm = &adxl34x_spi_pm, }, .probe = adxl34x_spi_probe, diff --git a/drivers/input/touchscreen/ad7877.c b/drivers/input/touchscreen/ad7877.c index da4e5bb..9c250ae 100644 --- a/drivers/input/touchscreen/ad7877.c +++ b/drivers/input/touchscreen/ad7877.c @@ -843,7 +843,6 @@ static SIMPLE_DEV_PM_OPS(ad7877_pm, ad7877_suspend, ad7877_resume); static struct spi_driver ad7877_driver = { .driver = { .name = "ad7877", - .owner = THIS_MODULE, .pm = &ad7877_pm, }, .probe = ad7877_probe, diff --git a/drivers/input/touchscreen/ad7879-spi.c b/drivers/input/touchscreen/ad7879-spi.c index 1a7b114..48033c2 100644 --- a/drivers/input/touchscreen/ad7879-spi.c +++ b/drivers/input/touchscreen/ad7879-spi.c @@ -149,7 +149,6 @@ static int ad7879_spi_remove(struct spi_device *spi) static struct spi_driver ad7879_spi_driver = { .driver = { .name = "ad7879", - .owner = THIS_MODULE, .pm = &ad7879_pm_ops, }, .probe = ad7879_spi_probe, diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c index 0f5f968..7f268cd 100644 --- a/drivers/input/touchscreen/ads7846.c +++ b/drivers/input/touchscreen/ads7846.c @@ -1496,7 +1496,6 @@ static int ads7846_remove(struct spi_device *spi) static struct spi_driver ads7846_driver = { .driver = { .name = "ads7846", - .owner = THIS_MODULE, .pm = &ads7846_pm, .of_match_table = of_match_ptr(ads7846_dt_ids), }, diff --git a/drivers/input/touchscreen/cyttsp4_spi.c b/drivers/input/touchscreen/cyttsp4_spi.c index b19434c..ec5f7c7 100644 --- a/drivers/input/touchscreen/cyttsp4_spi.c +++ b/drivers/input/touchscreen/cyttsp4_spi.c @@ -185,7 +185,6 @@ static int cyttsp4_spi_remove(struct spi_device *spi) static struct spi_driver cyttsp4_spi_driver = { .driver = { .name = CYTTSP4_SPI_NAME, - .owner = THIS_MODULE, .pm = &cyttsp4_pm_ops, }, .probe = cyttsp4_spi_probe, diff --git a/drivers/input/touchscreen/cyttsp_spi.c b/drivers/input/touchscreen/cyttsp_spi.c index 4728bcb..bbeeb24 100644 --- a/drivers/input/touchscreen/cyttsp_spi.c +++ b/drivers/input/touchscreen/cyttsp_spi.c @@ -182,7 +182,6 @@ static int cyttsp_spi_remove(struct spi_device *spi) static struct spi_driver cyttsp_spi_driver = { .driver = { .name = CY_SPI_NAME, - .owner = THIS_MODULE, .pm = &cyttsp_pm_ops, }, .probe = cyttsp_spi_probe, diff --git a/drivers/input/touchscreen/tsc2005.c b/drivers/input/touchscreen/tsc2005.c index 0f65d02..f41f233 100644 --- a/drivers/input/touchscreen/tsc2005.c +++ b/drivers/input/touchscreen/tsc2005.c @@ -752,7 +752,6 @@ static SIMPLE_DEV_PM_OPS(tsc2005_pm_ops, tsc2005_suspend, tsc2005_resume); static struct spi_driver tsc2005_driver = { .driver = { .name = "tsc2005", - .owner = THIS_MODULE, .pm = &tsc2005_pm_ops, }, .probe = tsc2005_probe, -- cgit v1.1 From 79211c8ed19c055ca105502c8733800d442a0ae6 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Mon, 9 Nov 2015 14:58:13 -0800 Subject: remove abs64() Switch everything to the new and more capable implementation of abs(). Mainly to give the new abs() a bit of a workout. Cc: Michal Nazarewicz Cc: John Stultz Cc: Ingo Molnar Cc: Steven Rostedt Cc: Peter Zijlstra Cc: Masami Hiramatsu Cc: Peter Zijlstra Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/input/joystick/walkera0701.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/joystick/walkera0701.c b/drivers/input/joystick/walkera0701.c index d88f5dd..9c07fe9 100644 --- a/drivers/input/joystick/walkera0701.c +++ b/drivers/input/joystick/walkera0701.c @@ -150,7 +150,7 @@ static void walkera0701_irq_handler(void *handler_data) if (w->counter == 24) { /* full frame */ walkera0701_parse_frame(w); w->counter = NO_SYNC; - if (abs64(pulse_time - SYNC_PULSE) < RESERVE) /* new frame sync */ + if (abs(pulse_time - SYNC_PULSE) < RESERVE) /* new frame sync */ w->counter = 0; } else { if ((pulse_time > (ANALOG_MIN_PULSE - RESERVE) @@ -161,7 +161,7 @@ static void walkera0701_irq_handler(void *handler_data) } else w->counter = NO_SYNC; } - } else if (abs64(pulse_time - SYNC_PULSE - BIN0_PULSE) < + } else if (abs(pulse_time - SYNC_PULSE - BIN0_PULSE) < RESERVE + BIN1_PULSE - BIN0_PULSE) /* frame sync .. */ w->counter = 0; -- cgit v1.1 From d352c0e1f32e168e20d14d666a73f3712fbdc1f5 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 2 Dec 2015 09:28:03 -0800 Subject: Input: sparcspkr - use platform_register/unregister_drivers() These new helpers simplify implementing multi-driver modules and properly handle failure to register one driver by unregistering all previously registered drivers. Signed-off-by: Thierry Reding Signed-off-by: Dmitry Torokhov --- drivers/input/misc/sparcspkr.c | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/misc/sparcspkr.c b/drivers/input/misc/sparcspkr.c index 6f997aa..4a5afc7 100644 --- a/drivers/input/misc/sparcspkr.c +++ b/drivers/input/misc/sparcspkr.c @@ -345,23 +345,19 @@ static struct platform_driver grover_beep_driver = { .shutdown = sparcspkr_shutdown, }; +static struct platform_driver * const drivers[] = { + &bbc_beep_driver, + &grover_beep_driver, +}; + static int __init sparcspkr_init(void) { - int err = platform_driver_register(&bbc_beep_driver); - - if (!err) { - err = platform_driver_register(&grover_beep_driver); - if (err) - platform_driver_unregister(&bbc_beep_driver); - } - - return err; + return platform_register_drivers(drivers, ARRAY_SIZE(drivers)); } static void __exit sparcspkr_exit(void) { - platform_driver_unregister(&bbc_beep_driver); - platform_driver_unregister(&grover_beep_driver); + platform_unregister_drivers(drivers, ARRAY_SIZE(drivers)); } module_init(sparcspkr_init); -- cgit v1.1 From 86c03f46b9a40b251e4d97f461a13f8b1f18ae17 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 1 Dec 2015 13:31:06 -0800 Subject: Input: wacom_w8001 - use __set_bit for evbits Signed-off-by: Peter Hutterer Acked-by: Benjamin Tissoires Reviewed-by: Ping Cheng Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/wacom_w8001.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/wacom_w8001.c b/drivers/input/touchscreen/wacom_w8001.c index 2792ca3..d194d57 100644 --- a/drivers/input/touchscreen/wacom_w8001.c +++ b/drivers/input/touchscreen/wacom_w8001.c @@ -393,7 +393,8 @@ static int w8001_setup(struct w8001 *w8001) msleep(250); /* wait 250ms before querying the device */ - dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + __set_bit(EV_KEY, dev->evbit); + __set_bit(EV_ABS, dev->evbit); strlcat(w8001->name, "Wacom Serial", sizeof(w8001->name)); __set_bit(INPUT_PROP_DIRECT, dev->propbit); -- cgit v1.1 From ec9acda7360de7ee70cc0db36f41c1fd819a3310 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 1 Dec 2015 13:31:28 -0800 Subject: Input: wacom_w8001 - set BTN_TOOL_DOUBLETAP if we have 2fg support Signed-off-by: Peter Hutterer Acked-by: Benjamin Tissoires Reviewed-by: Ping Cheng Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/wacom_w8001.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/wacom_w8001.c b/drivers/input/touchscreen/wacom_w8001.c index d194d57..222006e 100644 --- a/drivers/input/touchscreen/wacom_w8001.c +++ b/drivers/input/touchscreen/wacom_w8001.c @@ -471,6 +471,7 @@ static int w8001_setup(struct w8001 *w8001) case 5: w8001->pktlen = W8001_PKTLEN_TOUCH2FG; + __set_bit(BTN_TOOL_DOUBLETAP, dev->keybit); input_mt_init_slots(dev, 2, 0); input_set_abs_params(dev, ABS_MT_POSITION_X, 0, touch.x, 0, 0); -- cgit v1.1 From e171735410ae5a0ebf90d6bc6a8a97fc28bfc041 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 3 Dec 2015 12:24:26 -0800 Subject: Input: wacom_w8001 - handle touch error case correctly If a device failed at the pen setup and gets a zero reply from the touch device, we need to return an error. Otherwise we have a device with nothing but a name and the EV_KEY and EV_ABS bits. Signed-off-by: Peter Hutterer Acked-by: Benjamin Tissoires Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/wacom_w8001.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/wacom_w8001.c b/drivers/input/touchscreen/wacom_w8001.c index 222006e..9ea5f82 100644 --- a/drivers/input/touchscreen/wacom_w8001.c +++ b/drivers/input/touchscreen/wacom_w8001.c @@ -385,7 +385,7 @@ static int w8001_setup(struct w8001 *w8001) struct input_dev *dev = w8001->dev; struct w8001_coord coord; struct w8001_touch_query touch; - int error; + int error, err_pen, err_touch; error = w8001_command(w8001, W8001_CMD_STOP, false); if (error) @@ -400,8 +400,8 @@ static int w8001_setup(struct w8001 *w8001) __set_bit(INPUT_PROP_DIRECT, dev->propbit); /* penabled? */ - error = w8001_command(w8001, W8001_CMD_QUERY, true); - if (!error) { + err_pen = w8001_command(w8001, W8001_CMD_QUERY, true); + if (!err_pen) { __set_bit(BTN_TOUCH, dev->keybit); __set_bit(BTN_TOOL_PEN, dev->keybit); __set_bit(BTN_TOOL_RUBBER, dev->keybit); @@ -426,13 +426,12 @@ static int w8001_setup(struct w8001 *w8001) } /* Touch enabled? */ - error = w8001_command(w8001, W8001_CMD_TOUCHQUERY, true); + err_touch = w8001_command(w8001, W8001_CMD_TOUCHQUERY, true); - /* - * Some non-touch devices may reply to the touch query. But their - * second byte is empty, which indicates touch is not supported. - */ - if (!error && w8001->response[1]) { + if (!err_touch && !w8001->response[1]) + err_touch = -ENXIO; + + if (!err_touch) { __set_bit(BTN_TOUCH, dev->keybit); __set_bit(BTN_TOOL_FINGER, dev->keybit); @@ -491,7 +490,7 @@ static int w8001_setup(struct w8001 *w8001) strlcat(w8001->name, " Touchscreen", sizeof(w8001->name)); - return 0; + return !err_pen || !err_touch ? 0 : -ENXIO; } /* -- cgit v1.1 From 5d0a4fe2a9347b05bc5db2963cb7cd6cf014a9ed Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 3 Dec 2015 12:25:09 -0800 Subject: Input: wacom_w8001 - split pen and touch initialization up This is preparation work for splitting it up for two event nodes. Signed-off-by: Peter Hutterer Acked-by: Benjamin Tissoires Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/wacom_w8001.c | 194 ++++++++++++++++++-------------- 1 file changed, 111 insertions(+), 83 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/wacom_w8001.c b/drivers/input/touchscreen/wacom_w8001.c index 9ea5f82..de8f0e4 100644 --- a/drivers/input/touchscreen/wacom_w8001.c +++ b/drivers/input/touchscreen/wacom_w8001.c @@ -380,12 +380,10 @@ static void w8001_close(struct input_dev *dev) w8001_command(w8001, W8001_CMD_STOP, false); } -static int w8001_setup(struct w8001 *w8001) +static int w8001_detect(struct w8001 *w8001) { struct input_dev *dev = w8001->dev; - struct w8001_coord coord; - struct w8001_touch_query touch; - int error, err_pen, err_touch; + int error; error = w8001_command(w8001, W8001_CMD_STOP, false); if (error) @@ -399,98 +397,121 @@ static int w8001_setup(struct w8001 *w8001) __set_bit(INPUT_PROP_DIRECT, dev->propbit); + return 0; +} + +static int w8001_setup_pen(struct w8001 *w8001) +{ + struct input_dev *dev = w8001->dev; + struct w8001_coord coord; + int error; + /* penabled? */ - err_pen = w8001_command(w8001, W8001_CMD_QUERY, true); - if (!err_pen) { - __set_bit(BTN_TOUCH, dev->keybit); - __set_bit(BTN_TOOL_PEN, dev->keybit); - __set_bit(BTN_TOOL_RUBBER, dev->keybit); - __set_bit(BTN_STYLUS, dev->keybit); - __set_bit(BTN_STYLUS2, dev->keybit); - - parse_pen_data(w8001->response, &coord); - w8001->max_pen_x = coord.x; - w8001->max_pen_y = coord.y; - - input_set_abs_params(dev, ABS_X, 0, coord.x, 0, 0); - input_set_abs_params(dev, ABS_Y, 0, coord.y, 0, 0); - input_abs_set_res(dev, ABS_X, W8001_PEN_RESOLUTION); - input_abs_set_res(dev, ABS_Y, W8001_PEN_RESOLUTION); - input_set_abs_params(dev, ABS_PRESSURE, 0, coord.pen_pressure, 0, 0); - if (coord.tilt_x && coord.tilt_y) { - input_set_abs_params(dev, ABS_TILT_X, 0, coord.tilt_x, 0, 0); - input_set_abs_params(dev, ABS_TILT_Y, 0, coord.tilt_y, 0, 0); - } - w8001->id = 0x90; - strlcat(w8001->name, " Penabled", sizeof(w8001->name)); + error = w8001_command(w8001, W8001_CMD_QUERY, true); + if (error) + return error; + + __set_bit(BTN_TOUCH, dev->keybit); + __set_bit(BTN_TOOL_PEN, dev->keybit); + __set_bit(BTN_TOOL_RUBBER, dev->keybit); + __set_bit(BTN_STYLUS, dev->keybit); + __set_bit(BTN_STYLUS2, dev->keybit); + + parse_pen_data(w8001->response, &coord); + w8001->max_pen_x = coord.x; + w8001->max_pen_y = coord.y; + + input_set_abs_params(dev, ABS_X, 0, coord.x, 0, 0); + input_set_abs_params(dev, ABS_Y, 0, coord.y, 0, 0); + input_abs_set_res(dev, ABS_X, W8001_PEN_RESOLUTION); + input_abs_set_res(dev, ABS_Y, W8001_PEN_RESOLUTION); + input_set_abs_params(dev, ABS_PRESSURE, 0, coord.pen_pressure, 0, 0); + if (coord.tilt_x && coord.tilt_y) { + input_set_abs_params(dev, ABS_TILT_X, 0, coord.tilt_x, 0, 0); + input_set_abs_params(dev, ABS_TILT_Y, 0, coord.tilt_y, 0, 0); } + w8001->id = 0x90; + strlcat(w8001->name, " Penabled", sizeof(w8001->name)); + + return 0; +} + +static int w8001_setup_touch(struct w8001 *w8001) +{ + struct input_dev *dev = w8001->dev; + struct w8001_touch_query touch; + int error; + /* Touch enabled? */ - err_touch = w8001_command(w8001, W8001_CMD_TOUCHQUERY, true); + error = w8001_command(w8001, W8001_CMD_TOUCHQUERY, true); + if (error) + return error; + /* + * Some non-touch devices may reply to the touch query. But their + * second byte is empty, which indicates touch is not supported. + */ + if (!w8001->response[1]) + return -ENXIO; - if (!err_touch && !w8001->response[1]) - err_touch = -ENXIO; + __set_bit(BTN_TOUCH, dev->keybit); + __set_bit(BTN_TOOL_FINGER, dev->keybit); - if (!err_touch) { - __set_bit(BTN_TOUCH, dev->keybit); - __set_bit(BTN_TOOL_FINGER, dev->keybit); + parse_touchquery(w8001->response, &touch); + w8001->max_touch_x = touch.x; + w8001->max_touch_y = touch.y; - parse_touchquery(w8001->response, &touch); - w8001->max_touch_x = touch.x; - w8001->max_touch_y = touch.y; + if (w8001->max_pen_x && w8001->max_pen_y) { + /* if pen is supported scale to pen maximum */ + touch.x = w8001->max_pen_x; + touch.y = w8001->max_pen_y; + touch.panel_res = W8001_PEN_RESOLUTION; + } - if (w8001->max_pen_x && w8001->max_pen_y) { - /* if pen is supported scale to pen maximum */ - touch.x = w8001->max_pen_x; - touch.y = w8001->max_pen_y; - touch.panel_res = W8001_PEN_RESOLUTION; - } + input_set_abs_params(dev, ABS_X, 0, touch.x, 0, 0); + input_set_abs_params(dev, ABS_Y, 0, touch.y, 0, 0); + input_abs_set_res(dev, ABS_X, touch.panel_res); + input_abs_set_res(dev, ABS_Y, touch.panel_res); - input_set_abs_params(dev, ABS_X, 0, touch.x, 0, 0); - input_set_abs_params(dev, ABS_Y, 0, touch.y, 0, 0); - input_abs_set_res(dev, ABS_X, touch.panel_res); - input_abs_set_res(dev, ABS_Y, touch.panel_res); - - switch (touch.sensor_id) { - case 0: - case 2: - w8001->pktlen = W8001_PKTLEN_TOUCH93; - w8001->id = 0x93; - strlcat(w8001->name, " 1FG", sizeof(w8001->name)); - break; + switch (touch.sensor_id) { + case 0: + case 2: + w8001->pktlen = W8001_PKTLEN_TOUCH93; + w8001->id = 0x93; + strlcat(w8001->name, " 1FG", sizeof(w8001->name)); + break; - case 1: - case 3: - case 4: - w8001->pktlen = W8001_PKTLEN_TOUCH9A; - strlcat(w8001->name, " 1FG", sizeof(w8001->name)); - w8001->id = 0x9a; - break; + case 1: + case 3: + case 4: + w8001->pktlen = W8001_PKTLEN_TOUCH9A; + strlcat(w8001->name, " 1FG", sizeof(w8001->name)); + w8001->id = 0x9a; + break; - case 5: - w8001->pktlen = W8001_PKTLEN_TOUCH2FG; - - __set_bit(BTN_TOOL_DOUBLETAP, dev->keybit); - input_mt_init_slots(dev, 2, 0); - input_set_abs_params(dev, ABS_MT_POSITION_X, - 0, touch.x, 0, 0); - input_set_abs_params(dev, ABS_MT_POSITION_Y, - 0, touch.y, 0, 0); - input_set_abs_params(dev, ABS_MT_TOOL_TYPE, - 0, MT_TOOL_MAX, 0, 0); - - strlcat(w8001->name, " 2FG", sizeof(w8001->name)); - if (w8001->max_pen_x && w8001->max_pen_y) - w8001->id = 0xE3; - else - w8001->id = 0xE2; - break; - } + case 5: + w8001->pktlen = W8001_PKTLEN_TOUCH2FG; + + __set_bit(BTN_TOOL_DOUBLETAP, dev->keybit); + input_mt_init_slots(dev, 2, 0); + input_set_abs_params(dev, ABS_MT_POSITION_X, + 0, touch.x, 0, 0); + input_set_abs_params(dev, ABS_MT_POSITION_Y, + 0, touch.y, 0, 0); + input_set_abs_params(dev, ABS_MT_TOOL_TYPE, + 0, MT_TOOL_MAX, 0, 0); + + strlcat(w8001->name, " 2FG", sizeof(w8001->name)); + if (w8001->max_pen_x && w8001->max_pen_y) + w8001->id = 0xE3; + else + w8001->id = 0xE2; + break; } strlcat(w8001->name, " Touchscreen", sizeof(w8001->name)); - return !err_pen || !err_touch ? 0 : -ENXIO; + return 0; } /* @@ -519,7 +540,7 @@ static int w8001_connect(struct serio *serio, struct serio_driver *drv) { struct w8001 *w8001; struct input_dev *input_dev; - int err; + int err, err_pen, err_touch; w8001 = kzalloc(sizeof(struct w8001), GFP_KERNEL); input_dev = input_allocate_device(); @@ -538,10 +559,17 @@ static int w8001_connect(struct serio *serio, struct serio_driver *drv) if (err) goto fail2; - err = w8001_setup(w8001); + err = w8001_detect(w8001); if (err) goto fail3; + err_pen = w8001_setup_pen(w8001); + err_touch = w8001_setup_touch(w8001); + if (err_pen && err_touch) { + err = -ENXIO; + goto fail3; + } + input_dev->name = w8001->name; input_dev->phys = w8001->phys; input_dev->id.product = w8001->id; -- cgit v1.1 From e0361b70175f0cd6199dd9ed6679632de73973d4 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 3 Dec 2015 12:26:19 -0800 Subject: Input: wacom_w8001 - split the touch and pen devices into two devices These devices have a pen device and a touch device through the same serial protocol, split it up into two separate devices like we do for USB Wacom tablets too. Userspace already matches on the device name so we can't drop it completely. Compose the same basename based on capabilities and append the tool type, leading to a name like "Wacom Serial Penabled 2FG Touchscreen Pen". Note that this drops BTN_TOOL_FINGER, it is not needed once the tools are split out (and a touch device with BTN_TOOL_FINGER is interpreted as touchpad by most of userspace). Signed-off-by: Peter Hutterer Acked-by: Benjamin Tissoires Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/wacom_w8001.c | 167 ++++++++++++++++++++++---------- 1 file changed, 116 insertions(+), 51 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/wacom_w8001.c b/drivers/input/touchscreen/wacom_w8001.c index de8f0e4..fe983e7 100644 --- a/drivers/input/touchscreen/wacom_w8001.c +++ b/drivers/input/touchscreen/wacom_w8001.c @@ -80,7 +80,8 @@ struct w8001_touch_query { */ struct w8001 { - struct input_dev *dev; + struct input_dev *pen_dev; + struct input_dev *touch_dev; struct serio *serio; struct completion cmd_done; int id; @@ -95,7 +96,10 @@ struct w8001 { u16 max_touch_y; u16 max_pen_x; u16 max_pen_y; - char name[64]; + char pen_name[64]; + char touch_name[64]; + int open_count; + struct mutex mutex; }; static void parse_pen_data(u8 *data, struct w8001_coord *coord) @@ -141,7 +145,7 @@ static void scale_touch_coordinates(struct w8001 *w8001, static void parse_multi_touch(struct w8001 *w8001) { - struct input_dev *dev = w8001->dev; + struct input_dev *dev = w8001->touch_dev; unsigned char *data = w8001->data; unsigned int x, y; int i; @@ -207,7 +211,7 @@ static void parse_touchquery(u8 *data, struct w8001_touch_query *query) static void report_pen_events(struct w8001 *w8001, struct w8001_coord *coord) { - struct input_dev *dev = w8001->dev; + struct input_dev *dev = w8001->pen_dev; /* * We have 1 bit for proximity (rdy) and 3 bits for tip, side, @@ -233,11 +237,6 @@ static void report_pen_events(struct w8001 *w8001, struct w8001_coord *coord) break; case BTN_TOOL_FINGER: - input_report_key(dev, BTN_TOUCH, 0); - input_report_key(dev, BTN_TOOL_FINGER, 0); - input_sync(dev); - /* fall through */ - case KEY_RESERVED: w8001->type = coord->f2 ? BTN_TOOL_RUBBER : BTN_TOOL_PEN; break; @@ -261,7 +260,7 @@ static void report_pen_events(struct w8001 *w8001, struct w8001_coord *coord) static void report_single_touch(struct w8001 *w8001, struct w8001_coord *coord) { - struct input_dev *dev = w8001->dev; + struct input_dev *dev = w8001->touch_dev; unsigned int x = coord->x; unsigned int y = coord->y; @@ -271,7 +270,6 @@ static void report_single_touch(struct w8001 *w8001, struct w8001_coord *coord) input_report_abs(dev, ABS_X, x); input_report_abs(dev, ABS_Y, y); input_report_key(dev, BTN_TOUCH, coord->tsw); - input_report_key(dev, BTN_TOOL_FINGER, coord->tsw); input_sync(dev); @@ -369,20 +367,36 @@ static int w8001_command(struct w8001 *w8001, unsigned char command, static int w8001_open(struct input_dev *dev) { struct w8001 *w8001 = input_get_drvdata(dev); + int err; + + err = mutex_lock_interruptible(&w8001->mutex); + if (err) + return err; + + if (w8001->open_count++ == 0) { + err = w8001_command(w8001, W8001_CMD_START, false); + if (err) + w8001->open_count--; + } - return w8001_command(w8001, W8001_CMD_START, false); + mutex_unlock(&w8001->mutex); + return err; } static void w8001_close(struct input_dev *dev) { struct w8001 *w8001 = input_get_drvdata(dev); - w8001_command(w8001, W8001_CMD_STOP, false); + mutex_lock(&w8001->mutex); + + if (--w8001->open_count == 0) + w8001_command(w8001, W8001_CMD_STOP, false); + + mutex_unlock(&w8001->mutex); } static int w8001_detect(struct w8001 *w8001) { - struct input_dev *dev = w8001->dev; int error; error = w8001_command(w8001, W8001_CMD_STOP, false); @@ -391,18 +405,13 @@ static int w8001_detect(struct w8001 *w8001) msleep(250); /* wait 250ms before querying the device */ - __set_bit(EV_KEY, dev->evbit); - __set_bit(EV_ABS, dev->evbit); - strlcat(w8001->name, "Wacom Serial", sizeof(w8001->name)); - - __set_bit(INPUT_PROP_DIRECT, dev->propbit); - return 0; } -static int w8001_setup_pen(struct w8001 *w8001) +static int w8001_setup_pen(struct w8001 *w8001, char *basename, + size_t basename_sz) { - struct input_dev *dev = w8001->dev; + struct input_dev *dev = w8001->pen_dev; struct w8001_coord coord; int error; @@ -411,11 +420,14 @@ static int w8001_setup_pen(struct w8001 *w8001) if (error) return error; + __set_bit(EV_KEY, dev->evbit); + __set_bit(EV_ABS, dev->evbit); __set_bit(BTN_TOUCH, dev->keybit); __set_bit(BTN_TOOL_PEN, dev->keybit); __set_bit(BTN_TOOL_RUBBER, dev->keybit); __set_bit(BTN_STYLUS, dev->keybit); __set_bit(BTN_STYLUS2, dev->keybit); + __set_bit(INPUT_PROP_DIRECT, dev->propbit); parse_pen_data(w8001->response, &coord); w8001->max_pen_x = coord.x; @@ -432,17 +444,19 @@ static int w8001_setup_pen(struct w8001 *w8001) } w8001->id = 0x90; - strlcat(w8001->name, " Penabled", sizeof(w8001->name)); + strlcat(basename, " Penabled", basename_sz); return 0; } -static int w8001_setup_touch(struct w8001 *w8001) +static int w8001_setup_touch(struct w8001 *w8001, char *basename, + size_t basename_sz) { - struct input_dev *dev = w8001->dev; + struct input_dev *dev = w8001->touch_dev; struct w8001_touch_query touch; int error; + /* Touch enabled? */ error = w8001_command(w8001, W8001_CMD_TOUCHQUERY, true); if (error) @@ -454,8 +468,10 @@ static int w8001_setup_touch(struct w8001 *w8001) if (!w8001->response[1]) return -ENXIO; + __set_bit(EV_KEY, dev->evbit); + __set_bit(EV_ABS, dev->evbit); __set_bit(BTN_TOUCH, dev->keybit); - __set_bit(BTN_TOOL_FINGER, dev->keybit); + __set_bit(INPUT_PROP_DIRECT, dev->propbit); parse_touchquery(w8001->response, &touch); w8001->max_touch_x = touch.x; @@ -478,14 +494,14 @@ static int w8001_setup_touch(struct w8001 *w8001) case 2: w8001->pktlen = W8001_PKTLEN_TOUCH93; w8001->id = 0x93; - strlcat(w8001->name, " 1FG", sizeof(w8001->name)); + strlcat(basename, " 1FG", basename_sz); break; case 1: case 3: case 4: w8001->pktlen = W8001_PKTLEN_TOUCH9A; - strlcat(w8001->name, " 1FG", sizeof(w8001->name)); + strlcat(basename, " 1FG", basename_sz); w8001->id = 0x9a; break; @@ -501,7 +517,7 @@ static int w8001_setup_touch(struct w8001 *w8001) input_set_abs_params(dev, ABS_MT_TOOL_TYPE, 0, MT_TOOL_MAX, 0, 0); - strlcat(w8001->name, " 2FG", sizeof(w8001->name)); + strlcat(basename, " 2FG", basename_sz); if (w8001->max_pen_x && w8001->max_pen_y) w8001->id = 0xE3; else @@ -509,11 +525,27 @@ static int w8001_setup_touch(struct w8001 *w8001) break; } - strlcat(w8001->name, " Touchscreen", sizeof(w8001->name)); + strlcat(basename, " Touchscreen", basename_sz); return 0; } +static void w8001_set_devdata(struct input_dev *dev, struct w8001 *w8001, + struct serio *serio) +{ + dev->phys = w8001->phys; + dev->id.bustype = BUS_RS232; + dev->id.product = w8001->id; + dev->id.vendor = 0x056a; + dev->id.version = 0x0100; + dev->open = w8001_open; + dev->close = w8001_close; + + dev->dev.parent = &serio->dev; + + input_set_drvdata(dev, w8001); +} + /* * w8001_disconnect() is the opposite of w8001_connect() */ @@ -524,7 +556,10 @@ static void w8001_disconnect(struct serio *serio) serio_close(serio); - input_unregister_device(w8001->dev); + if (w8001->pen_dev) + input_unregister_device(w8001->pen_dev); + if (w8001->touch_dev) + input_unregister_device(w8001->touch_dev); kfree(w8001); serio_set_drvdata(serio, NULL); @@ -539,18 +574,23 @@ static void w8001_disconnect(struct serio *serio) static int w8001_connect(struct serio *serio, struct serio_driver *drv) { struct w8001 *w8001; - struct input_dev *input_dev; + struct input_dev *input_dev_pen; + struct input_dev *input_dev_touch; + char basename[64]; int err, err_pen, err_touch; w8001 = kzalloc(sizeof(struct w8001), GFP_KERNEL); - input_dev = input_allocate_device(); - if (!w8001 || !input_dev) { + input_dev_pen = input_allocate_device(); + input_dev_touch = input_allocate_device(); + if (!w8001 || !input_dev_pen || !input_dev_touch) { err = -ENOMEM; goto fail1; } w8001->serio = serio; - w8001->dev = input_dev; + w8001->pen_dev = input_dev_pen; + w8001->touch_dev = input_dev_touch; + mutex_init(&w8001->mutex); init_completion(&w8001->cmd_done); snprintf(w8001->phys, sizeof(w8001->phys), "%s/input0", serio->phys); @@ -563,38 +603,63 @@ static int w8001_connect(struct serio *serio, struct serio_driver *drv) if (err) goto fail3; - err_pen = w8001_setup_pen(w8001); - err_touch = w8001_setup_touch(w8001); + /* For backwards-compatibility we compose the basename based on + * capabilities and then just append the tool type + */ + strlcpy(basename, "Wacom Serial", sizeof(basename)); + + err_pen = w8001_setup_pen(w8001, basename, sizeof(basename)); + err_touch = w8001_setup_touch(w8001, basename, sizeof(basename)); if (err_pen && err_touch) { err = -ENXIO; goto fail3; } - input_dev->name = w8001->name; - input_dev->phys = w8001->phys; - input_dev->id.product = w8001->id; - input_dev->id.bustype = BUS_RS232; - input_dev->id.vendor = 0x056a; - input_dev->id.version = 0x0100; - input_dev->dev.parent = &serio->dev; + if (!err_pen) { + strlcpy(w8001->pen_name, basename, sizeof(w8001->pen_name)); + strlcat(w8001->pen_name, " Pen", sizeof(w8001->pen_name)); + input_dev_pen->name = w8001->pen_name; - input_dev->open = w8001_open; - input_dev->close = w8001_close; + w8001_set_devdata(input_dev_pen, w8001, serio); - input_set_drvdata(input_dev, w8001); + err = input_register_device(w8001->pen_dev); + if (err) + goto fail3; + } else { + input_free_device(input_dev_pen); + input_dev_pen = NULL; + w8001->pen_dev = NULL; + } - err = input_register_device(w8001->dev); - if (err) - goto fail3; + if (!err_touch) { + strlcpy(w8001->touch_name, basename, sizeof(w8001->touch_name)); + strlcat(w8001->touch_name, " Finger", + sizeof(w8001->touch_name)); + input_dev_touch->name = w8001->touch_name; + + w8001_set_devdata(input_dev_touch, w8001, serio); + + err = input_register_device(w8001->touch_dev); + if (err) + goto fail4; + } else { + input_free_device(input_dev_touch); + input_dev_touch = NULL; + w8001->touch_dev = NULL; + } return 0; +fail4: + if (w8001->pen_dev) + input_unregister_device(w8001->pen_dev); fail3: serio_close(serio); fail2: serio_set_drvdata(serio, NULL); fail1: - input_free_device(input_dev); + input_free_device(input_dev_pen); + input_free_device(input_dev_touch); kfree(w8001); return err; } -- cgit v1.1 From bcf5b3deb0d269a3a28ecf4c73e1c89121ba61d4 Mon Sep 17 00:00:00 2001 From: Sander Vermin Date: Tue, 1 Dec 2015 13:25:05 -0800 Subject: Input: pixcir_i2c - add support for wake and enable gpios On some devices the wake and enable pins of the pixcir touchscreen controller are connected to gpios and these must be controlled by the driver for the device to operate properly. Signed-off-by: Sander Vermin Signed-off-by: Hans de Goede Acked-by: Rob Herring Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/pixcir_i2c_ts.c | 41 +++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/pixcir_i2c_ts.c b/drivers/input/touchscreen/pixcir_i2c_ts.c index 4b961ad..09523a3 100644 --- a/drivers/input/touchscreen/pixcir_i2c_ts.c +++ b/drivers/input/touchscreen/pixcir_i2c_ts.c @@ -38,6 +38,8 @@ struct pixcir_i2c_ts_data { struct input_dev *input; struct gpio_desc *gpio_attb; struct gpio_desc *gpio_reset; + struct gpio_desc *gpio_enable; + struct gpio_desc *gpio_wake; const struct pixcir_i2c_chip_data *chip; int max_fingers; /* Max fingers supported in this instance */ bool running; @@ -208,6 +210,11 @@ static int pixcir_set_power_mode(struct pixcir_i2c_ts_data *ts, struct device *dev = &ts->client->dev; int ret; + if (mode == PIXCIR_POWER_ACTIVE || mode == PIXCIR_POWER_IDLE) { + if (ts->gpio_wake) + gpiod_set_value_cansleep(ts->gpio_wake, 1); + } + ret = i2c_smbus_read_byte_data(ts->client, PIXCIR_REG_POWER_MODE); if (ret < 0) { dev_err(dev, "%s: can't read reg 0x%x : %d\n", @@ -228,6 +235,11 @@ static int pixcir_set_power_mode(struct pixcir_i2c_ts_data *ts, return ret; } + if (mode == PIXCIR_POWER_HALT) { + if (ts->gpio_wake) + gpiod_set_value_cansleep(ts->gpio_wake, 0); + } + return 0; } @@ -302,6 +314,11 @@ static int pixcir_start(struct pixcir_i2c_ts_data *ts) struct device *dev = &ts->client->dev; int error; + if (ts->gpio_enable) { + gpiod_set_value_cansleep(ts->gpio_enable, 1); + msleep(100); + } + /* LEVEL_TOUCH interrupt with active low polarity */ error = pixcir_set_int_mode(ts, PIXCIR_INT_LEVEL_TOUCH, 0); if (error) { @@ -343,6 +360,9 @@ static int pixcir_stop(struct pixcir_i2c_ts_data *ts) /* Wait till running ISR is complete */ synchronize_irq(ts->client->irq); + if (ts->gpio_enable) + gpiod_set_value_cansleep(ts->gpio_enable, 0); + return 0; } @@ -534,6 +554,27 @@ static int pixcir_i2c_ts_probe(struct i2c_client *client, return error; } + tsdata->gpio_wake = devm_gpiod_get_optional(dev, "wake", + GPIOD_OUT_HIGH); + if (IS_ERR(tsdata->gpio_wake)) { + error = PTR_ERR(tsdata->gpio_wake); + if (error != -EPROBE_DEFER) + dev_err(dev, "Failed to get wake gpio: %d\n", error); + return error; + } + + tsdata->gpio_enable = devm_gpiod_get_optional(dev, "enable", + GPIOD_OUT_HIGH); + if (IS_ERR(tsdata->gpio_enable)) { + error = PTR_ERR(tsdata->gpio_enable); + if (error != -EPROBE_DEFER) + dev_err(dev, "Failed to get enable gpio: %d\n", error); + return error; + } + + if (tsdata->gpio_enable) + msleep(100); + error = devm_request_threaded_irq(dev, client->irq, NULL, pixcir_ts_isr, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, client->name, tsdata); -- cgit v1.1 From f889beaaab1ce2ff9d018302359abb345f49be29 Mon Sep 17 00:00:00 2001 From: Steve Twiss Date: Sat, 12 Dec 2015 20:43:35 -0800 Subject: Input: da9063 - report KEY_POWER instead of KEY_SLEEP during power key-press Stop reporting KEY_SLEEP for a short key-press and report KEY_POWER instead This change applies to both DA9063 and DA9062 ONKEY drivers. A previous application used for testing by the developer required a KEY_SLEEP and KEY_POWER input_report_key event to distinguish between a short and long key-press of the power key. This is not the general convention and the typical solution is for KEY_POWER to be used in both cases: suspend and S/W power off. Signed-off-by: Steve Twiss Signed-off-by: Dmitry Torokhov --- drivers/input/misc/da9063_onkey.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/misc/da9063_onkey.c b/drivers/input/misc/da9063_onkey.c index 8eb697d..bb863e0 100644 --- a/drivers/input/misc/da9063_onkey.c +++ b/drivers/input/misc/da9063_onkey.c @@ -179,13 +179,13 @@ static irqreturn_t da9063_onkey_irq_handler(int irq, void *data) input_report_key(onkey->input, KEY_POWER, 1); input_sync(onkey->input); schedule_delayed_work(&onkey->work, 0); - dev_dbg(onkey->dev, "KEY_POWER pressed.\n"); + dev_dbg(onkey->dev, "KEY_POWER long press.\n"); } else { - input_report_key(onkey->input, KEY_SLEEP, 1); + input_report_key(onkey->input, KEY_POWER, 1); input_sync(onkey->input); - input_report_key(onkey->input, KEY_SLEEP, 0); + input_report_key(onkey->input, KEY_POWER, 0); input_sync(onkey->input); - dev_dbg(onkey->dev, "KEY_SLEEP pressed.\n"); + dev_dbg(onkey->dev, "KEY_POWER short press.\n"); } return IRQ_HANDLED; -- cgit v1.1 From 6b0f8f9c52efe24d6dac06ab963b7bd91c723751 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=B6sz=C3=B6rm=C3=A9nyi=20Zolt=C3=A1n?= Date: Wed, 16 Dec 2015 11:11:50 -0800 Subject: Input: add eGalaxTouch serial touchscreen driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are two EETI touchscreen drivers in the kernel (eeti_ts and egalax_ts) but both are for I2C-connected panels. This is for a different, serial and not multi-touch touchscreen panel. The protocol documentation is at http://www.eeti.com.tw/pdf/Software%20Programming%20Guide_v2.0.pdf Signed-off-by: Böszörményi Zoltán Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/Kconfig | 10 ++ drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/egalax_ts_serial.c | 194 +++++++++++++++++++++++++++ 3 files changed, 205 insertions(+) create mode 100644 drivers/input/touchscreen/egalax_ts_serial.c (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index ae33da7..9bcb718 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -295,6 +295,16 @@ config TOUCHSCREEN_EGALAX To compile this driver as a module, choose M here: the module will be called egalax_ts. +config TOUCHSCREEN_EGALAX_SERIAL + tristate "EETI eGalax serial touchscreen" + select SERIO + help + Say Y here to enable support for serial connected EETI + eGalax touch panels. + + To compile this driver as a module, choose M here: the + module will be called egalax_ts_serial. + config TOUCHSCREEN_FT6236 tristate "FT6236 I2C touchscreen" depends on I2C diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index cbaa6ab..001357c 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -35,6 +35,7 @@ obj-$(CONFIG_TOUCHSCREEN_EETI) += eeti_ts.o obj-$(CONFIG_TOUCHSCREEN_ELAN) += elants_i2c.o obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o obj-$(CONFIG_TOUCHSCREEN_EGALAX) += egalax_ts.o +obj-$(CONFIG_TOUCHSCREEN_EGALAX_SERIAL) += egalax_ts_serial.o obj-$(CONFIG_TOUCHSCREEN_FT6236) += ft6236.o obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o obj-$(CONFIG_TOUCHSCREEN_GOODIX) += goodix.o diff --git a/drivers/input/touchscreen/egalax_ts_serial.c b/drivers/input/touchscreen/egalax_ts_serial.c new file mode 100644 index 0000000..a078c1c --- /dev/null +++ b/drivers/input/touchscreen/egalax_ts_serial.c @@ -0,0 +1,194 @@ +/* + * EETI Egalax serial touchscreen driver + * + * Copyright (c) 2015 Zoltán Böszörményi + * + * based on the + * + * Hampshire serial touchscreen driver (Copyright (c) 2010 Adam Bennett) + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +#define DRIVER_DESC "EETI Egalax serial touchscreen driver" + +/* + * Definitions & global arrays. + */ + +#define EGALAX_FORMAT_MAX_LENGTH 6 +#define EGALAX_FORMAT_START_BIT BIT(7) +#define EGALAX_FORMAT_PRESSURE_BIT BIT(6) +#define EGALAX_FORMAT_TOUCH_BIT BIT(0) +#define EGALAX_FORMAT_RESOLUTION_MASK 0x06 + +#define EGALAX_MIN_XC 0 +#define EGALAX_MAX_XC 0x4000 +#define EGALAX_MIN_YC 0 +#define EGALAX_MAX_YC 0x4000 + +/* + * Per-touchscreen data. + */ +struct egalax { + struct input_dev *input; + struct serio *serio; + int idx; + u8 data[EGALAX_FORMAT_MAX_LENGTH]; + char phys[32]; +}; + +static void egalax_process_data(struct egalax *egalax) +{ + struct input_dev *dev = egalax->input; + u8 *data = egalax->data; + u16 x, y; + u8 shift; + u8 mask; + + shift = 3 - ((data[0] & EGALAX_FORMAT_RESOLUTION_MASK) >> 1); + mask = 0xff >> (shift + 1); + + x = (((u16)(data[1] & mask) << 7) | (data[2] & 0x7f)) << shift; + y = (((u16)(data[3] & mask) << 7) | (data[4] & 0x7f)) << shift; + + input_report_key(dev, BTN_TOUCH, data[0] & EGALAX_FORMAT_TOUCH_BIT); + input_report_abs(dev, ABS_X, x); + input_report_abs(dev, ABS_Y, y); + input_sync(dev); +} + +static irqreturn_t egalax_interrupt(struct serio *serio, + unsigned char data, unsigned int flags) +{ + struct egalax *egalax = serio_get_drvdata(serio); + int pkt_len; + + egalax->data[egalax->idx++] = data; + + if (likely(egalax->data[0] & EGALAX_FORMAT_START_BIT)) { + pkt_len = egalax->data[0] & EGALAX_FORMAT_PRESSURE_BIT ? 6 : 5; + if (pkt_len == egalax->idx) { + egalax_process_data(egalax); + egalax->idx = 0; + } + } else { + dev_dbg(&serio->dev, "unknown/unsynchronized data: %x\n", + egalax->data[0]); + egalax->idx = 0; + } + + return IRQ_HANDLED; +} + +/* + * egalax_connect() is the routine that is called when someone adds a + * new serio device that supports egalax protocol and registers it as + * an input device. This is usually accomplished using inputattach. + */ +static int egalax_connect(struct serio *serio, struct serio_driver *drv) +{ + struct egalax *egalax; + struct input_dev *input_dev; + int error; + + egalax = kzalloc(sizeof(struct egalax), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!egalax) { + error = -ENOMEM; + goto err_free_mem; + } + + egalax->serio = serio; + egalax->input = input_dev; + snprintf(egalax->phys, sizeof(egalax->phys), + "%s/input0", serio->phys); + + input_dev->name = "EETI eGalaxTouch Serial TouchScreen"; + input_dev->phys = egalax->phys; + input_dev->id.bustype = BUS_RS232; + input_dev->id.vendor = SERIO_EGALAX; + input_dev->id.product = 0; + input_dev->id.version = 0x0001; + input_dev->dev.parent = &serio->dev; + + input_set_capability(input_dev, EV_KEY, BTN_TOUCH); + input_set_abs_params(input_dev, ABS_X, + EGALAX_MIN_XC, EGALAX_MAX_XC, 0, 0); + input_set_abs_params(input_dev, ABS_Y, + EGALAX_MIN_YC, EGALAX_MAX_YC, 0, 0); + + serio_set_drvdata(serio, egalax); + + error = serio_open(serio, drv); + if (error) + goto err_reset_drvdata; + + error = input_register_device(input_dev); + if (error) + goto err_close_serio; + + return 0; + +err_close_serio: + serio_close(serio); +err_reset_drvdata: + serio_set_drvdata(serio, NULL); +err_free_mem: + input_free_device(input_dev); + kfree(egalax); + return error; +} + +static void egalax_disconnect(struct serio *serio) +{ + struct egalax *egalax = serio_get_drvdata(serio); + + serio_close(serio); + serio_set_drvdata(serio, NULL); + input_unregister_device(egalax->input); + kfree(egalax); +} + +/* + * The serio driver structure. + */ + +static const struct serio_device_id egalax_serio_ids[] = { + { + .type = SERIO_RS232, + .proto = SERIO_EGALAX, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { 0 } +}; + +MODULE_DEVICE_TABLE(serio, egalax_serio_ids); + +static struct serio_driver egalax_drv = { + .driver = { + .name = "egalax", + }, + .description = DRIVER_DESC, + .id_table = egalax_serio_ids, + .interrupt = egalax_interrupt, + .connect = egalax_connect, + .disconnect = egalax_disconnect, +}; +module_serio_driver(egalax_drv); + +MODULE_AUTHOR("Zoltán Böszörményi "); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL v2"); -- cgit v1.1 From 40e3be3933aee185fd6ab1ec87dfaf3502d9f5b3 Mon Sep 17 00:00:00 2001 From: Damien Riegel Date: Wed, 16 Dec 2015 11:49:14 -0800 Subject: Input: add touchscreen support for TS-4800 On this board, the touchscreen, an ads7843, is not handled directly by Linux but by a companion FPGA. This FPGA is memory-mapped and the IP design is very similar to the mk712. This commit adds the support for this IP. Signed-off-by: Damien Riegel Acked-by: Rob Herring Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/Kconfig | 16 +++ drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/ts4800-ts.c | 216 ++++++++++++++++++++++++++++++++++ 3 files changed, 233 insertions(+) create mode 100644 drivers/input/touchscreen/ts4800-ts.c (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 9bcb718..f986e8b 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -937,6 +937,22 @@ config TOUCHSCREEN_TOUCHIT213 To compile this driver as a module, choose M here: the module will be called touchit213. +config TOUCHSCREEN_TS4800 + tristate "TS-4800 touchscreen" + depends on HAS_IOMEM && OF + select MFD_SYSCON + select INPUT_POLLDEV + help + Say Y here if you have a touchscreen on a TS-4800 board. + + On TS-4800, the touchscreen is not handled directly by Linux but by + a companion FPGA. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called ts4800_ts. + config TOUCHSCREEN_TSC_SERIO tristate "TSC-10/25/40 serial touchscreen support" select SERIO diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 001357c..968ff12 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -69,6 +69,7 @@ obj-$(CONFIG_TOUCHSCREEN_TI_AM335X_TSC) += ti_am335x_tsc.o obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN) += touchwin.o +obj-$(CONFIG_TOUCHSCREEN_TS4800) += ts4800-ts.o obj-$(CONFIG_TOUCHSCREEN_TSC_SERIO) += tsc40.o obj-$(CONFIG_TOUCHSCREEN_TSC200X_CORE) += tsc200x-core.o obj-$(CONFIG_TOUCHSCREEN_TSC2004) += tsc2004.o diff --git a/drivers/input/touchscreen/ts4800-ts.c b/drivers/input/touchscreen/ts4800-ts.c new file mode 100644 index 0000000..3c3dd78 --- /dev/null +++ b/drivers/input/touchscreen/ts4800-ts.c @@ -0,0 +1,216 @@ +/* + * Touchscreen driver for the TS-4800 board + * + * Copyright (c) 2015 - Savoir-faire Linux + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* polling interval in ms */ +#define POLL_INTERVAL 3 + +#define DEBOUNCE_COUNT 1 + +/* sensor values are 12-bit wide */ +#define MAX_12BIT ((1 << 12) - 1) + +#define PENDOWN_MASK 0x1 + +#define X_OFFSET 0x0 +#define Y_OFFSET 0x2 + +struct ts4800_ts { + struct input_polled_dev *poll_dev; + struct device *dev; + char phys[32]; + + void __iomem *base; + struct regmap *regmap; + unsigned int reg; + unsigned int bit; + + bool pendown; + int debounce; +}; + +static void ts4800_ts_open(struct input_polled_dev *dev) +{ + struct ts4800_ts *ts = dev->private; + int ret; + + ts->pendown = false; + ts->debounce = DEBOUNCE_COUNT; + + ret = regmap_update_bits(ts->regmap, ts->reg, ts->bit, ts->bit); + if (ret) + dev_warn(ts->dev, "Failed to enable touchscreen\n"); +} + +static void ts4800_ts_close(struct input_polled_dev *dev) +{ + struct ts4800_ts *ts = dev->private; + int ret; + + ret = regmap_update_bits(ts->regmap, ts->reg, ts->bit, 0); + if (ret) + dev_warn(ts->dev, "Failed to disable touchscreen\n"); + +} + +static void ts4800_ts_poll(struct input_polled_dev *dev) +{ + struct input_dev *input_dev = dev->input; + struct ts4800_ts *ts = dev->private; + u16 last_x = readw(ts->base + X_OFFSET); + u16 last_y = readw(ts->base + Y_OFFSET); + bool pendown = last_x & PENDOWN_MASK; + + if (pendown) { + if (ts->debounce) { + ts->debounce--; + return; + } + + if (!ts->pendown) { + input_report_key(input_dev, BTN_TOUCH, 1); + ts->pendown = true; + } + + last_x = ((~last_x) >> 4) & MAX_12BIT; + last_y = ((~last_y) >> 4) & MAX_12BIT; + + input_report_abs(input_dev, ABS_X, last_x); + input_report_abs(input_dev, ABS_Y, last_y); + input_sync(input_dev); + } else if (ts->pendown) { + ts->pendown = false; + ts->debounce = DEBOUNCE_COUNT; + input_report_key(input_dev, BTN_TOUCH, 0); + input_sync(input_dev); + } +} + +static int ts4800_parse_dt(struct platform_device *pdev, + struct ts4800_ts *ts) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct device_node *syscon_np; + u32 reg, bit; + int error; + + syscon_np = of_parse_phandle(np, "syscon", 0); + if (!syscon_np) { + dev_err(dev, "no syscon property\n"); + return -ENODEV; + } + + error = of_property_read_u32_index(np, "syscon", 1, ®); + if (error < 0) { + dev_err(dev, "no offset in syscon\n"); + return error; + } + + ts->reg = reg; + + error = of_property_read_u32_index(np, "syscon", 2, &bit); + if (error < 0) { + dev_err(dev, "no bit in syscon\n"); + return error; + } + + ts->bit = BIT(bit); + + ts->regmap = syscon_node_to_regmap(syscon_np); + if (IS_ERR(ts->regmap)) { + dev_err(dev, "cannot get parent's regmap\n"); + return PTR_ERR(ts->regmap); + } + + return 0; +} + +static int ts4800_ts_probe(struct platform_device *pdev) +{ + struct input_polled_dev *poll_dev; + struct ts4800_ts *ts; + struct resource *res; + int error; + + ts = devm_kzalloc(&pdev->dev, sizeof(*ts), GFP_KERNEL); + if (!ts) + return -ENOMEM; + + error = ts4800_parse_dt(pdev, ts); + if (error) + return error; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + ts->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(ts->base)) + return PTR_ERR(ts->base); + + poll_dev = devm_input_allocate_polled_device(&pdev->dev); + if (!poll_dev) + return -ENOMEM; + + snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(&pdev->dev)); + ts->poll_dev = poll_dev; + ts->dev = &pdev->dev; + + poll_dev->private = ts; + poll_dev->poll_interval = POLL_INTERVAL; + poll_dev->open = ts4800_ts_open; + poll_dev->close = ts4800_ts_close; + poll_dev->poll = ts4800_ts_poll; + + poll_dev->input->name = "TS-4800 Touchscreen"; + poll_dev->input->phys = ts->phys; + + input_set_capability(poll_dev->input, EV_KEY, BTN_TOUCH); + input_set_abs_params(poll_dev->input, ABS_X, 0, MAX_12BIT, 0, 0); + input_set_abs_params(poll_dev->input, ABS_Y, 0, MAX_12BIT, 0, 0); + + error = input_register_polled_device(poll_dev); + if (error) { + dev_err(&pdev->dev, + "Unabled to register polled input device (%d)\n", + error); + return error; + } + + return 0; +} + +static const struct of_device_id ts4800_ts_of_match[] = { + { .compatible = "technologic,ts4800-ts", }, + { }, +}; +MODULE_DEVICE_TABLE(of, ts4800_ts_of_match); + +static struct platform_driver ts4800_ts_driver = { + .driver = { + .name = "ts4800-ts", + .of_match_table = ts4800_ts_of_match, + }, + .probe = ts4800_ts_probe, +}; +module_platform_driver(ts4800_ts_driver); + +MODULE_AUTHOR("Damien Riegel "); +MODULE_DESCRIPTION("TS-4800 Touchscreen Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:ts4800_ts"); -- cgit v1.1 From 0a88d60784440da1ef8f72156cb9547aeebf56cf Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 27 Nov 2015 20:29:22 -0800 Subject: Input: psmouse - use switch statement in psmouse_process_byte() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of a series mostly exclusive "if" statements testing protocol type of the mouse let's use "switch" statement. Reviewed-by: Hans de Goede Reviewed-by: Pali Rohár Tested-by: Marcin Sochacki Tested-by: Till Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/psmouse-base.c | 65 ++++++++++++++++++-------------------- 1 file changed, 31 insertions(+), 34 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index ad18dab..4e43e7e 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -138,22 +138,16 @@ psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse) if (psmouse->pktcnt < psmouse->pktsize) return PSMOUSE_GOOD_DATA; -/* - * Full packet accumulated, process it - */ - -/* - * Scroll wheel on IntelliMice, scroll buttons on NetMice - */ + /* Full packet accumulated, process it */ - if (psmouse->type == PSMOUSE_IMPS || psmouse->type == PSMOUSE_GENPS) + switch (psmouse->type) { + case PSMOUSE_IMPS: + /* IntelliMouse has scroll wheel */ input_report_rel(dev, REL_WHEEL, -(signed char) packet[3]); + break; -/* - * Scroll wheel and buttons on IntelliMouse Explorer - */ - - if (psmouse->type == PSMOUSE_IMEX) { + case PSMOUSE_IMEX: + /* Scroll wheel and buttons on IntelliMouse Explorer */ switch (packet[3] & 0xC0) { case 0x80: /* vertical scroll on IntelliMouse Explorer 4.0 */ input_report_rel(dev, REL_WHEEL, (int) (packet[3] & 32) - (int) (packet[3] & 31)); @@ -168,39 +162,42 @@ psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse) input_report_key(dev, BTN_EXTRA, (packet[3] >> 5) & 1); break; } - } + break; -/* - * Extra buttons on Genius NewNet 3D - */ + case PSMOUSE_GENPS: + /* Report scroll buttons on NetMice */ + input_report_rel(dev, REL_WHEEL, -(signed char) packet[3]); - if (psmouse->type == PSMOUSE_GENPS) { + /* Extra buttons on Genius NewNet 3D */ input_report_key(dev, BTN_SIDE, (packet[0] >> 6) & 1); input_report_key(dev, BTN_EXTRA, (packet[0] >> 7) & 1); - } + break; -/* - * Extra button on ThinkingMouse - */ - if (psmouse->type == PSMOUSE_THINKPS) { + case PSMOUSE_THINKPS: + /* Extra button on ThinkingMouse */ input_report_key(dev, BTN_EXTRA, (packet[0] >> 3) & 1); - /* Without this bit of weirdness moving up gives wildly high Y changes. */ + + /* + * Without this bit of weirdness moving up gives wildly + * high Y changes. + */ packet[1] |= (packet[0] & 0x40) << 1; - } + break; -/* - * Cortron PS2 Trackball reports SIDE button on the 4th bit of the first - * byte. - */ - if (psmouse->type == PSMOUSE_CORTRON) { + case PSMOUSE_CORTRON: + /* + * Cortron PS2 Trackball reports SIDE button in the + * 4th bit of the first byte. + */ input_report_key(dev, BTN_SIDE, (packet[0] >> 3) & 1); packet[0] |= 0x08; - } + break; -/* - * Generic PS/2 Mouse - */ + default: + break; + } + /* Generic PS/2 Mouse */ input_report_key(dev, BTN_LEFT, packet[0] & 1); input_report_key(dev, BTN_MIDDLE, (packet[0] >> 2) & 1); input_report_key(dev, BTN_RIGHT, (packet[0] >> 1) & 1); -- cgit v1.1 From ad5307715b2d0afed511b2cad9aed530bbbb236b Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 27 Nov 2015 20:47:08 -0800 Subject: Input: psmouse - fix comment style MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The module was using non-standard comment style with comment blocks often starting at the very beginning of a line instead of being aligned with the code. Let's switch to standard formatting. Reviewed-by: Hans de Goede Reviewed-by: Pali Rohár Tested-by: Marcin Sochacki Tested-by: Till Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/psmouse-base.c | 279 +++++++++++++++++-------------------- 1 file changed, 124 insertions(+), 155 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index 4e43e7e..525de2e 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -129,7 +129,6 @@ struct psmouse_protocol { * psmouse_process_byte() analyzes the PS/2 data stream and reports * relevant events to the input module once full packet has arrived. */ - psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse) { struct input_dev *dev = psmouse->dev; @@ -219,7 +218,6 @@ void psmouse_queue_work(struct psmouse *psmouse, struct delayed_work *work, /* * __psmouse_set_state() sets new psmouse state and resets all flags. */ - static inline void __psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state) { psmouse->state = new_state; @@ -228,13 +226,11 @@ static inline void __psmouse_set_state(struct psmouse *psmouse, enum psmouse_sta psmouse->last = jiffies; } - /* * psmouse_set_state() sets new psmouse state and resets all flags and * counters while holding serio lock so fighting with interrupt handler * is not a concern. */ - void psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state) { serio_pause_rx(psmouse->ps2dev.serio); @@ -246,7 +242,6 @@ void psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state) * psmouse_handle_byte() processes one byte of the input data stream * by calling corresponding protocol handler. */ - static int psmouse_handle_byte(struct psmouse *psmouse) { psmouse_ret_t rc = psmouse->protocol_handler(psmouse); @@ -289,7 +284,6 @@ static int psmouse_handle_byte(struct psmouse *psmouse) * psmouse_interrupt() handles incoming characters, either passing them * for normal processing or gathering them as command response. */ - static irqreturn_t psmouse_interrupt(struct serio *serio, unsigned char data, unsigned int flags) { @@ -332,9 +326,8 @@ static irqreturn_t psmouse_interrupt(struct serio *serio, } psmouse->packet[psmouse->pktcnt++] = data; -/* - * Check if this is a new device announcement (0xAA 0x00) - */ + + /* Check if this is a new device announcement (0xAA 0x00) */ if (unlikely(psmouse->packet[0] == PSMOUSE_RET_BAT && psmouse->pktcnt <= 2)) { if (psmouse->pktcnt == 1) { psmouse->last = jiffies; @@ -348,9 +341,8 @@ static irqreturn_t psmouse_interrupt(struct serio *serio, serio_reconnect(serio); goto out; } -/* - * Not a new device, try processing first byte normally - */ + + /* Not a new device, try processing first byte normally */ psmouse->pktcnt = 1; if (psmouse_handle_byte(psmouse)) goto out; @@ -358,9 +350,10 @@ static irqreturn_t psmouse_interrupt(struct serio *serio, psmouse->packet[psmouse->pktcnt++] = data; } -/* - * See if we need to force resync because mouse was idle for too long - */ + /* + * See if we need to force resync because mouse was idle for + * too long. + */ if (psmouse->state == PSMOUSE_ACTIVATED && psmouse->pktcnt == 1 && psmouse->resync_time && time_after(jiffies, psmouse->last + psmouse->resync_time * HZ)) { @@ -377,7 +370,6 @@ static irqreturn_t psmouse_interrupt(struct serio *serio, return IRQ_HANDLED; } - /* * psmouse_sliced_command() sends an extended PS/2 command to the mouse * using sliced syntax, understood by advanced devices, such as Logitech @@ -401,7 +393,6 @@ int psmouse_sliced_command(struct psmouse *psmouse, unsigned char command) return 0; } - /* * psmouse_reset() resets the mouse into power-on state. */ @@ -421,7 +412,6 @@ int psmouse_reset(struct psmouse *psmouse) /* * Here we set the mouse resolution. */ - void psmouse_set_resolution(struct psmouse *psmouse, unsigned int resolution) { static const unsigned char params[] = { 0, 1, 2, 2, 3 }; @@ -438,7 +428,6 @@ void psmouse_set_resolution(struct psmouse *psmouse, unsigned int resolution) /* * Here we set the mouse report rate. */ - static void psmouse_set_rate(struct psmouse *psmouse, unsigned int rate) { static const unsigned char rates[] = { 200, 100, 80, 60, 40, 20, 10, 0 }; @@ -454,7 +443,6 @@ static void psmouse_set_rate(struct psmouse *psmouse, unsigned int rate) /* * Here we set the mouse scaling. */ - static void psmouse_set_scale(struct psmouse *psmouse, enum psmouse_scale scale) { ps2_command(&psmouse->ps2dev, NULL, @@ -465,7 +453,6 @@ static void psmouse_set_scale(struct psmouse *psmouse, enum psmouse_scale scale) /* * psmouse_poll() - default poll handler. Everyone except for ALPS uses it. */ - static int psmouse_poll(struct psmouse *psmouse) { return ps2_command(&psmouse->ps2dev, psmouse->packet, @@ -599,7 +586,7 @@ static int im_explorer_detect(struct psmouse *psmouse, bool set_properties) if (param[0] != 4) return -1; -/* Magic to enable horizontal scrolling on IntelliMouse 4.0 */ + /* Magic to enable horizontal scrolling on IntelliMouse 4.0 */ param[0] = 200; ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE); param[0] = 80; @@ -669,10 +656,10 @@ static int ps2bare_detect(struct psmouse *psmouse, bool set_properties) if (!psmouse->name) psmouse->name = "Mouse"; -/* - * We have no way of figuring true number of buttons so let's - * assume that the device has 3. - */ + /* + * We have no way of figuring true number of buttons so let's + * assume that the device has 3. + */ __set_bit(BTN_MIDDLE, psmouse->dev->keybit); } @@ -700,7 +687,6 @@ static int cortron_detect(struct psmouse *psmouse, bool set_properties) * Apply default settings to the psmouse structure. Most of them will * be overridden by individual protocol initialization routines. */ - static void psmouse_apply_defaults(struct psmouse *psmouse) { struct input_dev *input_dev = psmouse->dev; @@ -753,13 +739,15 @@ static int psmouse_do_detect(int (*detect)(struct psmouse *psmouse, * psmouse_extensions() probes for any extensions to the basic PS/2 protocol * the mouse may have. */ - static int psmouse_extensions(struct psmouse *psmouse, unsigned int max_proto, bool set_properties) { bool synaptics_hardware = false; -/* Always check for focaltech, this is safe as it uses pnp-id matching */ + /* + * Always check for focaltech, this is safe as it uses pnp-id + * matching. + */ if (psmouse_do_detect(focaltech_detect, psmouse, set_properties) == 0) { if (max_proto > PSMOUSE_IMEX) { if (!set_properties || focaltech_init(psmouse) == 0) { @@ -777,10 +765,10 @@ static int psmouse_extensions(struct psmouse *psmouse, } } -/* - * We always check for lifebook because it does not disturb mouse - * (it only checks DMI information). - */ + /* + * We always check for LifeBook because it does not disturb mouse + * (it only checks DMI information). + */ if (psmouse_do_detect(lifebook_detect, psmouse, set_properties) == 0) { if (max_proto > PSMOUSE_IMEX) { if (!set_properties || lifebook_init(psmouse) == 0) @@ -795,53 +783,57 @@ static int psmouse_extensions(struct psmouse *psmouse, } } -/* - * Try Kensington ThinkingMouse (we try first, because synaptics probe - * upsets the thinkingmouse). - */ - + /* + * Try Kensington ThinkingMouse (we try first, because Synaptics + * probe upsets the ThinkingMouse). + */ if (max_proto > PSMOUSE_IMEX && psmouse_do_detect(thinking_detect, psmouse, set_properties) == 0) { return PSMOUSE_THINKPS; } -/* - * Try Synaptics TouchPad. Note that probing is done even if Synaptics protocol - * support is disabled in config - we need to know if it is synaptics so we - * can reset it properly after probing for intellimouse. - */ + /* + * Try Synaptics TouchPad. Note that probing is done even if + * Synaptics protocol support is disabled in config - we need to + * know if it is Synaptics so we can reset it properly after + * probing for IntelliMouse. + */ if (max_proto > PSMOUSE_PS2 && psmouse_do_detect(synaptics_detect, psmouse, set_properties) == 0) { synaptics_hardware = true; if (max_proto > PSMOUSE_IMEX) { -/* - * Try activating protocol, but check if support is enabled first, since - * we try detecting Synaptics even when protocol is disabled. - */ + /* + * Try activating protocol, but check if support is + * enabled first, since we try detecting Synaptics + * even when protocol is disabled. + */ if (IS_ENABLED(CONFIG_MOUSE_PS2_SYNAPTICS) && (!set_properties || synaptics_init(psmouse) == 0)) { return PSMOUSE_SYNAPTICS; } -/* - * Some Synaptics touchpads can emulate extended protocols (like IMPS/2). - * Unfortunately Logitech/Genius probes confuse some firmware versions so - * we'll have to skip them. - */ + /* + * Some Synaptics touchpads can emulate extended + * protocols (like IMPS/2). Unfortunately + * Logitech/Genius probes confuse some firmware + * versions so we'll have to skip them. + */ max_proto = PSMOUSE_IMEX; } -/* - * Make sure that touchpad is in relative mode, gestures (taps) are enabled - */ + + /* + * Make sure that touchpad is in relative mode, gestures + * (taps) are enabled. + */ synaptics_reset(psmouse); } -/* - * Try Cypress Trackpad. - * Must try it before Finger Sensing Pad because Finger Sensing Pad probe - * upsets some modules of Cypress Trackpads. - */ + /* + * Try Cypress Trackpad. We must try it before Finger Sensing Pad + * because Finger Sensing Pad probe upsets some modules of Cypress + * Trackpads. + */ if (max_proto > PSMOUSE_IMEX && cypress_detect(psmouse, set_properties) == 0) { if (IS_ENABLED(CONFIG_MOUSE_PS2_CYPRESS)) { @@ -859,45 +851,34 @@ static int psmouse_extensions(struct psmouse *psmouse, max_proto = PSMOUSE_IMEX; } -/* - * Try ALPS TouchPad - */ + /* Try ALPS TouchPad */ if (max_proto > PSMOUSE_IMEX) { ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS); if (psmouse_do_detect(alps_detect, psmouse, set_properties) == 0) { if (!set_properties || alps_init(psmouse) == 0) return PSMOUSE_ALPS; -/* - * Init failed, try basic relative protocols - */ + + /* Init failed, try basic relative protocols */ max_proto = PSMOUSE_IMEX; } } -/* - * Try OLPC HGPK touchpad. - */ + /* Try OLPC HGPK touchpad */ if (max_proto > PSMOUSE_IMEX && psmouse_do_detect(hgpk_detect, psmouse, set_properties) == 0) { if (!set_properties || hgpk_init(psmouse) == 0) return PSMOUSE_HGPK; -/* - * Init failed, try basic relative protocols - */ + /* Init failed, try basic relative protocols */ max_proto = PSMOUSE_IMEX; } -/* - * Try Elantech touchpad. - */ + /* Try Elantech touchpad */ if (max_proto > PSMOUSE_IMEX && psmouse_do_detect(elantech_detect, psmouse, set_properties) == 0) { if (!set_properties || elantech_init(psmouse) == 0) return PSMOUSE_ELANTECH; -/* - * Init failed, try basic relative protocols - */ + /* Init failed, try basic relative protocols */ max_proto = PSMOUSE_IMEX; } @@ -919,27 +900,25 @@ static int psmouse_extensions(struct psmouse *psmouse, return PSMOUSE_TOUCHKIT_PS2; } -/* - * Try Finger Sensing Pad. We do it here because its probe upsets - * Trackpoint devices (causing TP_READ_ID command to time out). - */ + /* + * Try Finger Sensing Pad. We do it here because its probe upsets + * Trackpoint devices (causing TP_READ_ID command to time out). + */ if (max_proto > PSMOUSE_IMEX) { if (psmouse_do_detect(fsp_detect, psmouse, set_properties) == 0) { if (!set_properties || fsp_init(psmouse) == 0) return PSMOUSE_FSP; -/* - * Init failed, try basic relative protocols - */ + /* Init failed, try basic relative protocols */ max_proto = PSMOUSE_IMEX; } } -/* - * Reset to defaults in case the device got confused by extended - * protocol probes. Note that we follow up with full reset because - * some mice put themselves to sleep when they see PSMOUSE_RESET_DIS. - */ + /* + * Reset to defaults in case the device got confused by extended + * protocol probes. Note that we follow up with full reset because + * some mice put themselves to sleep when they see PSMOUSE_RESET_DIS. + */ ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS); psmouse_reset(psmouse); @@ -955,19 +934,19 @@ static int psmouse_extensions(struct psmouse *psmouse, return PSMOUSE_IMPS; } -/* - * Okay, all failed, we have a standard mouse here. The number of the buttons - * is still a question, though. We assume 3. - */ + /* + * Okay, all failed, we have a standard mouse here. The number of + * the buttons is still a question, though. We assume 3. + */ psmouse_do_detect(ps2bare_detect, psmouse, set_properties); if (synaptics_hardware) { -/* - * We detected Synaptics hardware but it did not respond to IMPS/2 probes. - * We need to reset the touchpad because if there is a track point on the - * pass through port it could get disabled while probing for protocol - * extensions. - */ + /* + * We detected Synaptics hardware but it did not respond to + * IMPS/2 probes. We need to reset the touchpad because if + * there is a track point on the pass through port it could + * get disabled while probing for protocol extensions. + */ psmouse_reset(psmouse); } @@ -1167,19 +1146,17 @@ static const struct psmouse_protocol *psmouse_protocol_by_name(const char *name, /* * psmouse_probe() probes for a PS/2 mouse. */ - static int psmouse_probe(struct psmouse *psmouse) { struct ps2dev *ps2dev = &psmouse->ps2dev; unsigned char param[2]; -/* - * First, we check if it's a mouse. It should send 0x00 or 0x03 - * in case of an IntelliMouse in 4-byte mode or 0x04 for IM Explorer. - * Sunrex K8561 IR Keyboard/Mouse reports 0xff on second and subsequent - * ID queries, probably due to a firmware bug. - */ - + /* + * First, we check if it's a mouse. It should send 0x00 or 0x03 in + * case of an IntelliMouse in 4-byte mode or 0x04 for IM Explorer. + * Sunrex K8561 IR Keyboard/Mouse reports 0xff on second and + * subsequent ID queries, probably due to a firmware bug. + */ param[0] = 0xa5; if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETID)) return -1; @@ -1188,10 +1165,10 @@ static int psmouse_probe(struct psmouse *psmouse) param[0] != 0x04 && param[0] != 0xff) return -1; -/* - * Then we reset and disable the mouse so that it doesn't generate events. - */ - + /* + * Then we reset and disable the mouse so that it doesn't generate + * events. + */ if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_DIS)) psmouse_warn(psmouse, "Failed to reset mouse on %s\n", ps2dev->serio->phys); @@ -1202,13 +1179,11 @@ static int psmouse_probe(struct psmouse *psmouse) /* * psmouse_initialize() initializes the mouse to a sane state. */ - static void psmouse_initialize(struct psmouse *psmouse) { -/* - * We set the mouse report rate, resolution and scaling. - */ - + /* + * We set the mouse report rate, resolution and scaling. + */ if (psmouse_max_proto != PSMOUSE_PS2) { psmouse->set_rate(psmouse, psmouse->rate); psmouse->set_resolution(psmouse, psmouse->resolution); @@ -1219,7 +1194,6 @@ static void psmouse_initialize(struct psmouse *psmouse) /* * psmouse_activate() enables the mouse so that we get motion reports from it. */ - int psmouse_activate(struct psmouse *psmouse) { if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_ENABLE)) { @@ -1233,10 +1207,9 @@ int psmouse_activate(struct psmouse *psmouse) } /* - * psmouse_deactivate() puts the mouse into poll mode so that we don't get motion - * reports from it unless we explicitly request it. + * psmouse_deactivate() puts the mouse into poll mode so that we don't get + * motion reports from it unless we explicitly request it. */ - int psmouse_deactivate(struct psmouse *psmouse) { if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_DISABLE)) { @@ -1249,11 +1222,9 @@ int psmouse_deactivate(struct psmouse *psmouse) return 0; } - /* * psmouse_resync() attempts to re-validate current protocol. */ - static void psmouse_resync(struct work_struct *work) { struct psmouse *parent = NULL, *psmouse = @@ -1273,16 +1244,16 @@ static void psmouse_resync(struct work_struct *work) psmouse_deactivate(parent); } -/* - * Some mice don't ACK commands sent while they are in the middle of - * transmitting motion packet. To avoid delay we use ps2_sendbyte() - * instead of ps2_command() which would wait for 200ms for an ACK - * that may never come. - * As an additional quirk ALPS touchpads may not only forget to ACK - * disable command but will stop reporting taps, so if we see that - * mouse at least once ACKs disable we will do full reconnect if ACK - * is missing. - */ + /* + * Some mice don't ACK commands sent while they are in the middle of + * transmitting motion packet. To avoid delay we use ps2_sendbyte() + * instead of ps2_command() which would wait for 200ms for an ACK + * that may never come. + * As an additional quirk ALPS touchpads may not only forget to ACK + * disable command but will stop reporting taps, so if we see that + * mouse at least once ACKs disable we will do full reconnect if ACK + * is missing. + */ psmouse->num_resyncs++; if (ps2_sendbyte(&psmouse->ps2dev, PSMOUSE_CMD_DISABLE, 20)) { @@ -1291,13 +1262,13 @@ static void psmouse_resync(struct work_struct *work) } else psmouse->acks_disable_command = true; -/* - * Poll the mouse. If it was reset the packet will be shorter than - * psmouse->pktsize and ps2_command will fail. We do not expect and - * do not handle scenario when mouse "upgrades" its protocol while - * disconnected since it would require additional delay. If we ever - * see a mouse that does it we'll adjust the code. - */ + /* + * Poll the mouse. If it was reset the packet will be shorter than + * psmouse->pktsize and ps2_command will fail. We do not expect and + * do not handle scenario when mouse "upgrades" its protocol while + * disconnected since it would require additional delay. If we ever + * see a mouse that does it we'll adjust the code. + */ if (!failed) { if (psmouse->poll(psmouse)) failed = true; @@ -1314,11 +1285,12 @@ static void psmouse_resync(struct work_struct *work) psmouse_set_state(psmouse, PSMOUSE_RESYNCING); } } -/* - * Now try to enable mouse. We try to do that even if poll failed and also - * repeat our attempts 5 times, otherwise we may be left out with disabled - * mouse. - */ + + /* + * Now try to enable mouse. We try to do that even if poll failed + * and also repeat our attempts 5 times, otherwise we may be left + * out with disabled mouse. + */ for (i = 0; i < 5; i++) { if (!ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_ENABLE)) { enabled = true; @@ -1350,7 +1322,6 @@ static void psmouse_resync(struct work_struct *work) /* * psmouse_cleanup() resets the mouse into power-on state. */ - static void psmouse_cleanup(struct serio *serio) { struct psmouse *psmouse = serio_get_drvdata(serio); @@ -1375,15 +1346,15 @@ static void psmouse_cleanup(struct serio *serio) if (psmouse->cleanup) psmouse->cleanup(psmouse); -/* - * Reset the mouse to defaults (bare PS/2 protocol). - */ + /* + * Reset the mouse to defaults (bare PS/2 protocol). + */ ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS); -/* - * Some boxes, such as HP nx7400, get terribly confused if mouse - * is not fully enabled before suspending/shutting down. - */ + /* + * Some boxes, such as HP nx7400, get terribly confused if mouse + * is not fully enabled before suspending/shutting down. + */ ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_ENABLE); if (parent) { @@ -1399,7 +1370,6 @@ static void psmouse_cleanup(struct serio *serio) /* * psmouse_disconnect() closes and frees. */ - static void psmouse_disconnect(struct serio *serio) { struct psmouse *psmouse, *parent = NULL; @@ -1599,7 +1569,6 @@ static int psmouse_connect(struct serio *serio, struct serio_driver *drv) goto out; } - static int psmouse_reconnect(struct serio *serio) { struct psmouse *psmouse = serio_get_drvdata(serio); -- cgit v1.1 From 2b6f39e9ee7b6a9fa98f6047b05733053876fdbe Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 27 Nov 2015 20:52:36 -0800 Subject: Input: psmouse - rearrange Focaltech init code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The fact that we were calling focaltech_init() even when Focaltech support is disabled was confusing. Rearrange the code so that if support is disabled we continue to fall through the rest of protocol probing code until we get to full reset that Focaltech devices need to work properly. Also, replace focaltech_init() with a stub now that it is only called when protocol is enabled. Reviewed-by: Hans de Goede Reviewed-by: Pali Rohár Tested-by: Marcin Sochacki Tested-by: Till Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/focaltech.c | 22 ++++++---------------- drivers/input/mouse/focaltech.h | 8 ++++++++ drivers/input/mouse/psmouse-base.c | 23 ++++++++++++----------- 3 files changed, 26 insertions(+), 27 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/focaltech.c b/drivers/input/mouse/focaltech.c index 4d5576d..c8c6a8c 100644 --- a/drivers/input/mouse/focaltech.c +++ b/drivers/input/mouse/focaltech.c @@ -49,12 +49,6 @@ int focaltech_detect(struct psmouse *psmouse, bool set_properties) return 0; } -static void focaltech_reset(struct psmouse *psmouse) -{ - ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS); - psmouse_reset(psmouse); -} - #ifdef CONFIG_MOUSE_PS2_FOCALTECH /* @@ -300,6 +294,12 @@ static int focaltech_switch_protocol(struct psmouse *psmouse) return 0; } +static void focaltech_reset(struct psmouse *psmouse) +{ + ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS); + psmouse_reset(psmouse); +} + static void focaltech_disconnect(struct psmouse *psmouse) { focaltech_reset(psmouse); @@ -456,14 +456,4 @@ fail: kfree(priv); return error; } - -#else /* CONFIG_MOUSE_PS2_FOCALTECH */ - -int focaltech_init(struct psmouse *psmouse) -{ - focaltech_reset(psmouse); - - return 0; -} - #endif /* CONFIG_MOUSE_PS2_FOCALTECH */ diff --git a/drivers/input/mouse/focaltech.h b/drivers/input/mouse/focaltech.h index ca61ebf..783b28e 100644 --- a/drivers/input/mouse/focaltech.h +++ b/drivers/input/mouse/focaltech.h @@ -18,6 +18,14 @@ #define _FOCALTECH_H int focaltech_detect(struct psmouse *psmouse, bool set_properties); + +#ifdef CONFIG_MOUSE_PS2_FOCALTECH int focaltech_init(struct psmouse *psmouse); +#else +static inline int focaltech_init(struct psmouse *psmouse) +{ + return -ENOSYS; +} +#endif #endif diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index 525de2e..c2bd866 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -750,19 +750,20 @@ static int psmouse_extensions(struct psmouse *psmouse, */ if (psmouse_do_detect(focaltech_detect, psmouse, set_properties) == 0) { if (max_proto > PSMOUSE_IMEX) { - if (!set_properties || focaltech_init(psmouse) == 0) { - if (IS_ENABLED(CONFIG_MOUSE_PS2_FOCALTECH)) - return PSMOUSE_FOCALTECH; - /* - * Note that we need to also restrict - * psmouse_max_proto so that psmouse_initialize() - * does not try to reset rate and resolution, - * because even that upsets the device. - */ - psmouse_max_proto = PSMOUSE_PS2; - return PSMOUSE_PS2; + if (IS_ENABLED(CONFIG_MOUSE_PS2_FOCALTECH) && + (!set_properties || focaltech_init(psmouse) == 0)) { + return PSMOUSE_FOCALTECH; } } + /* + * Restrict psmouse_max_proto so that psmouse_initialize() + * does not try to reset rate and resolution, because even + * that upsets the device. + * This also causes us to basically fall through to basic + * protocol detection, where we fully reset the mouse, + * and set it up as bare PS/2 protocol device. + */ + psmouse_max_proto = max_proto = PSMOUSE_PS2; } /* -- cgit v1.1 From 24a06f3e3ab640490f49c8e8812d9550d6f15e92 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 2 Dec 2015 16:50:42 -0800 Subject: Input: psmouse - clean up Cypress probe When Cypress protocol support is disabled cypress_init() is a stub that always returns -ENOSYS, so there is not point in testing for CONFIG_MOUSE_PS2_CYPRESS after we decided that we are dealing with a Cypress device. Also, we should only be calling cypress_detect() when set_properties argument is "true", like with other protocols. There is a slight change in behavior to make follow-up patches more uniform: when we detect Cypress but its initialization fails, instead of immediately returning PSMOUSE_PS2 protocol we now continue trying IntelliMouse [Explorer]. Given that Cypress devices only have issue with Sentelic probes probing Imtellimouse should be safe. Reviewed-by: Hans de Goede Tested-by: Marcin Sochacki Tested-by: Till Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/psmouse-base.c | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index c2bd866..978ba0b 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -836,19 +836,15 @@ static int psmouse_extensions(struct psmouse *psmouse, * Trackpads. */ if (max_proto > PSMOUSE_IMEX && - cypress_detect(psmouse, set_properties) == 0) { - if (IS_ENABLED(CONFIG_MOUSE_PS2_CYPRESS)) { - if (cypress_init(psmouse) == 0) - return PSMOUSE_CYPRESS; - - /* - * Finger Sensing Pad probe upsets some modules of - * Cypress Trackpad, must avoid Finger Sensing Pad - * probe if Cypress Trackpad device detected. - */ - return PSMOUSE_PS2; - } + psmouse_do_detect(cypress_detect, psmouse, set_properties) == 0) { + if (!set_properties || cypress_init(psmouse) == 0) + return PSMOUSE_CYPRESS; + /* + * Finger Sensing Pad probe upsets some modules of + * Cypress Trackpad, must avoid Finger Sensing Pad + * probe if Cypress Trackpad device detected. + */ max_proto = PSMOUSE_IMEX; } -- cgit v1.1 From 5fa75cfe23633edf2fd26abe4a08f22ced4415d1 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 27 Nov 2015 21:08:28 -0800 Subject: Input: psmouse - move protocol descriptions around MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We move protocol descriptions and psmouse_find_by_type() and pmouse_find_by_name() so that we can use them without forward declarations in the subsequent patches. Reviewed-by: Hans de Goede Reviewed-by: Pali Rohár Tested-by: Marcin Sochacki Tested-by: Till Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/psmouse-base.c | 379 ++++++++++++++++++------------------- 1 file changed, 189 insertions(+), 190 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index 978ba0b..131bbc1 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -683,6 +683,195 @@ static int cortron_detect(struct psmouse *psmouse, bool set_properties) return 0; } +static const struct psmouse_protocol psmouse_protocols[] = { + { + .type = PSMOUSE_PS2, + .name = "PS/2", + .alias = "bare", + .maxproto = true, + .ignore_parity = true, + .detect = ps2bare_detect, + }, +#ifdef CONFIG_MOUSE_PS2_LOGIPS2PP + { + .type = PSMOUSE_PS2PP, + .name = "PS2++", + .alias = "logitech", + .detect = ps2pp_init, + }, +#endif + { + .type = PSMOUSE_THINKPS, + .name = "ThinkPS/2", + .alias = "thinkps", + .detect = thinking_detect, + }, +#ifdef CONFIG_MOUSE_PS2_CYPRESS + { + .type = PSMOUSE_CYPRESS, + .name = "CyPS/2", + .alias = "cypress", + .detect = cypress_detect, + .init = cypress_init, + }, +#endif + { + .type = PSMOUSE_GENPS, + .name = "GenPS/2", + .alias = "genius", + .detect = genius_detect, + }, + { + .type = PSMOUSE_IMPS, + .name = "ImPS/2", + .alias = "imps", + .maxproto = true, + .ignore_parity = true, + .detect = intellimouse_detect, + }, + { + .type = PSMOUSE_IMEX, + .name = "ImExPS/2", + .alias = "exps", + .maxproto = true, + .ignore_parity = true, + .detect = im_explorer_detect, + }, +#ifdef CONFIG_MOUSE_PS2_SYNAPTICS + { + .type = PSMOUSE_SYNAPTICS, + .name = "SynPS/2", + .alias = "synaptics", + .detect = synaptics_detect, + .init = synaptics_init, + }, + { + .type = PSMOUSE_SYNAPTICS_RELATIVE, + .name = "SynRelPS/2", + .alias = "synaptics-relative", + .detect = synaptics_detect, + .init = synaptics_init_relative, + }, +#endif +#ifdef CONFIG_MOUSE_PS2_ALPS + { + .type = PSMOUSE_ALPS, + .name = "AlpsPS/2", + .alias = "alps", + .detect = alps_detect, + .init = alps_init, + }, +#endif +#ifdef CONFIG_MOUSE_PS2_LIFEBOOK + { + .type = PSMOUSE_LIFEBOOK, + .name = "LBPS/2", + .alias = "lifebook", + .init = lifebook_init, + }, +#endif +#ifdef CONFIG_MOUSE_PS2_TRACKPOINT + { + .type = PSMOUSE_TRACKPOINT, + .name = "TPPS/2", + .alias = "trackpoint", + .detect = trackpoint_detect, + }, +#endif +#ifdef CONFIG_MOUSE_PS2_TOUCHKIT + { + .type = PSMOUSE_TOUCHKIT_PS2, + .name = "touchkitPS/2", + .alias = "touchkit", + .detect = touchkit_ps2_detect, + }, +#endif +#ifdef CONFIG_MOUSE_PS2_OLPC + { + .type = PSMOUSE_HGPK, + .name = "OLPC HGPK", + .alias = "hgpk", + .detect = hgpk_detect, + }, +#endif +#ifdef CONFIG_MOUSE_PS2_ELANTECH + { + .type = PSMOUSE_ELANTECH, + .name = "ETPS/2", + .alias = "elantech", + .detect = elantech_detect, + .init = elantech_init, + }, +#endif +#ifdef CONFIG_MOUSE_PS2_SENTELIC + { + .type = PSMOUSE_FSP, + .name = "FSPPS/2", + .alias = "fsp", + .detect = fsp_detect, + .init = fsp_init, + }, +#endif + { + .type = PSMOUSE_CORTRON, + .name = "CortronPS/2", + .alias = "cortps", + .detect = cortron_detect, + }, +#ifdef CONFIG_MOUSE_PS2_FOCALTECH + { + .type = PSMOUSE_FOCALTECH, + .name = "FocalTechPS/2", + .alias = "focaltech", + .detect = focaltech_detect, + .init = focaltech_init, + }, +#endif +#ifdef CONFIG_MOUSE_PS2_VMMOUSE + { + .type = PSMOUSE_VMMOUSE, + .name = VMMOUSE_PSNAME, + .alias = "vmmouse", + .detect = vmmouse_detect, + .init = vmmouse_init, + }, +#endif + { + .type = PSMOUSE_AUTO, + .name = "auto", + .alias = "any", + .maxproto = true, + }, +}; + +static const struct psmouse_protocol *psmouse_protocol_by_type(enum psmouse_type type) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(psmouse_protocols); i++) + if (psmouse_protocols[i].type == type) + return &psmouse_protocols[i]; + + WARN_ON(1); + return &psmouse_protocols[0]; +} + +static const struct psmouse_protocol *psmouse_protocol_by_name(const char *name, size_t len) +{ + const struct psmouse_protocol *p; + int i; + + for (i = 0; i < ARRAY_SIZE(psmouse_protocols); i++) { + p = &psmouse_protocols[i]; + + if ((strlen(p->name) == len && !strncmp(p->name, name, len)) || + (strlen(p->alias) == len && !strncmp(p->alias, name, len))) + return &psmouse_protocols[i]; + } + + return NULL; +} + /* * Apply default settings to the psmouse structure. Most of them will * be overridden by individual protocol initialization routines. @@ -950,196 +1139,6 @@ static int psmouse_extensions(struct psmouse *psmouse, return PSMOUSE_PS2; } -static const struct psmouse_protocol psmouse_protocols[] = { - { - .type = PSMOUSE_PS2, - .name = "PS/2", - .alias = "bare", - .maxproto = true, - .ignore_parity = true, - .detect = ps2bare_detect, - }, -#ifdef CONFIG_MOUSE_PS2_LOGIPS2PP - { - .type = PSMOUSE_PS2PP, - .name = "PS2++", - .alias = "logitech", - .detect = ps2pp_init, - }, -#endif - { - .type = PSMOUSE_THINKPS, - .name = "ThinkPS/2", - .alias = "thinkps", - .detect = thinking_detect, - }, -#ifdef CONFIG_MOUSE_PS2_CYPRESS - { - .type = PSMOUSE_CYPRESS, - .name = "CyPS/2", - .alias = "cypress", - .detect = cypress_detect, - .init = cypress_init, - }, -#endif - { - .type = PSMOUSE_GENPS, - .name = "GenPS/2", - .alias = "genius", - .detect = genius_detect, - }, - { - .type = PSMOUSE_IMPS, - .name = "ImPS/2", - .alias = "imps", - .maxproto = true, - .ignore_parity = true, - .detect = intellimouse_detect, - }, - { - .type = PSMOUSE_IMEX, - .name = "ImExPS/2", - .alias = "exps", - .maxproto = true, - .ignore_parity = true, - .detect = im_explorer_detect, - }, -#ifdef CONFIG_MOUSE_PS2_SYNAPTICS - { - .type = PSMOUSE_SYNAPTICS, - .name = "SynPS/2", - .alias = "synaptics", - .detect = synaptics_detect, - .init = synaptics_init, - }, - { - .type = PSMOUSE_SYNAPTICS_RELATIVE, - .name = "SynRelPS/2", - .alias = "synaptics-relative", - .detect = synaptics_detect, - .init = synaptics_init_relative, - }, -#endif -#ifdef CONFIG_MOUSE_PS2_ALPS - { - .type = PSMOUSE_ALPS, - .name = "AlpsPS/2", - .alias = "alps", - .detect = alps_detect, - .init = alps_init, - }, -#endif -#ifdef CONFIG_MOUSE_PS2_LIFEBOOK - { - .type = PSMOUSE_LIFEBOOK, - .name = "LBPS/2", - .alias = "lifebook", - .init = lifebook_init, - }, -#endif -#ifdef CONFIG_MOUSE_PS2_TRACKPOINT - { - .type = PSMOUSE_TRACKPOINT, - .name = "TPPS/2", - .alias = "trackpoint", - .detect = trackpoint_detect, - }, -#endif -#ifdef CONFIG_MOUSE_PS2_TOUCHKIT - { - .type = PSMOUSE_TOUCHKIT_PS2, - .name = "touchkitPS/2", - .alias = "touchkit", - .detect = touchkit_ps2_detect, - }, -#endif -#ifdef CONFIG_MOUSE_PS2_OLPC - { - .type = PSMOUSE_HGPK, - .name = "OLPC HGPK", - .alias = "hgpk", - .detect = hgpk_detect, - }, -#endif -#ifdef CONFIG_MOUSE_PS2_ELANTECH - { - .type = PSMOUSE_ELANTECH, - .name = "ETPS/2", - .alias = "elantech", - .detect = elantech_detect, - .init = elantech_init, - }, -#endif -#ifdef CONFIG_MOUSE_PS2_SENTELIC - { - .type = PSMOUSE_FSP, - .name = "FSPPS/2", - .alias = "fsp", - .detect = fsp_detect, - .init = fsp_init, - }, -#endif - { - .type = PSMOUSE_CORTRON, - .name = "CortronPS/2", - .alias = "cortps", - .detect = cortron_detect, - }, -#ifdef CONFIG_MOUSE_PS2_FOCALTECH - { - .type = PSMOUSE_FOCALTECH, - .name = "FocalTechPS/2", - .alias = "focaltech", - .detect = focaltech_detect, - .init = focaltech_init, - }, -#endif -#ifdef CONFIG_MOUSE_PS2_VMMOUSE - { - .type = PSMOUSE_VMMOUSE, - .name = VMMOUSE_PSNAME, - .alias = "vmmouse", - .detect = vmmouse_detect, - .init = vmmouse_init, - }, -#endif - { - .type = PSMOUSE_AUTO, - .name = "auto", - .alias = "any", - .maxproto = true, - }, -}; - -static const struct psmouse_protocol *psmouse_protocol_by_type(enum psmouse_type type) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(psmouse_protocols); i++) - if (psmouse_protocols[i].type == type) - return &psmouse_protocols[i]; - - WARN_ON(1); - return &psmouse_protocols[0]; -} - -static const struct psmouse_protocol *psmouse_protocol_by_name(const char *name, size_t len) -{ - const struct psmouse_protocol *p; - int i; - - for (i = 0; i < ARRAY_SIZE(psmouse_protocols); i++) { - p = &psmouse_protocols[i]; - - if ((strlen(p->name) == len && !strncmp(p->name, name, len)) || - (strlen(p->alias) == len && !strncmp(p->alias, name, len))) - return &psmouse_protocols[i]; - } - - return NULL; -} - - /* * psmouse_probe() probes for a PS/2 mouse. */ -- cgit v1.1 From c378b5119eb0ea25bfd3348a06b51fca654d9903 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 27 Nov 2015 21:17:40 -0800 Subject: Input: psmouse - factor out common protocol probing code In preparation of limiting protocols that we try on pass-through ports, let's rework initialization code and factor common code into psmouse_try_protocol() that accepts protocol type (instead of detec() function pointer) and can, for most protocols, perform both detection and initialization. Note that this removes option of forcing Lifebook protocol on devices that are not recognized by lifebook_detect() as having the hardware, but I do not recall anyone using this option. Reviewed-by: Hans de Goede Tested-by: Marcin Sochacki Tested-by: Till Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/psmouse-base.c | 164 +++++++++++++++++++------------------ 1 file changed, 86 insertions(+), 78 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index 131bbc1..316105e 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -767,6 +767,7 @@ static const struct psmouse_protocol psmouse_protocols[] = { .type = PSMOUSE_LIFEBOOK, .name = "LBPS/2", .alias = "lifebook", + .detect = lifebook_detect, .init = lifebook_init, }, #endif @@ -844,7 +845,7 @@ static const struct psmouse_protocol psmouse_protocols[] = { }, }; -static const struct psmouse_protocol *psmouse_protocol_by_type(enum psmouse_type type) +static const struct psmouse_protocol *__psmouse_protocol_by_type(enum psmouse_type type) { int i; @@ -852,6 +853,17 @@ static const struct psmouse_protocol *psmouse_protocol_by_type(enum psmouse_type if (psmouse_protocols[i].type == type) return &psmouse_protocols[i]; + return NULL; +} + +static const struct psmouse_protocol *psmouse_protocol_by_type(enum psmouse_type type) +{ + const struct psmouse_protocol *proto; + + proto = __psmouse_protocol_by_type(type); + if (proto) + return proto; + WARN_ON(1); return &psmouse_protocols[0]; } @@ -910,18 +922,37 @@ static void psmouse_apply_defaults(struct psmouse *psmouse) psmouse->pt_deactivate = NULL; } -/* - * Apply default settings to the psmouse structure and call specified - * protocol detection or initialization routine. - */ -static int psmouse_do_detect(int (*detect)(struct psmouse *psmouse, - bool set_properties), - struct psmouse *psmouse, bool set_properties) +static bool psmouse_try_protocol(struct psmouse *psmouse, + enum psmouse_type type, + unsigned int *max_proto, + bool set_properties, bool init_allowed) { + const struct psmouse_protocol *proto; + + proto = __psmouse_protocol_by_type(type); + if (!proto) + return false; + if (set_properties) psmouse_apply_defaults(psmouse); - return detect(psmouse, set_properties); + if (proto->detect(psmouse, set_properties) != 0) + return false; + + if (set_properties && proto->init && init_allowed) { + if (proto->init(psmouse) != 0) { + /* + * We detected device, but init failed. Adjust + * max_proto so we only try standard protocols. + */ + if (*max_proto > PSMOUSE_IMEX) + *max_proto = PSMOUSE_IMEX; + + return false; + } + } + + return true; } /* @@ -937,12 +968,12 @@ static int psmouse_extensions(struct psmouse *psmouse, * Always check for focaltech, this is safe as it uses pnp-id * matching. */ - if (psmouse_do_detect(focaltech_detect, psmouse, set_properties) == 0) { - if (max_proto > PSMOUSE_IMEX) { - if (IS_ENABLED(CONFIG_MOUSE_PS2_FOCALTECH) && - (!set_properties || focaltech_init(psmouse) == 0)) { - return PSMOUSE_FOCALTECH; - } + if (psmouse_try_protocol(psmouse, PSMOUSE_FOCALTECH, + &max_proto, set_properties, false)) { + if (max_proto > PSMOUSE_IMEX && + IS_ENABLED(CONFIG_MOUSE_PS2_FOCALTECH) && + (!set_properties || focaltech_init(psmouse) == 0)) { + return PSMOUSE_FOCALTECH; } /* * Restrict psmouse_max_proto so that psmouse_initialize() @@ -959,26 +990,21 @@ static int psmouse_extensions(struct psmouse *psmouse, * We always check for LifeBook because it does not disturb mouse * (it only checks DMI information). */ - if (psmouse_do_detect(lifebook_detect, psmouse, set_properties) == 0) { - if (max_proto > PSMOUSE_IMEX) { - if (!set_properties || lifebook_init(psmouse) == 0) - return PSMOUSE_LIFEBOOK; - } - } + if (psmouse_try_protocol(psmouse, PSMOUSE_LIFEBOOK, &max_proto, + set_properties, max_proto > PSMOUSE_IMEX)) + return PSMOUSE_LIFEBOOK; - if (psmouse_do_detect(vmmouse_detect, psmouse, set_properties) == 0) { - if (max_proto > PSMOUSE_IMEX) { - if (!set_properties || vmmouse_init(psmouse) == 0) - return PSMOUSE_VMMOUSE; - } - } + if (psmouse_try_protocol(psmouse, PSMOUSE_VMMOUSE, &max_proto, + set_properties, max_proto > PSMOUSE_IMEX)) + return PSMOUSE_VMMOUSE; /* * Try Kensington ThinkingMouse (we try first, because Synaptics * probe upsets the ThinkingMouse). */ if (max_proto > PSMOUSE_IMEX && - psmouse_do_detect(thinking_detect, psmouse, set_properties) == 0) { + psmouse_try_protocol(psmouse, PSMOUSE_THINKPS, &max_proto, + set_properties, true)) { return PSMOUSE_THINKPS; } @@ -989,7 +1015,8 @@ static int psmouse_extensions(struct psmouse *psmouse, * probing for IntelliMouse. */ if (max_proto > PSMOUSE_PS2 && - psmouse_do_detect(synaptics_detect, psmouse, set_properties) == 0) { + psmouse_try_protocol(psmouse, PSMOUSE_SYNAPTICS, &max_proto, + set_properties, false)) { synaptics_hardware = true; if (max_proto > PSMOUSE_IMEX) { @@ -1025,64 +1052,48 @@ static int psmouse_extensions(struct psmouse *psmouse, * Trackpads. */ if (max_proto > PSMOUSE_IMEX && - psmouse_do_detect(cypress_detect, psmouse, set_properties) == 0) { - if (!set_properties || cypress_init(psmouse) == 0) - return PSMOUSE_CYPRESS; - - /* - * Finger Sensing Pad probe upsets some modules of - * Cypress Trackpad, must avoid Finger Sensing Pad - * probe if Cypress Trackpad device detected. - */ - max_proto = PSMOUSE_IMEX; + psmouse_try_protocol(psmouse, PSMOUSE_CYPRESS, &max_proto, + set_properties, true)) { + return PSMOUSE_CYPRESS; } /* Try ALPS TouchPad */ if (max_proto > PSMOUSE_IMEX) { ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS); - if (psmouse_do_detect(alps_detect, - psmouse, set_properties) == 0) { - if (!set_properties || alps_init(psmouse) == 0) - return PSMOUSE_ALPS; - - /* Init failed, try basic relative protocols */ - max_proto = PSMOUSE_IMEX; - } + if (psmouse_try_protocol(psmouse, PSMOUSE_ALPS, + &max_proto, set_properties, true)) + return PSMOUSE_ALPS; } /* Try OLPC HGPK touchpad */ if (max_proto > PSMOUSE_IMEX && - psmouse_do_detect(hgpk_detect, psmouse, set_properties) == 0) { - if (!set_properties || hgpk_init(psmouse) == 0) - return PSMOUSE_HGPK; - /* Init failed, try basic relative protocols */ - max_proto = PSMOUSE_IMEX; + psmouse_try_protocol(psmouse, PSMOUSE_HGPK, &max_proto, + set_properties, true)) { + return PSMOUSE_HGPK; } /* Try Elantech touchpad */ if (max_proto > PSMOUSE_IMEX && - psmouse_do_detect(elantech_detect, psmouse, set_properties) == 0) { - if (!set_properties || elantech_init(psmouse) == 0) - return PSMOUSE_ELANTECH; - /* Init failed, try basic relative protocols */ - max_proto = PSMOUSE_IMEX; + psmouse_try_protocol(psmouse, PSMOUSE_ELANTECH, + &max_proto, set_properties, true)) { + return PSMOUSE_ELANTECH; } if (max_proto > PSMOUSE_IMEX) { - if (psmouse_do_detect(genius_detect, - psmouse, set_properties) == 0) + if (psmouse_try_protocol(psmouse, PSMOUSE_GENPS, + &max_proto, set_properties, true)) return PSMOUSE_GENPS; - if (psmouse_do_detect(ps2pp_init, - psmouse, set_properties) == 0) + if (psmouse_try_protocol(psmouse, PSMOUSE_PS2PP, + &max_proto, set_properties, true)) return PSMOUSE_PS2PP; - if (psmouse_do_detect(trackpoint_detect, - psmouse, set_properties) == 0) + if (psmouse_try_protocol(psmouse, PSMOUSE_TRACKPOINT, + &max_proto, set_properties, true)) return PSMOUSE_TRACKPOINT; - if (psmouse_do_detect(touchkit_ps2_detect, - psmouse, set_properties) == 0) + if (psmouse_try_protocol(psmouse, PSMOUSE_TOUCHKIT_PS2, + &max_proto, set_properties, true)) return PSMOUSE_TOUCHKIT_PS2; } @@ -1090,14 +1101,10 @@ static int psmouse_extensions(struct psmouse *psmouse, * Try Finger Sensing Pad. We do it here because its probe upsets * Trackpoint devices (causing TP_READ_ID command to time out). */ - if (max_proto > PSMOUSE_IMEX) { - if (psmouse_do_detect(fsp_detect, - psmouse, set_properties) == 0) { - if (!set_properties || fsp_init(psmouse) == 0) - return PSMOUSE_FSP; - /* Init failed, try basic relative protocols */ - max_proto = PSMOUSE_IMEX; - } + if (max_proto > PSMOUSE_IMEX && + psmouse_try_protocol(psmouse, PSMOUSE_FSP, + &max_proto, set_properties, true)) { + return PSMOUSE_FSP; } /* @@ -1109,14 +1116,14 @@ static int psmouse_extensions(struct psmouse *psmouse, psmouse_reset(psmouse); if (max_proto >= PSMOUSE_IMEX && - psmouse_do_detect(im_explorer_detect, - psmouse, set_properties) == 0) { + psmouse_try_protocol(psmouse, PSMOUSE_IMEX, + &max_proto, set_properties, true)) { return PSMOUSE_IMEX; } if (max_proto >= PSMOUSE_IMPS && - psmouse_do_detect(intellimouse_detect, - psmouse, set_properties) == 0) { + psmouse_try_protocol(psmouse, PSMOUSE_IMPS, + &max_proto, set_properties, true)) { return PSMOUSE_IMPS; } @@ -1124,7 +1131,8 @@ static int psmouse_extensions(struct psmouse *psmouse, * Okay, all failed, we have a standard mouse here. The number of * the buttons is still a question, though. We assume 3. */ - psmouse_do_detect(ps2bare_detect, psmouse, set_properties); + psmouse_try_protocol(psmouse, PSMOUSE_PS2, + &max_proto, set_properties, true); if (synaptics_hardware) { /* -- cgit v1.1 From ec6184b1c717b8768122e25fe6d312f609cc1bb4 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 24 Nov 2015 12:58:46 -0800 Subject: Input: psmouse - limit protocols that we try on passthrough ports MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PS/2 protocol is slow, and using it with pass-through port (where we encapsulate PS/2 into PS/2) is slower yet so it takes quite a bit of time to do full protocol discovery for device attached to a pass-through port. However, so far we have not see anything but trackpoints or basic PS/2 mice on pass-through ports, so let's limit protocols that we probe there to Trackpoint, IntelliMouse Explorer, IntelliMouse, and bare PS/2 protocol, and avoid other extended protocols, such as Synaptics, ALPS, etc. Reviewed-by: Hans de Goede Reviewed-by: Pali Rohár Tested-by: Marcin Sochacki Tested-by: Till Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/psmouse-base.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers/input') diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index 316105e..f12df77 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -119,6 +119,7 @@ struct psmouse_protocol { enum psmouse_type type; bool maxproto; bool ignore_parity; /* Protocol should ignore parity errors from KBC */ + bool try_passthru; /* Try protocol also on passthrough ports */ const char *name; const char *alias; int (*detect)(struct psmouse *, bool); @@ -691,6 +692,7 @@ static const struct psmouse_protocol psmouse_protocols[] = { .maxproto = true, .ignore_parity = true, .detect = ps2bare_detect, + .try_passthru = true, }, #ifdef CONFIG_MOUSE_PS2_LOGIPS2PP { @@ -728,6 +730,7 @@ static const struct psmouse_protocol psmouse_protocols[] = { .maxproto = true, .ignore_parity = true, .detect = intellimouse_detect, + .try_passthru = true, }, { .type = PSMOUSE_IMEX, @@ -736,6 +739,7 @@ static const struct psmouse_protocol psmouse_protocols[] = { .maxproto = true, .ignore_parity = true, .detect = im_explorer_detect, + .try_passthru = true, }, #ifdef CONFIG_MOUSE_PS2_SYNAPTICS { @@ -777,6 +781,7 @@ static const struct psmouse_protocol psmouse_protocols[] = { .name = "TPPS/2", .alias = "trackpoint", .detect = trackpoint_detect, + .try_passthru = true, }, #endif #ifdef CONFIG_MOUSE_PS2_TOUCHKIT @@ -933,6 +938,11 @@ static bool psmouse_try_protocol(struct psmouse *psmouse, if (!proto) return false; + if (psmouse->ps2dev.serio->id.type == SERIO_PS_PSTHRU && + !proto->try_passthru) { + return false; + } + if (set_properties) psmouse_apply_defaults(psmouse); -- cgit v1.1 From 190e2031e2db542094659cfa55bfc28545458df5 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 2 Dec 2015 11:02:39 -0800 Subject: Input: psmouse - rename ps2pp_init() to ps2pp_detect() This makes Logitech PS2++ protocol implementation consistent with the naming in other protocols. Also mark the stub as "static inline" Reviewed-by: Hans de Goede Tested-by: Marcin Sochacki Tested-by: Till Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/logips2pp.c | 2 +- drivers/input/mouse/logips2pp.h | 4 ++-- drivers/input/mouse/psmouse-base.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/logips2pp.c b/drivers/input/mouse/logips2pp.c index 136e222..422da1c 100644 --- a/drivers/input/mouse/logips2pp.c +++ b/drivers/input/mouse/logips2pp.c @@ -325,7 +325,7 @@ static void ps2pp_set_model_properties(struct psmouse *psmouse, * that support it. */ -int ps2pp_init(struct psmouse *psmouse, bool set_properties) +int ps2pp_detect(struct psmouse *psmouse, bool set_properties) { struct ps2dev *ps2dev = &psmouse->ps2dev; unsigned char param[4]; diff --git a/drivers/input/mouse/logips2pp.h b/drivers/input/mouse/logips2pp.h index 0c186f0..bf62945 100644 --- a/drivers/input/mouse/logips2pp.h +++ b/drivers/input/mouse/logips2pp.h @@ -12,9 +12,9 @@ #define _LOGIPS2PP_H #ifdef CONFIG_MOUSE_PS2_LOGIPS2PP -int ps2pp_init(struct psmouse *psmouse, bool set_properties); +int ps2pp_detect(struct psmouse *psmouse, bool set_properties); #else -inline int ps2pp_init(struct psmouse *psmouse, bool set_properties) +static inline int ps2pp_detect(struct psmouse *psmouse, bool set_properties) { return -ENOSYS; } diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index f12df77..b9e4ee3 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -699,7 +699,7 @@ static const struct psmouse_protocol psmouse_protocols[] = { .type = PSMOUSE_PS2PP, .name = "PS2++", .alias = "logitech", - .detect = ps2pp_init, + .detect = ps2pp_detect, }, #endif { -- cgit v1.1 From a779fbc6c931007559b74fd74c2dc7b1c25bac33 Mon Sep 17 00:00:00 2001 From: Irina Tirdea Date: Thu, 17 Dec 2015 15:55:21 -0800 Subject: Input: goodix - use actual config length for each device type Each of the Goodix devices supported by this driver has a fixed size for the configuration information registers. The size varies depending on the device and is specified in the datasheet. Use the proper configuration length as specified in the datasheet for each device model, so we do not read more than the actual size of the configuration registers. Signed-off-by: Irina Tirdea Acked-by: Bastien Nocera Tested-by: Bastien Nocera Tested-by: Aleksei Mamlin Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/goodix.c | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c index 4d113c9..5479aa1 100644 --- a/drivers/input/touchscreen/goodix.c +++ b/drivers/input/touchscreen/goodix.c @@ -36,6 +36,7 @@ struct goodix_ts_data { unsigned int max_touch_num; unsigned int int_trigger_type; bool rotated_screen; + int cfg_len; }; #define GOODIX_MAX_HEIGHT 4096 @@ -45,6 +46,8 @@ struct goodix_ts_data { #define GOODIX_MAX_CONTACTS 10 #define GOODIX_CONFIG_MAX_LENGTH 240 +#define GOODIX_CONFIG_911_LENGTH 186 +#define GOODIX_CONFIG_967_LENGTH 228 /* Register defines */ #define GOODIX_READ_COOR_ADDR 0x814E @@ -115,6 +118,25 @@ static int goodix_i2c_read(struct i2c_client *client, return ret < 0 ? ret : (ret != ARRAY_SIZE(msgs) ? -EIO : 0); } +static int goodix_get_cfg_len(u16 id) +{ + switch (id) { + case 911: + case 9271: + case 9110: + case 927: + case 928: + return GOODIX_CONFIG_911_LENGTH; + + case 912: + case 967: + return GOODIX_CONFIG_967_LENGTH; + + default: + return GOODIX_CONFIG_MAX_LENGTH; + } +} + static int goodix_ts_read_input_report(struct goodix_ts_data *ts, u8 *data) { int touch_num; @@ -230,8 +252,7 @@ static void goodix_read_config(struct goodix_ts_data *ts) int error; error = goodix_i2c_read(ts->client, GOODIX_REG_CONFIG_DATA, - config, - GOODIX_CONFIG_MAX_LENGTH); + config, ts->cfg_len); if (error) { dev_warn(&ts->client->dev, "Error reading config (%d), using defaults\n", @@ -398,6 +419,8 @@ static int goodix_ts_probe(struct i2c_client *client, return error; } + ts->cfg_len = goodix_get_cfg_len(id_info); + goodix_read_config(ts); error = goodix_request_input_dev(ts, version_info, id_info); -- cgit v1.1 From ec6e1b4082d9f5b0858ce33169a1c22a27a982f6 Mon Sep 17 00:00:00 2001 From: Irina Tirdea Date: Thu, 17 Dec 2015 15:57:34 -0800 Subject: Input: goodix - reset device at init After power on, it is recommended that the driver resets the device. The reset procedure timing is described in the datasheet and is used at device init (before writing device configuration) and for power management. It is a sequence of setting the interrupt and reset pins high/low at specific timing intervals. This procedure also includes setting the slave address to the one specified in the ACPI/device tree. This is based on Goodix datasheets for GT911 and GT9271 and on Goodix driver gt9xx.c for Android (publicly available in Android kernel trees for various devices). For reset the driver needs to control the interrupt and reset gpio pins (configured through ACPI/device tree). For devices that do not have the gpio pins properly declared, the functionality depending on these pins will not be available, but the device can still be used with basic functionality. For both device tree and ACPI, the interrupt gpio pin configuration is read from the "irq-gpios" property and the reset pin configuration is read from the "reset-gpios" property. For ACPI 5.1, named properties can be specified using the _DSD section. This functionality will not be available for devices that use indexed gpio pins declared in the _CRS section (we need to provide backward compatibility with devices that do not support using the interrupt gpio pin as output). For ACPI, the pins can be specified using ACPI 5.1: Device (STAC) { Name (_HID, "GDIX1001") ... Method (_CRS, 0, Serialized) { Name (RBUF, ResourceTemplate () { I2cSerialBus (0x0014, ControllerInitiated, 0x00061A80, AddressingMode7Bit, "\\I2C0", 0x00, ResourceConsumer, , ) GpioInt (Edge, ActiveHigh, Exclusive, PullNone, 0x0000, "\\I2C0", 0x00, ResourceConsumer, , ) { // Pin list 0 } GpioIo (Exclusive, PullDown, 0x0000, 0x0000, IoRestrictionOutputOnly, "\\I2C0", 0x00, ResourceConsumer, , ) { 1 } }) Return (RBUF) } Name (_DSD, Package () { ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"), Package () { Package (2) {"irq-gpios", Package() {^STAC, 0, 0, 0 }}, Package (2) {"reset-gpios", Package() {^STAC, 1, 0, 0 }}, ... } } Signed-off-by: Octavian Purdila Signed-off-by: Irina Tirdea Acked-by: Rob Herring Acked-by: Bastien Nocera Tested-by: Bastien Nocera Tested-by: Aleksei Mamlin Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/Kconfig | 1 + drivers/input/touchscreen/goodix.c | 119 +++++++++++++++++++++++++++++++++++++ 2 files changed, 120 insertions(+) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index ae33da7..709527c 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -324,6 +324,7 @@ config TOUCHSCREEN_FUJITSU config TOUCHSCREEN_GOODIX tristate "Goodix I2C touchscreen" depends on I2C + depends on GPIOLIB help Say Y here if you have the Goodix touchscreen (such as one installed in Onda v975w tablets) connected to your diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c index 5479aa1..6ad379a 100644 --- a/drivers/input/touchscreen/goodix.c +++ b/drivers/input/touchscreen/goodix.c @@ -16,6 +16,7 @@ #include #include +#include #include #include #include @@ -37,8 +38,13 @@ struct goodix_ts_data { unsigned int int_trigger_type; bool rotated_screen; int cfg_len; + struct gpio_desc *gpiod_int; + struct gpio_desc *gpiod_rst; }; +#define GOODIX_GPIO_INT_NAME "irq" +#define GOODIX_GPIO_RST_NAME "reset" + #define GOODIX_MAX_HEIGHT 4096 #define GOODIX_MAX_WIDTH 4096 #define GOODIX_INT_TRIGGER 1 @@ -239,6 +245,106 @@ static irqreturn_t goodix_ts_irq_handler(int irq, void *dev_id) return IRQ_HANDLED; } +static int goodix_int_sync(struct goodix_ts_data *ts) +{ + int error; + + error = gpiod_direction_output(ts->gpiod_int, 0); + if (error) + return error; + + msleep(50); /* T5: 50ms */ + + error = gpiod_direction_input(ts->gpiod_int); + if (error) + return error; + + return 0; +} + +/** + * goodix_reset - Reset device during power on + * + * @ts: goodix_ts_data pointer + */ +static int goodix_reset(struct goodix_ts_data *ts) +{ + int error; + + /* begin select I2C slave addr */ + error = gpiod_direction_output(ts->gpiod_rst, 0); + if (error) + return error; + + msleep(20); /* T2: > 10ms */ + + /* HIGH: 0x28/0x29, LOW: 0xBA/0xBB */ + error = gpiod_direction_output(ts->gpiod_int, ts->client->addr == 0x14); + if (error) + return error; + + usleep_range(100, 2000); /* T3: > 100us */ + + error = gpiod_direction_output(ts->gpiod_rst, 1); + if (error) + return error; + + usleep_range(6000, 10000); /* T4: > 5ms */ + + /* end select I2C slave addr */ + error = gpiod_direction_input(ts->gpiod_rst); + if (error) + return error; + + error = goodix_int_sync(ts); + if (error) + return error; + + return 0; +} + +/** + * goodix_get_gpio_config - Get GPIO config from ACPI/DT + * + * @ts: goodix_ts_data pointer + */ +static int goodix_get_gpio_config(struct goodix_ts_data *ts) +{ + int error; + struct device *dev; + struct gpio_desc *gpiod; + + if (!ts->client) + return -EINVAL; + dev = &ts->client->dev; + + /* Get the interrupt GPIO pin number */ + gpiod = devm_gpiod_get_optional(dev, GOODIX_GPIO_INT_NAME, GPIOD_IN); + if (IS_ERR(gpiod)) { + error = PTR_ERR(gpiod); + if (error != -EPROBE_DEFER) + dev_dbg(dev, "Failed to get %s GPIO: %d\n", + GOODIX_GPIO_INT_NAME, error); + return error; + } + + ts->gpiod_int = gpiod; + + /* Get the reset line GPIO pin number */ + gpiod = devm_gpiod_get_optional(dev, GOODIX_GPIO_RST_NAME, GPIOD_IN); + if (IS_ERR(gpiod)) { + error = PTR_ERR(gpiod); + if (error != -EPROBE_DEFER) + dev_dbg(dev, "Failed to get %s GPIO: %d\n", + GOODIX_GPIO_RST_NAME, error); + return error; + } + + ts->gpiod_rst = gpiod; + + return 0; +} + /** * goodix_read_config - Read the embedded configuration of the panel * @@ -407,6 +513,19 @@ static int goodix_ts_probe(struct i2c_client *client, ts->client = client; i2c_set_clientdata(client, ts); + error = goodix_get_gpio_config(ts); + if (error) + return error; + + if (ts->gpiod_int && ts->gpiod_rst) { + /* reset the controller */ + error = goodix_reset(ts); + if (error) { + dev_err(&client->dev, "Controller reset failed.\n"); + return error; + } + } + error = goodix_i2c_test(client); if (error) { dev_err(&client->dev, "I2C communication failure: %d\n", error); -- cgit v1.1 From 68caf85881cd842b59d5e2124a236ecce0389a73 Mon Sep 17 00:00:00 2001 From: Irina Tirdea Date: Thu, 17 Dec 2015 16:05:42 -0800 Subject: Input: goodix - write configuration data to device Goodix devices can be configured by writing custom data to the device at init. The configuration data is read with request_firmware from "goodix__cfg.bin", where is the product id read from the device (e.g.: goodix_911_cfg.bin for Goodix GT911, goodix_9271_cfg.bin for GT9271). The configuration information has a specific format described in the Goodix datasheet. It includes X/Y resolution, maximum supported touch points, interrupt flags, various sensitivity factors and settings for advanced features (like gesture recognition). Before writing the firmware, it is necessary to reset the device. If the device ACPI/DT information does not declare gpio pins (needed for reset), writing the firmware will not be available for these devices. This is based on Goodix datasheets for GT911 and GT9271 and on Goodix driver gt9xx.c for Android (publicly available in Android kernel trees for various devices). Signed-off-by: Octavian Purdila Signed-off-by: Irina Tirdea Tested-by: Bastien Nocera Tested-by: Aleksei Mamlin Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/goodix.c | 244 ++++++++++++++++++++++++++++++++----- 1 file changed, 212 insertions(+), 32 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c index 6ad379a..cd92d8f 100644 --- a/drivers/input/touchscreen/goodix.c +++ b/drivers/input/touchscreen/goodix.c @@ -16,6 +16,7 @@ #include #include +#include #include #include #include @@ -40,6 +41,10 @@ struct goodix_ts_data { int cfg_len; struct gpio_desc *gpiod_int; struct gpio_desc *gpiod_rst; + u16 id; + u16 version; + const char *cfg_name; + struct completion firmware_loading_complete; }; #define GOODIX_GPIO_INT_NAME "irq" @@ -124,6 +129,39 @@ static int goodix_i2c_read(struct i2c_client *client, return ret < 0 ? ret : (ret != ARRAY_SIZE(msgs) ? -EIO : 0); } +/** + * goodix_i2c_write - write data to a register of the i2c slave device. + * + * @client: i2c device. + * @reg: the register to write to. + * @buf: raw data buffer to write. + * @len: length of the buffer to write + */ +static int goodix_i2c_write(struct i2c_client *client, u16 reg, const u8 *buf, + unsigned len) +{ + u8 *addr_buf; + struct i2c_msg msg; + int ret; + + addr_buf = kmalloc(len + 2, GFP_KERNEL); + if (!addr_buf) + return -ENOMEM; + + addr_buf[0] = reg >> 8; + addr_buf[1] = reg & 0xFF; + memcpy(&addr_buf[2], buf, len); + + msg.flags = 0; + msg.addr = client->addr; + msg.buf = addr_buf; + msg.len = len + 2; + + ret = i2c_transfer(client->adapter, &msg, 1); + kfree(addr_buf); + return ret < 0 ? ret : (ret != 1 ? -EIO : 0); +} + static int goodix_get_cfg_len(u16 id) { switch (id) { @@ -245,6 +283,73 @@ static irqreturn_t goodix_ts_irq_handler(int irq, void *dev_id) return IRQ_HANDLED; } +/** + * goodix_check_cfg - Checks if config fw is valid + * + * @ts: goodix_ts_data pointer + * @cfg: firmware config data + */ +static int goodix_check_cfg(struct goodix_ts_data *ts, + const struct firmware *cfg) +{ + int i, raw_cfg_len; + u8 check_sum = 0; + + if (cfg->size > GOODIX_CONFIG_MAX_LENGTH) { + dev_err(&ts->client->dev, + "The length of the config fw is not correct"); + return -EINVAL; + } + + raw_cfg_len = cfg->size - 2; + for (i = 0; i < raw_cfg_len; i++) + check_sum += cfg->data[i]; + check_sum = (~check_sum) + 1; + if (check_sum != cfg->data[raw_cfg_len]) { + dev_err(&ts->client->dev, + "The checksum of the config fw is not correct"); + return -EINVAL; + } + + if (cfg->data[raw_cfg_len + 1] != 1) { + dev_err(&ts->client->dev, + "Config fw must have Config_Fresh register set"); + return -EINVAL; + } + + return 0; +} + +/** + * goodix_send_cfg - Write fw config to device + * + * @ts: goodix_ts_data pointer + * @cfg: config firmware to write to device + */ +static int goodix_send_cfg(struct goodix_ts_data *ts, + const struct firmware *cfg) +{ + int error; + + error = goodix_check_cfg(ts, cfg); + if (error) + return error; + + error = goodix_i2c_write(ts->client, GOODIX_REG_CONFIG_DATA, cfg->data, + cfg->size); + if (error) { + dev_err(&ts->client->dev, "Failed to write config data: %d", + error); + return error; + } + dev_dbg(&ts->client->dev, "Config sent successfully."); + + /* Let the firmware reconfigure itself, so sleep for 10ms */ + usleep_range(10000, 11000); + + return 0; +} + static int goodix_int_sync(struct goodix_ts_data *ts) { int error; @@ -391,30 +496,29 @@ static void goodix_read_config(struct goodix_ts_data *ts) /** * goodix_read_version - Read goodix touchscreen version * - * @client: the i2c client - * @version: output buffer containing the version on success - * @id: output buffer containing the id on success + * @ts: our goodix_ts_data pointer */ -static int goodix_read_version(struct i2c_client *client, u16 *version, u16 *id) +static int goodix_read_version(struct goodix_ts_data *ts) { int error; u8 buf[6]; char id_str[5]; - error = goodix_i2c_read(client, GOODIX_REG_ID, buf, sizeof(buf)); + error = goodix_i2c_read(ts->client, GOODIX_REG_ID, buf, sizeof(buf)); if (error) { - dev_err(&client->dev, "read version failed: %d\n", error); + dev_err(&ts->client->dev, "read version failed: %d\n", error); return error; } memcpy(id_str, buf, 4); id_str[4] = 0; - if (kstrtou16(id_str, 10, id)) - *id = 0x1001; + if (kstrtou16(id_str, 10, &ts->id)) + ts->id = 0x1001; - *version = get_unaligned_le16(&buf[4]); + ts->version = get_unaligned_le16(&buf[4]); - dev_info(&client->dev, "ID %d, version: %04x\n", *id, *version); + dev_info(&ts->client->dev, "ID %d, version: %04x\n", ts->id, + ts->version); return 0; } @@ -448,13 +552,10 @@ static int goodix_i2c_test(struct i2c_client *client) * goodix_request_input_dev - Allocate, populate and register the input device * * @ts: our goodix_ts_data pointer - * @version: device firmware version - * @id: device ID * * Must be called during probe */ -static int goodix_request_input_dev(struct goodix_ts_data *ts, u16 version, - u16 id) +static int goodix_request_input_dev(struct goodix_ts_data *ts) { int error; @@ -478,8 +579,8 @@ static int goodix_request_input_dev(struct goodix_ts_data *ts, u16 version, ts->input_dev->phys = "input/ts"; ts->input_dev->id.bustype = BUS_I2C; ts->input_dev->id.vendor = 0x0416; - ts->input_dev->id.product = id; - ts->input_dev->id.version = version; + ts->input_dev->id.product = ts->id; + ts->input_dev->id.version = ts->version; error = input_register_device(ts->input_dev); if (error) { @@ -491,13 +592,71 @@ static int goodix_request_input_dev(struct goodix_ts_data *ts, u16 version, return 0; } +/** + * goodix_configure_dev - Finish device initialization + * + * @ts: our goodix_ts_data pointer + * + * Must be called from probe to finish initialization of the device. + * Contains the common initialization code for both devices that + * declare gpio pins and devices that do not. It is either called + * directly from probe or from request_firmware_wait callback. + */ +static int goodix_configure_dev(struct goodix_ts_data *ts) +{ + int error; + unsigned long irq_flags; + + goodix_read_config(ts); + + error = goodix_request_input_dev(ts); + if (error) + return error; + + irq_flags = goodix_irq_flags[ts->int_trigger_type] | IRQF_ONESHOT; + error = devm_request_threaded_irq(&ts->client->dev, ts->client->irq, + NULL, goodix_ts_irq_handler, + irq_flags, ts->client->name, ts); + if (error) { + dev_err(&ts->client->dev, "request IRQ failed: %d\n", error); + return error; + } + + return 0; +} + +/** + * goodix_config_cb - Callback to finish device init + * + * @ts: our goodix_ts_data pointer + * + * request_firmware_wait callback that finishes + * initialization of the device. + */ +static void goodix_config_cb(const struct firmware *cfg, void *ctx) +{ + struct goodix_ts_data *ts = ctx; + int error; + + if (cfg) { + /* send device configuration to the firmware */ + error = goodix_send_cfg(ts, cfg); + if (error) + goto err_release_cfg; + } + + goodix_configure_dev(ts); + +err_release_cfg: + release_firmware(cfg); + complete_all(&ts->firmware_loading_complete); +} + static int goodix_ts_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct goodix_ts_data *ts; - unsigned long irq_flags; int error; - u16 version_info, id_info; dev_dbg(&client->dev, "I2C Address: 0x%02x\n", client->addr); @@ -512,6 +671,7 @@ static int goodix_ts_probe(struct i2c_client *client, ts->client = client; i2c_set_clientdata(client, ts); + init_completion(&ts->firmware_loading_complete); error = goodix_get_gpio_config(ts); if (error) @@ -532,32 +692,51 @@ static int goodix_ts_probe(struct i2c_client *client, return error; } - error = goodix_read_version(client, &version_info, &id_info); + error = goodix_read_version(ts); if (error) { dev_err(&client->dev, "Read version failed.\n"); return error; } - ts->cfg_len = goodix_get_cfg_len(id_info); - - goodix_read_config(ts); + ts->cfg_len = goodix_get_cfg_len(ts->id); - error = goodix_request_input_dev(ts, version_info, id_info); - if (error) - return error; + if (ts->gpiod_int && ts->gpiod_rst) { + /* update device config */ + ts->cfg_name = devm_kasprintf(&client->dev, GFP_KERNEL, + "goodix_%d_cfg.bin", ts->id); + if (!ts->cfg_name) + return -ENOMEM; + + error = request_firmware_nowait(THIS_MODULE, true, ts->cfg_name, + &client->dev, GFP_KERNEL, ts, + goodix_config_cb); + if (error) { + dev_err(&client->dev, + "Failed to invoke firmware loader: %d\n", + error); + return error; + } - irq_flags = goodix_irq_flags[ts->int_trigger_type] | IRQF_ONESHOT; - error = devm_request_threaded_irq(&ts->client->dev, client->irq, - NULL, goodix_ts_irq_handler, - irq_flags, client->name, ts); - if (error) { - dev_err(&client->dev, "request IRQ failed: %d\n", error); - return error; + return 0; + } else { + error = goodix_configure_dev(ts); + if (error) + return error; } return 0; } +static int goodix_ts_remove(struct i2c_client *client) +{ + struct goodix_ts_data *ts = i2c_get_clientdata(client); + + if (ts->gpiod_int && ts->gpiod_rst) + wait_for_completion(&ts->firmware_loading_complete); + + return 0; +} + static const struct i2c_device_id goodix_ts_id[] = { { "GDIX1001:00", 0 }, { } @@ -588,6 +767,7 @@ MODULE_DEVICE_TABLE(of, goodix_of_match); static struct i2c_driver goodix_ts_driver = { .probe = goodix_ts_probe, + .remove = goodix_ts_remove, .id_table = goodix_ts_id, .driver = { .name = "Goodix-TS", -- cgit v1.1 From 5ab09d6a8f6406134085fb3f30ab61968c6f1ddf Mon Sep 17 00:00:00 2001 From: Irina Tirdea Date: Thu, 17 Dec 2015 16:43:39 -0800 Subject: Input: goodix - add power management support Implement suspend/resume for goodix driver. The suspend and resume process uses the gpio pins. If the device ACPI/DT information does not declare gpio pins, suspend/resume will not be available for these devices. This is based on Goodix datasheets for GT911 and GT9271 and on Goodix driver gt9xx.c for Android (publicly available in Android kernel trees for various devices). Signed-off-by: Octavian Purdila Signed-off-by: Irina Tirdea Tested-by: Bastien Nocera Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/goodix.c | 103 +++++++++++++++++++++++++++++++++++-- 1 file changed, 98 insertions(+), 5 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c index cd92d8f..34ed686 100644 --- a/drivers/input/touchscreen/goodix.c +++ b/drivers/input/touchscreen/goodix.c @@ -45,6 +45,7 @@ struct goodix_ts_data { u16 version; const char *cfg_name; struct completion firmware_loading_complete; + unsigned long irq_flags; }; #define GOODIX_GPIO_INT_NAME "irq" @@ -61,6 +62,9 @@ struct goodix_ts_data { #define GOODIX_CONFIG_967_LENGTH 228 /* Register defines */ +#define GOODIX_REG_COMMAND 0x8040 +#define GOODIX_CMD_SCREEN_OFF 0x05 + #define GOODIX_READ_COOR_ADDR 0x814E #define GOODIX_REG_CONFIG_DATA 0x8047 #define GOODIX_REG_ID 0x8140 @@ -162,6 +166,11 @@ static int goodix_i2c_write(struct i2c_client *client, u16 reg, const u8 *buf, return ret < 0 ? ret : (ret != 1 ? -EIO : 0); } +static int goodix_i2c_write_u8(struct i2c_client *client, u16 reg, u8 value) +{ + return goodix_i2c_write(client, reg, &value, sizeof(value)); +} + static int goodix_get_cfg_len(u16 id) { switch (id) { @@ -283,6 +292,18 @@ static irqreturn_t goodix_ts_irq_handler(int irq, void *dev_id) return IRQ_HANDLED; } +static void goodix_free_irq(struct goodix_ts_data *ts) +{ + devm_free_irq(&ts->client->dev, ts->client->irq, ts); +} + +static int goodix_request_irq(struct goodix_ts_data *ts) +{ + return devm_request_threaded_irq(&ts->client->dev, ts->client->irq, + NULL, goodix_ts_irq_handler, + ts->irq_flags, ts->client->name, ts); +} + /** * goodix_check_cfg - Checks if config fw is valid * @@ -605,7 +626,6 @@ static int goodix_request_input_dev(struct goodix_ts_data *ts) static int goodix_configure_dev(struct goodix_ts_data *ts) { int error; - unsigned long irq_flags; goodix_read_config(ts); @@ -613,10 +633,8 @@ static int goodix_configure_dev(struct goodix_ts_data *ts) if (error) return error; - irq_flags = goodix_irq_flags[ts->int_trigger_type] | IRQF_ONESHOT; - error = devm_request_threaded_irq(&ts->client->dev, ts->client->irq, - NULL, goodix_ts_irq_handler, - irq_flags, ts->client->name, ts); + ts->irq_flags = goodix_irq_flags[ts->int_trigger_type] | IRQF_ONESHOT; + error = goodix_request_irq(ts); if (error) { dev_err(&ts->client->dev, "request IRQ failed: %d\n", error); return error; @@ -737,6 +755,80 @@ static int goodix_ts_remove(struct i2c_client *client) return 0; } +static int __maybe_unused goodix_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct goodix_ts_data *ts = i2c_get_clientdata(client); + int error; + + /* We need gpio pins to suspend/resume */ + if (!ts->gpiod_int || !ts->gpiod_rst) + return 0; + + wait_for_completion(&ts->firmware_loading_complete); + + /* Free IRQ as IRQ pin is used as output in the suspend sequence */ + goodix_free_irq(ts); + + /* Output LOW on the INT pin for 5 ms */ + error = gpiod_direction_output(ts->gpiod_int, 0); + if (error) { + goodix_request_irq(ts); + return error; + } + + usleep_range(5000, 6000); + + error = goodix_i2c_write_u8(ts->client, GOODIX_REG_COMMAND, + GOODIX_CMD_SCREEN_OFF); + if (error) { + dev_err(&ts->client->dev, "Screen off command failed\n"); + gpiod_direction_input(ts->gpiod_int); + goodix_request_irq(ts); + return -EAGAIN; + } + + /* + * The datasheet specifies that the interval between sending screen-off + * command and wake-up should be longer than 58 ms. To avoid waking up + * sooner, delay 58ms here. + */ + msleep(58); + return 0; +} + +static int __maybe_unused goodix_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct goodix_ts_data *ts = i2c_get_clientdata(client); + int error; + + if (!ts->gpiod_int || !ts->gpiod_rst) + return 0; + + /* + * Exit sleep mode by outputting HIGH level to INT pin + * for 2ms~5ms. + */ + error = gpiod_direction_output(ts->gpiod_int, 1); + if (error) + return error; + + usleep_range(2000, 5000); + + error = goodix_int_sync(ts); + if (error) + return error; + + error = goodix_request_irq(ts); + if (error) + return error; + + return 0; +} + +static SIMPLE_DEV_PM_OPS(goodix_pm_ops, goodix_suspend, goodix_resume); + static const struct i2c_device_id goodix_ts_id[] = { { "GDIX1001:00", 0 }, { } @@ -773,6 +865,7 @@ static struct i2c_driver goodix_ts_driver = { .name = "Goodix-TS", .acpi_match_table = ACPI_PTR(goodix_acpi_match), .of_match_table = of_match_ptr(goodix_of_match), + .pm = &goodix_pm_ops, }, }; module_i2c_driver(goodix_ts_driver); -- cgit v1.1 From 5d655b35466835c6bb8774122db95ecb4e18888d Mon Sep 17 00:00:00 2001 From: Irina Tirdea Date: Thu, 17 Dec 2015 16:47:42 -0800 Subject: Input: goodix - use goodix_i2c_write_u8 instead of i2c_master_send Use goodix_i2c_write_u8 instead of i2c_master_send to simplify code. Signed-off-by: Irina Tirdea Tested-by: Bastien Nocera Tested-by: Aleksei Mamlin Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/goodix.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c index 34ed686..0acefe4 100644 --- a/drivers/input/touchscreen/goodix.c +++ b/drivers/input/touchscreen/goodix.c @@ -277,16 +277,11 @@ static void goodix_process_events(struct goodix_ts_data *ts) */ static irqreturn_t goodix_ts_irq_handler(int irq, void *dev_id) { - static const u8 end_cmd[] = { - GOODIX_READ_COOR_ADDR >> 8, - GOODIX_READ_COOR_ADDR & 0xff, - 0 - }; struct goodix_ts_data *ts = dev_id; goodix_process_events(ts); - if (i2c_master_send(ts->client, end_cmd, sizeof(end_cmd)) < 0) + if (goodix_i2c_write_u8(ts->client, GOODIX_READ_COOR_ADDR, 0) < 0) dev_err(&ts->client->dev, "I2C write end_cmd error\n"); return IRQ_HANDLED; -- cgit v1.1 From ad48cf5e9597147bb2bb526a6d379ee88970dec8 Mon Sep 17 00:00:00 2001 From: Karsten Merker Date: Thu, 17 Dec 2015 17:02:53 -0800 Subject: Input: goodix - add axis swapping and axis inversion support Implement support for the following device-tree and ACPI 5.1 DSD properties in the goodix touchscreen driver: - touchscreen-inverted-x: X axis is inverted (boolean) - touchscreen-inverted-y: Y axis is inverted (boolean) - touchscreen-swapped-x-y: X and Y axis are swapped (boolean) These are necessary on tablets which have a display in portrait format while the touchscreen is in landscape format, such as e.g. the MSI Primo 81. Signed-off-by: Karsten Merker Tested-by: Bastien Nocera Tested-by: Irina Tirdea (with ACPI DSD properties) Tested-by: Aleksei Mamlin (with device-tree properties) Acked-by: Rob Herring Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/goodix.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c index 0acefe4..b5e910a 100644 --- a/drivers/input/touchscreen/goodix.c +++ b/drivers/input/touchscreen/goodix.c @@ -2,6 +2,7 @@ * Driver for Goodix Touchscreens * * Copyright (c) 2014 Red Hat Inc. + * Copyright (c) 2015 K. Merker * * This code is based on gt9xx.c authored by andrew@goodix.com: * @@ -35,6 +36,9 @@ struct goodix_ts_data { struct input_dev *input_dev; int abs_x_max; int abs_y_max; + bool swapped_x_y; + bool inverted_x; + bool inverted_y; unsigned int max_touch_num; unsigned int int_trigger_type; bool rotated_screen; @@ -235,6 +239,14 @@ static void goodix_ts_report_touch(struct goodix_ts_data *ts, u8 *coor_data) input_y = ts->abs_y_max - input_y; } + /* Inversions have to happen before axis swapping */ + if (ts->inverted_x) + input_x = ts->abs_x_max - input_x; + if (ts->inverted_y) + input_y = ts->abs_y_max - input_y; + if (ts->swapped_x_y) + swap(input_x, input_y); + input_mt_slot(ts->input_dev, id); input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, true); input_report_abs(ts->input_dev, ABS_MT_POSITION_X, input_x); @@ -486,6 +498,8 @@ static void goodix_read_config(struct goodix_ts_data *ts) error); ts->abs_x_max = GOODIX_MAX_WIDTH; ts->abs_y_max = GOODIX_MAX_HEIGHT; + if (ts->swapped_x_y) + swap(ts->abs_x_max, ts->abs_y_max); ts->int_trigger_type = GOODIX_INT_TRIGGER; ts->max_touch_num = GOODIX_MAX_CONTACTS; return; @@ -493,6 +507,8 @@ static void goodix_read_config(struct goodix_ts_data *ts) ts->abs_x_max = get_unaligned_le16(&config[RESOLUTION_LOC]); ts->abs_y_max = get_unaligned_le16(&config[RESOLUTION_LOC + 2]); + if (ts->swapped_x_y) + swap(ts->abs_x_max, ts->abs_y_max); ts->int_trigger_type = config[TRIGGER_LOC] & 0x03; ts->max_touch_num = config[MAX_CONTACTS_LOC] & 0x0f; if (!ts->abs_x_max || !ts->abs_y_max || !ts->max_touch_num) { @@ -500,6 +516,8 @@ static void goodix_read_config(struct goodix_ts_data *ts) "Invalid config, using defaults\n"); ts->abs_x_max = GOODIX_MAX_WIDTH; ts->abs_y_max = GOODIX_MAX_HEIGHT; + if (ts->swapped_x_y) + swap(ts->abs_x_max, ts->abs_y_max); ts->max_touch_num = GOODIX_MAX_CONTACTS; } @@ -622,6 +640,13 @@ static int goodix_configure_dev(struct goodix_ts_data *ts) { int error; + ts->swapped_x_y = device_property_read_bool(&ts->client->dev, + "touchscreen-swapped-x-y"); + ts->inverted_x = device_property_read_bool(&ts->client->dev, + "touchscreen-inverted-x"); + ts->inverted_y = device_property_read_bool(&ts->client->dev, + "touchscreen-inverted-y"); + goodix_read_config(ts); error = goodix_request_input_dev(ts); -- cgit v1.1 From 57c80e8e5d1cddae0651e5314394e6069ebbbe3c Mon Sep 17 00:00:00 2001 From: Karsten Merker Date: Thu, 17 Dec 2015 17:08:31 -0800 Subject: Input: goodix - use "inverted_[xy]" flags instead of "rotated_screen" The goodix touchscreen driver uses a "rotated_screen" flag for systems on which the touchscreen is mounted rotated by 180 degrees with respect to the display. With the addition of support for the dt properties "touchscreen-inverted-x" and "touchscreen-inverted-y", a separate "rotated_screen" flag is not necessary anymore. This patch replaces it by setting the inverted_x and inverted_y flags instead. Signed-off-by: Karsten Merker Reviewed-by: Irina Tirdea Tested-by: Bastien Nocera Acked-by: Bastien Nocera Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/goodix.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c index b5e910a..240b16f 100644 --- a/drivers/input/touchscreen/goodix.c +++ b/drivers/input/touchscreen/goodix.c @@ -41,7 +41,6 @@ struct goodix_ts_data { bool inverted_y; unsigned int max_touch_num; unsigned int int_trigger_type; - bool rotated_screen; int cfg_len; struct gpio_desc *gpiod_int; struct gpio_desc *gpiod_rst; @@ -234,11 +233,6 @@ static void goodix_ts_report_touch(struct goodix_ts_data *ts, u8 *coor_data) int input_y = get_unaligned_le16(&coor_data[3]); int input_w = get_unaligned_le16(&coor_data[5]); - if (ts->rotated_screen) { - input_x = ts->abs_x_max - input_x; - input_y = ts->abs_y_max - input_y; - } - /* Inversions have to happen before axis swapping */ if (ts->inverted_x) input_x = ts->abs_x_max - input_x; @@ -521,10 +515,12 @@ static void goodix_read_config(struct goodix_ts_data *ts) ts->max_touch_num = GOODIX_MAX_CONTACTS; } - ts->rotated_screen = dmi_check_system(rotated_screen); - if (ts->rotated_screen) + if (dmi_check_system(rotated_screen)) { + ts->inverted_x = true; + ts->inverted_y = true; dev_dbg(&ts->client->dev, "Applying '180 degrees rotated screen' quirk\n"); + } } /** -- cgit v1.1 From 052876f8e5aec887d22c4d06e54aa5531ffcec75 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Fri, 18 Dec 2015 17:20:09 -0800 Subject: Input: uinput - add new UINPUT_DEV_SETUP and UI_ABS_SETUP ioctl This adds two new ioctls, UINPUT_DEV_SETUP and UI_ABS_SETUP, that replaces the old device setup method (by write()'ing "struct uinput_user_dev" to the node). The old method is not easily extendable and requires huge payloads. Furthermore, overloading write() without properly versioned objects is error-prone. Therefore, we introduce two new ioctls to replace the old method. These ioctls support all features of the old method, plus a "resolution" field for absinfo. Furthermore, it's properly forward-compatible to new ABS codes and a growing "struct input_absinfo" structure. UI_ABS_SETUP also allows user-space to skip unknown axes if not set. There is no need to copy the whole array temporarily into the kernel, but instead the caller issues several ioctl where we copy each value manually. Signed-off-by: David Herrmann Signed-off-by: Benjamin Tissoires Reviewed-by: David Herrmann Signed-off-by: Dmitry Torokhov --- drivers/input/misc/uinput.c | 86 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 83 insertions(+), 3 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c index 5adbced..a16fc4a 100644 --- a/drivers/input/misc/uinput.c +++ b/drivers/input/misc/uinput.c @@ -370,8 +370,78 @@ static int uinput_allocate_device(struct uinput_device *udev) return 0; } -static int uinput_setup_device(struct uinput_device *udev, - const char __user *buffer, size_t count) +static int uinput_dev_setup(struct uinput_device *udev, + struct uinput_setup __user *arg) +{ + struct uinput_setup setup; + struct input_dev *dev; + int retval; + + if (udev->state == UIST_CREATED) + return -EINVAL; + + if (copy_from_user(&setup, arg, sizeof(setup))) + return -EFAULT; + + if (!setup.name[0]) + return -EINVAL; + + dev = udev->dev; + dev->id = setup.id; + udev->ff_effects_max = setup.ff_effects_max; + + kfree(dev->name); + dev->name = kstrndup(setup.name, UINPUT_MAX_NAME_SIZE, GFP_KERNEL); + if (!dev->name) + return -ENOMEM; + + retval = uinput_validate_absbits(dev); + if (retval < 0) + return retval; + + udev->state = UIST_SETUP_COMPLETE; + return 0; +} + +static int uinput_abs_setup(struct uinput_device *udev, + struct uinput_setup __user *arg, size_t size) +{ + struct uinput_abs_setup setup = {}; + struct input_dev *dev; + + if (size > sizeof(setup)) + return -E2BIG; + + if (udev->state == UIST_CREATED) + return -EINVAL; + + if (copy_from_user(&setup, arg, size)) + return -EFAULT; + + if (setup.code > ABS_MAX) + return -ERANGE; + + dev = udev->dev; + + input_alloc_absinfo(dev); + if (!dev->absinfo) + return -ENOMEM; + + set_bit(setup.code, dev->absbit); + dev->absinfo[setup.code] = setup.absinfo; + + /* + * We restore the state to UIST_NEW_DEVICE because the user has to call + * UI_DEV_SETUP in the last place before UI_DEV_CREATE to check the + * validity of the absbits. + */ + udev->state = UIST_NEW_DEVICE; + return 0; +} + +/* legacy setup via write() */ +static int uinput_setup_device_legacy(struct uinput_device *udev, + const char __user *buffer, size_t count) { struct uinput_user_dev *user_dev; struct input_dev *dev; @@ -474,7 +544,7 @@ static ssize_t uinput_write(struct file *file, const char __user *buffer, retval = udev->state == UIST_CREATED ? uinput_inject_events(udev, buffer, count) : - uinput_setup_device(udev, buffer, count); + uinput_setup_device_legacy(udev, buffer, count); mutex_unlock(&udev->mutex); @@ -735,6 +805,12 @@ static long uinput_ioctl_handler(struct file *file, unsigned int cmd, uinput_destroy_device(udev); goto out; + case UI_DEV_SETUP: + retval = uinput_dev_setup(udev, p); + goto out; + + /* UI_ABS_SETUP is handled in the variable size ioctls */ + case UI_SET_EVBIT: retval = uinput_set_bit(arg, evbit, EV_MAX); goto out; @@ -879,6 +955,10 @@ static long uinput_ioctl_handler(struct file *file, unsigned int cmd, name = dev_name(&udev->dev->dev); retval = uinput_str_to_user(p, name, size); goto out; + + case UI_ABS_SETUP & ~IOCSIZE_MASK: + retval = uinput_abs_setup(udev, p, size); + goto out; } retval = -EINVAL; -- cgit v1.1 From fbae10db094046dba1d59e1c2ee5140835045f14 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 25 Oct 2015 10:34:13 +0100 Subject: Input: uinput - rework ABS validation Rework the uinput ABS validation to check passed absinfo data immediately, but do ABS initialization as last step in UI_DEV_CREATE. The behavior observed by user-space is not changed, as ABS initialization was never checked for errors. With this in place, the order of device initialization and abs configuration is no longer fixed. Userspace can initialize the device and afterwards set absinfo just fine. Signed-off-by: David Herrmann Reviewed-by: Benjamin Tissoires Tested-by: Benjamin Tissoires Signed-off-by: Dmitry Torokhov --- drivers/input/misc/uinput.c | 89 +++++++++++++++++++++++---------------------- 1 file changed, 45 insertions(+), 44 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c index a16fc4a..782df41 100644 --- a/drivers/input/misc/uinput.c +++ b/drivers/input/misc/uinput.c @@ -256,13 +256,22 @@ static void uinput_destroy_device(struct uinput_device *udev) static int uinput_create_device(struct uinput_device *udev) { struct input_dev *dev = udev->dev; - int error; + int error, nslot; if (udev->state != UIST_SETUP_COMPLETE) { printk(KERN_DEBUG "%s: write device info first\n", UINPUT_NAME); return -EINVAL; } + if (test_bit(ABS_MT_SLOT, dev->absbit)) { + nslot = input_abs_get_max(dev, ABS_MT_SLOT) + 1; + error = input_mt_init_slots(dev, nslot, 0); + if (error) + goto fail1; + } else if (test_bit(ABS_MT_POSITION_X, dev->absbit)) { + input_set_events_per_packet(dev, 60); + } + if (udev->ff_effects_max) { error = input_ff_create(dev, udev->ff_effects_max); if (error) @@ -308,10 +317,35 @@ static int uinput_open(struct inode *inode, struct file *file) return 0; } +static int uinput_validate_absinfo(struct input_dev *dev, unsigned int code, + const struct input_absinfo *abs) +{ + int min, max; + + min = abs->minimum; + max = abs->maximum; + + if ((min != 0 || max != 0) && max <= min) { + printk(KERN_DEBUG + "%s: invalid abs[%02x] min:%d max:%d\n", + UINPUT_NAME, code, min, max); + return -EINVAL; + } + + if (abs->flat > max - min) { + printk(KERN_DEBUG + "%s: abs_flat #%02x out of range: %d (min:%d/max:%d)\n", + UINPUT_NAME, code, abs->flat, min, max); + return -EINVAL; + } + + return 0; +} + static int uinput_validate_absbits(struct input_dev *dev) { unsigned int cnt; - int nslot; + int error; if (!test_bit(EV_ABS, dev->evbit)) return 0; @@ -321,38 +355,12 @@ static int uinput_validate_absbits(struct input_dev *dev) */ for_each_set_bit(cnt, dev->absbit, ABS_CNT) { - int min, max; - - min = input_abs_get_min(dev, cnt); - max = input_abs_get_max(dev, cnt); - - if ((min != 0 || max != 0) && max <= min) { - printk(KERN_DEBUG - "%s: invalid abs[%02x] min:%d max:%d\n", - UINPUT_NAME, cnt, - input_abs_get_min(dev, cnt), - input_abs_get_max(dev, cnt)); + if (!dev->absinfo) return -EINVAL; - } - - if (input_abs_get_flat(dev, cnt) > - input_abs_get_max(dev, cnt) - input_abs_get_min(dev, cnt)) { - printk(KERN_DEBUG - "%s: abs_flat #%02x out of range: %d " - "(min:%d/max:%d)\n", - UINPUT_NAME, cnt, - input_abs_get_flat(dev, cnt), - input_abs_get_min(dev, cnt), - input_abs_get_max(dev, cnt)); - return -EINVAL; - } - } - if (test_bit(ABS_MT_SLOT, dev->absbit)) { - nslot = input_abs_get_max(dev, ABS_MT_SLOT) + 1; - input_mt_init_slots(dev, nslot, 0); - } else if (test_bit(ABS_MT_POSITION_X, dev->absbit)) { - input_set_events_per_packet(dev, 60); + error = uinput_validate_absinfo(dev, cnt, &dev->absinfo[cnt]); + if (error) + return error; } return 0; @@ -375,7 +383,6 @@ static int uinput_dev_setup(struct uinput_device *udev, { struct uinput_setup setup; struct input_dev *dev; - int retval; if (udev->state == UIST_CREATED) return -EINVAL; @@ -395,10 +402,6 @@ static int uinput_dev_setup(struct uinput_device *udev, if (!dev->name) return -ENOMEM; - retval = uinput_validate_absbits(dev); - if (retval < 0) - return retval; - udev->state = UIST_SETUP_COMPLETE; return 0; } @@ -408,6 +411,7 @@ static int uinput_abs_setup(struct uinput_device *udev, { struct uinput_abs_setup setup = {}; struct input_dev *dev; + int error; if (size > sizeof(setup)) return -E2BIG; @@ -423,19 +427,16 @@ static int uinput_abs_setup(struct uinput_device *udev, dev = udev->dev; + error = uinput_validate_absinfo(dev, setup.code, &setup.absinfo); + if (error) + return error; + input_alloc_absinfo(dev); if (!dev->absinfo) return -ENOMEM; set_bit(setup.code, dev->absbit); dev->absinfo[setup.code] = setup.absinfo; - - /* - * We restore the state to UIST_NEW_DEVICE because the user has to call - * UI_DEV_SETUP in the last place before UI_DEV_CREATE to check the - * validity of the absbits. - */ - udev->state = UIST_NEW_DEVICE; return 0; } -- cgit v1.1 From daf6cd0c1829c48cba197bd87d57fc8bf3f65faa Mon Sep 17 00:00:00 2001 From: Elias Vanderstuyft Date: Fri, 18 Dec 2015 17:32:19 -0800 Subject: Input: uinput - sanity check on ff_effects_max and EV_FF Currently the user can set ff_effects_max to zero with the EV_FF bit (and the FF_GAIN and/or FF_AUTOCENTER bits) set, in this case the uninitialized methods ff->set_gain and/or ff->set_autocenter can be dereferenced, resulting in a kernel oops. Check in uinput_create_device() and print a helpful message and return -EINVAL in case the check fails. Signed-off-by: Elias Vanderstuyft Signed-off-by: Dmitry Torokhov --- drivers/input/misc/uinput.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers/input') diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c index 782df41..4eb9e4d 100644 --- a/drivers/input/misc/uinput.c +++ b/drivers/input/misc/uinput.c @@ -272,6 +272,13 @@ static int uinput_create_device(struct uinput_device *udev) input_set_events_per_packet(dev, 60); } + if (test_bit(EV_FF, dev->evbit) && !udev->ff_effects_max) { + printk(KERN_DEBUG "%s: ff_effects_max should be non-zero when FF_BIT is set\n", + UINPUT_NAME); + error = -EINVAL; + goto fail1; + } + if (udev->ff_effects_max) { error = input_ff_create(dev, udev->ff_effects_max); if (error) -- cgit v1.1 From 8dcb3c7628f19192dd568fbee9094a2d4b14b6af Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Sat, 19 Dec 2015 09:22:21 -0800 Subject: Input: egalax_ts_serial - fix potential NULL dereference on error We didn't check input_allocate_device() for failures so it could lead to a NULL deref. Fixes: 6b0f8f9c52ef ('Input: add eGalaxTouch serial touchscreen driver') Signed-off-by: Dan Carpenter Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/egalax_ts_serial.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/egalax_ts_serial.c b/drivers/input/touchscreen/egalax_ts_serial.c index a078c1c..657bbae 100644 --- a/drivers/input/touchscreen/egalax_ts_serial.c +++ b/drivers/input/touchscreen/egalax_ts_serial.c @@ -105,7 +105,7 @@ static int egalax_connect(struct serio *serio, struct serio_driver *drv) egalax = kzalloc(sizeof(struct egalax), GFP_KERNEL); input_dev = input_allocate_device(); - if (!egalax) { + if (!egalax || !input_dev) { error = -ENOMEM; goto err_free_mem; } -- cgit v1.1 From 7ed5ff82c2f06965652f8d1a17c427ba8d363b92 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sat, 2 Jan 2016 21:04:30 -0800 Subject: Input: bma150 - constify bma150_cfg structure The bma150_cfg structure is never modified, so declare it as const. Done with the help of Coccinelle. Signed-off-by: Julia Lawall Signed-off-by: Dmitry Torokhov --- drivers/input/misc/bma150.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/misc/bma150.c b/drivers/input/misc/bma150.c index 1d0e61d..b0d4453 100644 --- a/drivers/input/misc/bma150.c +++ b/drivers/input/misc/bma150.c @@ -147,7 +147,7 @@ struct bma150_data { * are stated and verified by Bosch Sensortec where they are configured * to provide a generic sensitivity performance. */ -static struct bma150_cfg default_cfg = { +static const struct bma150_cfg default_cfg = { .any_motion_int = 1, .hg_int = 1, .lg_int = 1, -- cgit v1.1 From fa68e2777cbc410fafb4e45e92222a63f6e60e57 Mon Sep 17 00:00:00 2001 From: Geliang Tang Date: Sat, 2 Jan 2016 21:06:53 -0800 Subject: Input: pcap_ts - use to_delayed_work Use to_delayed_work() instead of open-coding it. Signed-off-by: Geliang Tang Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/pcap_ts.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/pcap_ts.c b/drivers/input/touchscreen/pcap_ts.c index 23a354a..0e3fc41 100644 --- a/drivers/input/touchscreen/pcap_ts.c +++ b/drivers/input/touchscreen/pcap_ts.c @@ -87,7 +87,7 @@ static void pcap_ts_read_xy(void *data, u16 res[2]) static void pcap_ts_work(struct work_struct *work) { - struct delayed_work *dw = container_of(work, struct delayed_work, work); + struct delayed_work *dw = to_delayed_work(work); struct pcap_ts *pcap_ts = container_of(dw, struct pcap_ts, work); u8 ch[2]; -- cgit v1.1 From dae928ec3c29e7e16723c9c4c1299e00f4e9e949 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pali=20Roh=C3=A1r?= Date: Wed, 6 Jan 2016 15:50:08 -0800 Subject: Input: ALPS - detect trackstick presence for v7 protocol MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds detection of trackstick for v7 protocol devices. Code in this patch is used in official Dell touchpad linux drivers for Dell models: Dell Latitude E5250/5250, E5450/5450, E5550/5550 Detection code and base reg for alps v3 rushmore and v7 devices is exacly same. Also user in bug https://bugzilla.kernel.org/show_bug.cgi?id=94801 reported that Toshiba Sattellite Z30-A-1DG has only alps v7 touchpad device without trackstick and kernel reports to userspace also redundant trackstick device. Signed-off-by: Pali Rohár Reviewed-by: Hans de Goede Tested-by: Alex Hung Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/alps.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index 41e6cb5..1aa1709 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -31,6 +31,7 @@ #define ALPS_CMD_NIBBLE_10 0x01f2 #define ALPS_REG_BASE_RUSHMORE 0xc2c0 +#define ALPS_REG_BASE_V7 0xc2c0 #define ALPS_REG_BASE_PINNACLE 0x0000 static const struct alps_nibble_commands alps_v3_nibble_commands[] = { @@ -2047,7 +2048,7 @@ static int alps_absolute_mode_v3(struct psmouse *psmouse) return 0; } -static int alps_probe_trackstick_v3(struct psmouse *psmouse, int reg_base) +static int alps_probe_trackstick_v3_v7(struct psmouse *psmouse, int reg_base) { int ret = -EIO, reg_val; @@ -2132,7 +2133,7 @@ static int alps_hw_init_v3(struct psmouse *psmouse) int reg_val; unsigned char param[4]; - reg_val = alps_probe_trackstick_v3(psmouse, ALPS_REG_BASE_PINNACLE); + reg_val = alps_probe_trackstick_v3_v7(psmouse, ALPS_REG_BASE_PINNACLE); if (reg_val == -EIO) goto error; @@ -2625,8 +2626,8 @@ static int alps_set_protocol(struct psmouse *psmouse, priv->x_bits = 16; priv->y_bits = 12; - if (alps_probe_trackstick_v3(psmouse, - ALPS_REG_BASE_RUSHMORE) < 0) + if (alps_probe_trackstick_v3_v7(psmouse, + ALPS_REG_BASE_RUSHMORE) < 0) priv->flags &= ~ALPS_DUALPOINT; break; @@ -2676,6 +2677,9 @@ static int alps_set_protocol(struct psmouse *psmouse, if (priv->fw_ver[1] != 0xba) priv->flags |= ALPS_BUTTONPAD; + if (alps_probe_trackstick_v3_v7(psmouse, ALPS_REG_BASE_V7) < 0) + priv->flags &= ~ALPS_DUALPOINT; + break; case ALPS_PROTO_V8: -- cgit v1.1 From 4b1af853646ca893fa686701d1605998fffcc82c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pali=20Roh=C3=A1r?= Date: Wed, 6 Jan 2016 15:58:56 -0800 Subject: Input: ALPS - report v3 pinnacle trackstick device only if is present MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch moves v3 pinnacle code for trackstick detection from alps_hw_init_v3() to alps_set_protocol() so ALPS_DUALPOINT flag can be cleared before registering trackstick input device in kernel. Signed-off-by: Pali Rohár Acked-by: Hans de Goede Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/alps.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index 1aa1709..936f07a 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -2129,15 +2129,12 @@ error: static int alps_hw_init_v3(struct psmouse *psmouse) { + struct alps_data *priv = psmouse->private; struct ps2dev *ps2dev = &psmouse->ps2dev; int reg_val; unsigned char param[4]; - reg_val = alps_probe_trackstick_v3_v7(psmouse, ALPS_REG_BASE_PINNACLE); - if (reg_val == -EIO) - goto error; - - if (reg_val == 0 && + if ((priv->flags & ALPS_DUALPOINT) && alps_setup_trackstick_v3(psmouse, ALPS_REG_BASE_PINNACLE) == -EIO) goto error; @@ -2614,6 +2611,11 @@ static int alps_set_protocol(struct psmouse *psmouse, priv->decode_fields = alps_decode_pinnacle; priv->nibble_commands = alps_v3_nibble_commands; priv->addr_command = PSMOUSE_CMD_RESET_WRAP; + + if (alps_probe_trackstick_v3_v7(psmouse, + ALPS_REG_BASE_PINNACLE) < 0) + priv->flags &= ~ALPS_DUALPOINT; + break; case ALPS_PROTO_V3_RUSHMORE: -- cgit v1.1 From 415a249f88fd263bed0f7cca86ecde8c57aba9dc Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 6 Jan 2016 14:44:02 -0800 Subject: Input: rohm_bu21023 - fix handling of retrying firmware update Because of the wrong condition we'd never retry firmware update. Acked-by: Yoichi Yuasa Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/rohm_bu21023.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/rohm_bu21023.c b/drivers/input/touchscreen/rohm_bu21023.c index ba6024f..611156a 100644 --- a/drivers/input/touchscreen/rohm_bu21023.c +++ b/drivers/input/touchscreen/rohm_bu21023.c @@ -725,7 +725,7 @@ static int rohm_ts_load_firmware(struct i2c_client *client, break; error = -EIO; - } while (++retry >= FIRMWARE_RETRY_MAX); + } while (++retry <= FIRMWARE_RETRY_MAX); out: error2 = i2c_smbus_write_byte_data(client, INT_MASK, INT_ALL); -- cgit v1.1 From 7ae6cfe8d24681bea5a4a4b7a04915ac08c09ba5 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 7 Jan 2016 09:55:14 -0800 Subject: Input: omap-keypad - set tasklet data earlier It feels like we should set the tasklet data before enabling it. Signed-off-by: Dan Carpenter Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/omap-keypad.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/keyboard/omap-keypad.c b/drivers/input/keyboard/omap-keypad.c index 7502e46..75ad666 100644 --- a/drivers/input/keyboard/omap-keypad.c +++ b/drivers/input/keyboard/omap-keypad.c @@ -292,8 +292,8 @@ static int omap_kp_probe(struct platform_device *pdev) setup_timer(&omap_kp->timer, omap_kp_timer, (unsigned long)omap_kp); /* get the irq and init timer*/ - tasklet_enable(&kp_tasklet); kp_tasklet.data = (unsigned long) omap_kp; + tasklet_enable(&kp_tasklet); ret = device_create_file(&pdev->dev, &dev_attr_enable); if (ret < 0) -- cgit v1.1 From 3b7474ec0d7150044f91e3af460067f79d466522 Mon Sep 17 00:00:00 2001 From: Dave Gerlach Date: Thu, 7 Jan 2016 11:13:34 -0800 Subject: Input: ti_am335x_tsc - fix HWPEN interrupt handling Remove write to REG_IRQCLR and REG_IRQWAKEUP in interrupt handler for IRQENB_HW_PEN as the resume handler should and does clear REG_IRQWAKEUP. IRQENB_HW_PEN bit is set in irqclr so that all interrupts get cleared later so let IRQENB_HW_PEN be cleared by that. Without this patch wakeup events from TSC_ADC do not work because pending interrupts in TSC_ADC were causing HW_PEN interrupt, needed for wake from suspend modes, to get disabled immediately by IRQ handler after being enabled and preventing wake from happening. Signed-off-by: Dave Gerlach Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/ti_am335x_tsc.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/ti_am335x_tsc.c b/drivers/input/touchscreen/ti_am335x_tsc.c index 191a1b8..a21a07c 100644 --- a/drivers/input/touchscreen/ti_am335x_tsc.c +++ b/drivers/input/touchscreen/ti_am335x_tsc.c @@ -273,8 +273,6 @@ static irqreturn_t titsc_irq(int irq, void *dev) status = titsc_readl(ts_dev, REG_RAWIRQSTATUS); if (status & IRQENB_HW_PEN) { ts_dev->pen_down = true; - titsc_writel(ts_dev, REG_IRQWAKEUP, 0x00); - titsc_writel(ts_dev, REG_IRQCLR, IRQENB_HW_PEN); irqclr |= IRQENB_HW_PEN; } -- cgit v1.1 From 35f8679f577ae5673a778598bcbe7b45cbec8923 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 7 Jan 2016 15:06:51 -0800 Subject: Input: omap-keypad - remove dead check Commit da1f026b532ce944d74461497dc6d8c16456466e ("Keyboard: omap-keypad: use matrix_keypad.h") switched the driver to use matrix keypad infrastructure, which made array of keycodes to be unsigned short, and caused the test for negativity never trigger. This leads to the following static checker warning: drivers/input/keyboard/omap-keypad.c:158 omap_kp_tasklet() warn: 'keycodes[]' is never negative. Given that we did not care about this check for a few years already let's simply remove it. Reported-by: Dan Carpenter Acked-by: Aaro Koskinen Acked-by: Tony Lindgren Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/omap-keypad.c | 8 -------- 1 file changed, 8 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/keyboard/omap-keypad.c b/drivers/input/keyboard/omap-keypad.c index 75ad666..e0d72c8 100644 --- a/drivers/input/keyboard/omap-keypad.c +++ b/drivers/input/keyboard/omap-keypad.c @@ -155,14 +155,6 @@ static void omap_kp_tasklet(unsigned long data) "pressed" : "released"); #else key = keycodes[MATRIX_SCAN_CODE(row, col, row_shift)]; - if (key < 0) { - printk(KERN_WARNING - "omap-keypad: Spurious key event %d-%d\n", - col, row); - /* We scan again after a couple of seconds */ - spurious = 1; - continue; - } if (!(kp_cur_group == (key & GROUP_MASK) || kp_cur_group == -1)) -- cgit v1.1 From 8679ee4204cfd5cf78b996508ccadc1ec6130f1a Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 6 Jan 2016 14:20:07 -0800 Subject: Input: gpio-keys - fix check for disabling unsupported keys Commit 4ea14a53d8f881034fa9e186653821c4e3d9a8fb ("Input: gpio-keys - report error when disabling unsupported key") tried let user know that they attempted to disable an unsupported key, unfortunately the check is wrong as it believes that all codes are invalid. Fix it by ensuring that keys that we try to disable are subset of keys (or switches) that device reports. Fixes: 4ea14a53d8f8 ("Input: gpio-keys - report error when disabling unsupported key") Reported-by: Ivaylo Dimitrov Tested-by: Ivaylo Dimitrov Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/gpio_keys.c | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c index bef317f..b9f01bd 100644 --- a/drivers/input/keyboard/gpio_keys.c +++ b/drivers/input/keyboard/gpio_keys.c @@ -96,7 +96,7 @@ struct gpio_keys_drvdata { * Return value of this function can be used to allocate bitmap * large enough to hold all bits for given type. */ -static inline int get_n_events_by_type(int type) +static int get_n_events_by_type(int type) { BUG_ON(type != EV_SW && type != EV_KEY); @@ -104,6 +104,22 @@ static inline int get_n_events_by_type(int type) } /** + * get_bm_events_by_type() - returns bitmap of supported events per @type + * @input: input device from which bitmap is retrieved + * @type: type of button (%EV_KEY, %EV_SW) + * + * Return value of this function can be used to allocate bitmap + * large enough to hold all bits for given type. + */ +static const unsigned long *get_bm_events_by_type(struct input_dev *dev, + int type) +{ + BUG_ON(type != EV_SW && type != EV_KEY); + + return (type == EV_KEY) ? dev->keybit : dev->swbit; +} + +/** * gpio_keys_disable_button() - disables given GPIO button * @bdata: button data for button to be disabled * @@ -213,6 +229,7 @@ static ssize_t gpio_keys_attr_store_helper(struct gpio_keys_drvdata *ddata, const char *buf, unsigned int type) { int n_events = get_n_events_by_type(type); + const unsigned long *bitmap = get_bm_events_by_type(ddata->input, type); unsigned long *bits; ssize_t error; int i; @@ -226,6 +243,11 @@ static ssize_t gpio_keys_attr_store_helper(struct gpio_keys_drvdata *ddata, goto out; /* First validate */ + if (!bitmap_subset(bits, bitmap, n_events)) { + error = -EINVAL; + goto out; + } + for (i = 0; i < ddata->pdata->nbuttons; i++) { struct gpio_button_data *bdata = &ddata->data[i]; @@ -239,11 +261,6 @@ static ssize_t gpio_keys_attr_store_helper(struct gpio_keys_drvdata *ddata, } } - if (i == ddata->pdata->nbuttons) { - error = -EINVAL; - goto out; - } - mutex_lock(&ddata->disable_lock); for (i = 0; i < ddata->pdata->nbuttons; i++) { -- cgit v1.1 From 5f7e5445a2de848c66d2d80ba5479197e8287c33 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 11 Jan 2016 00:08:58 -0800 Subject: Input: wacom_w8001 - drop use of ABS_MT_TOOL_TYPE As of e0361b70175 ("Input: wacom_w8001 - split the touch and pen devices into two devices") the touch events aren't multiplexed over the same device anymore, the use of ABS_MT_TOOL_TYPE is superfluous. And even before then it only ever sent MT_TOOL_TYPE_FINGER anyway. Signed-off-by: Peter Hutterer Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/wacom_w8001.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/wacom_w8001.c b/drivers/input/touchscreen/wacom_w8001.c index fe983e7..bab3c6acf 100644 --- a/drivers/input/touchscreen/wacom_w8001.c +++ b/drivers/input/touchscreen/wacom_w8001.c @@ -155,7 +155,6 @@ static void parse_multi_touch(struct w8001 *w8001) bool touch = data[0] & (1 << i); input_mt_slot(dev, i); - input_mt_report_slot_state(dev, MT_TOOL_FINGER, touch); if (touch) { x = (data[6 * i + 1] << 7) | data[6 * i + 2]; y = (data[6 * i + 3] << 7) | data[6 * i + 4]; @@ -514,8 +513,6 @@ static int w8001_setup_touch(struct w8001 *w8001, char *basename, 0, touch.x, 0, 0); input_set_abs_params(dev, ABS_MT_POSITION_Y, 0, touch.y, 0, 0); - input_set_abs_params(dev, ABS_MT_TOOL_TYPE, - 0, MT_TOOL_MAX, 0, 0); strlcat(basename, " 2FG", basename_sz); if (w8001->max_pen_x && w8001->max_pen_y) -- cgit v1.1 From 6544a1df11c48c8413071aac3316792e4678fbfb Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Mon, 11 Jan 2016 17:35:38 -0800 Subject: Input: elantech - mark protocols v2 and v3 as semi-mt When using a protocol v2 or v3 hardware, elantech uses the function elantech_report_semi_mt_data() to report data. This devices are rather creepy because if num_finger is 3, (x2,y2) is (0,0). Yes, only one valid touch is reported. Anyway, userspace (libinput) is now confused by these (0,0) touches, and detect them as palm, and rejects them. Commit 3c0213d17a09 ("Input: elantech - fix semi-mt protocol for v3 HW") was sufficient enough for xf86-input-synaptics and libinput before it has palm rejection. Now we need to actually tell libinput that this device is a semi-mt one and it should not rely on the actual values of the 2 touches. Cc: stable@vger.kernel.org Signed-off-by: Benjamin Tissoires Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/elantech.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c index 537ebb0..78f93cf 100644 --- a/drivers/input/mouse/elantech.c +++ b/drivers/input/mouse/elantech.c @@ -1222,7 +1222,7 @@ static int elantech_set_input_params(struct psmouse *psmouse) input_set_abs_params(dev, ABS_TOOL_WIDTH, ETP_WMIN_V2, ETP_WMAX_V2, 0, 0); } - input_mt_init_slots(dev, 2, 0); + input_mt_init_slots(dev, 2, INPUT_MT_SEMI_MT); input_set_abs_params(dev, ABS_MT_POSITION_X, x_min, x_max, 0, 0); input_set_abs_params(dev, ABS_MT_POSITION_Y, y_min, y_max, 0, 0); break; -- cgit v1.1