Message ID | 20201029180448.e3aadaf2422d58470a9d0fcc@uvos.xyz (mailing list archive) |
---|---|
State | Superseded |
Headers | show |
Series | [v2] Drivers: input: misc: Add driver touchscreen-buttons to support physically labeled buttons on touch screen surfaces | expand |
Hi Carl, Thank you for the patch! Perhaps something to improve: [auto build test WARNING on input/next] [also build test WARNING on v5.10-rc2 next-20201102] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch] url: https://github.com/0day-ci/linux/commits/Carl-Philipp-Klemm/Drivers-input-misc-Add-driver-touchscreen-buttons-to-support-physically-labeled-buttons-on-touch-screen-surfaces/20201030-010622 base: https://git.kernel.org/pub/scm/linux/kernel/git/dtor/input.git next config: mips-allyesconfig (attached as .config) compiler: mips-linux-gcc (GCC) 9.3.0 reproduce (this is a W=1 build): wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross # https://github.com/0day-ci/linux/commit/00fd93a32ffbfc2ce311ed11e49a596eae970933 git remote add linux-review https://github.com/0day-ci/linux git fetch --no-tags linux-review Carl-Philipp-Klemm/Drivers-input-misc-Add-driver-touchscreen-buttons-to-support-physically-labeled-buttons-on-touch-screen-surfaces/20201030-010622 git checkout 00fd93a32ffbfc2ce311ed11e49a596eae970933 # save the attached .config to linux build tree COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-9.3.0 make.cross ARCH=mips If you fix the issue, kindly add following tag as appropriate Reported-by: kernel test robot <lkp@intel.com> All warnings (new ones prefixed by >>): drivers/input/misc/touchscreen-buttons.c: In function 'touchscreen_buttons_input_event': drivers/input/misc/touchscreen-buttons.c:179:85: warning: comparison of unsigned expression >= 0 is always true [-Wtype-limits] 179 | } else if (buttons->queue.lastindex < EVENT_QUEUE_SIZE && buttons->queue.lastindex >= 0) { | ^~ drivers/input/misc/touchscreen-buttons.c: At top level: >> drivers/input/misc/touchscreen-buttons.c:230:6: warning: no previous prototype for 'merge_task_handler' [-Wmissing-prototypes] 230 | void merge_task_handler(struct work_struct *work) | ^~~~~~~~~~~~~~~~~~ >> drivers/input/misc/touchscreen-buttons.c:240:6: warning: no previous prototype for 'close_task_handler' [-Wmissing-prototypes] 240 | void close_task_handler(struct work_struct *work) | ^~~~~~~~~~~~~~~~~~ >> drivers/input/misc/touchscreen-buttons.c:250:6: warning: no previous prototype for 'open_task_handler' [-Wmissing-prototypes] 250 | void open_task_handler(struct work_struct *work) | ^~~~~~~~~~~~~~~~~ >> drivers/input/misc/touchscreen-buttons.c:382:5: warning: no previous prototype for 'touchscreen_buttons_idev_opened' [-Wmissing-prototypes] 382 | int touchscreen_buttons_idev_opened(struct input_dev *idev) | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ >> drivers/input/misc/touchscreen-buttons.c:405:6: warning: no previous prototype for 'touchscreen_buttons_idev_closed' [-Wmissing-prototypes] 405 | void touchscreen_buttons_idev_closed(struct input_dev *idev) | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ vim +/merge_task_handler +230 drivers/input/misc/touchscreen-buttons.c 229 > 230 void merge_task_handler(struct work_struct *work) 231 { 232 struct touchscreen_buttons *buttons = container_of(work, struct touchscreen_buttons, merge_task); 233 234 mutex_lock(&buttons->mutex); 235 if (buttons->ts_handle && buttons->ts_handle->dev) 236 touchscreen_buttons_merge_capabilitys(buttons->filtered_ts_idev, buttons->ts_handle->dev); 237 mutex_unlock(&buttons->mutex); 238 } 239 > 240 void close_task_handler(struct work_struct *work) 241 { 242 struct touchscreen_buttons *buttons = container_of(work, struct touchscreen_buttons, close_task); 243 244 mutex_lock(&buttons->mutex); 245 if (buttons && buttons->ts_handle && buttons->ts_handle->open != 0) 246 input_close_device(buttons->ts_handle); 247 mutex_unlock(&buttons->mutex); 248 } 249 > 250 void open_task_handler(struct work_struct *work) 251 { 252 struct touchscreen_buttons *buttons = container_of(work, struct touchscreen_buttons, open_task); 253 int error; 254 255 mutex_lock(&buttons->mutex); 256 if (buttons && buttons->ts_handle) { 257 error = input_open_device(buttons->ts_handle); 258 if (error) { 259 dev_err(buttons->dev, "Failed to open input device, error %d\n", error); 260 input_unregister_handle(buttons->ts_handle); 261 kfree(buttons->ts_handle); 262 buttons->ts_handle = NULL; 263 } 264 } 265 mutex_unlock(&buttons->mutex); 266 } 267 268 static int touchscreen_buttons_input_connect(struct input_handler *handler, 269 struct input_dev *dev, const struct input_device_id *id) 270 { 271 struct touchscreen_buttons *buttons; 272 273 buttons = handler->private; 274 275 mutex_lock(&buttons->mutex); 276 277 if ((!buttons->ts_handle && device_match_of_node(&dev->dev, buttons->map->ts_node)) || 278 (dev->dev.parent && device_match_of_node(dev->dev.parent, buttons->map->ts_node))) { 279 int error; 280 281 dev_info(buttons->dev, "Binding to device: %s\n", dev_name(&dev->dev)); 282 283 buttons->ts_handle = kzalloc(sizeof(*buttons->ts_handle), GFP_KERNEL); 284 if (!buttons->ts_handle) { 285 mutex_unlock(&buttons->mutex); 286 return -ENOMEM; 287 } 288 289 buttons->ts_handle->dev = dev; 290 buttons->ts_handle->handler = handler; 291 buttons->ts_handle->name = "touchscreen-buttons"; 292 buttons->ts_handle->private = handler->private; 293 buttons->queue.lastindex = 0; 294 295 error = input_register_handle(buttons->ts_handle); 296 if (error) { 297 dev_err(buttons->dev, "Failed to register input handler, error %d\n", error); 298 kfree(buttons->ts_handle); 299 buttons->ts_handle = NULL; 300 mutex_unlock(&buttons->mutex); 301 return error; 302 } 303 304 queue_work(buttons->workqueue, &buttons->merge_task); 305 306 if (buttons->filtered_ts_idev->users > 0 && buttons->ts_handle->open == 0) 307 queue_work(buttons->workqueue, &buttons->open_task); 308 } 309 310 mutex_unlock(&buttons->mutex); 311 return 0; 312 } 313 314 static void touchscreen_buttons_input_disconnect(struct input_handle *handle) 315 { 316 struct touchscreen_buttons *buttons; 317 318 buttons = handle->private; 319 320 mutex_lock(&buttons->mutex); 321 if (handle == buttons->ts_handle) { 322 input_close_device(handle); 323 input_unregister_handle(handle); 324 kfree(handle); 325 buttons->ts_handle = NULL; 326 dev_info(buttons->dev, "Touchscreen device disconnected buttons disabled\n"); 327 } else { 328 dev_err(buttons->dev, "Unknown device disconnected, %p should be %p", handle, 329 buttons->ts_handle); 330 } 331 mutex_unlock(&buttons->mutex); 332 } 333 334 static struct touchscreen_button_map 335 *touchscreen_buttons_get_devtree_pdata(struct device *dev) 336 { 337 struct touchscreen_button_map *map; 338 struct fwnode_handle *child_node; 339 struct device_node *node; 340 int i; 341 342 map = kzalloc(sizeof(*map), GFP_KERNEL); 343 if (!map) 344 return ERR_PTR(-ENOMEM); 345 346 map->count = device_get_child_node_count(dev); 347 if (map->count == 0) 348 return ERR_PTR(-ENODEV); 349 350 map->buttons = kcalloc(map->count, sizeof(*map->buttons), GFP_KERNEL); 351 if (!map->buttons) 352 return ERR_PTR(-ENOMEM); 353 354 node = dev->of_node; 355 map->ts_node = of_parse_phandle(node, "touchscreen_phandle", 0); 356 if (!map->ts_node) { 357 dev_err(dev, "touchscreen_phandle node missing\n"); 358 return ERR_PTR(-ENODEV); 359 } 360 361 dev_info(dev, "Device_node name: %s\n", map->ts_node->name); 362 363 i = 0; 364 device_for_each_child_node(dev, child_node) { 365 struct touchscreen_button *button; 366 367 button = &map->buttons[i]; 368 369 fwnode_property_read_u32(child_node, "x-position", &button->x); 370 fwnode_property_read_u32(child_node, "y-position", &button->y); 371 fwnode_property_read_u32(child_node, "x-size", &button->width); 372 fwnode_property_read_u32(child_node, "y-size", &button->height); 373 fwnode_property_read_u32(child_node, "keycode", &button->keycode); 374 dev_info(dev, 375 "Adding button at x=%u y=%u size %u x %u keycode=%u\n", 376 button->x, button->y, button->width, button->height, button->keycode); 377 ++i; 378 } 379 return map; 380 } 381 > 382 int touchscreen_buttons_idev_opened(struct input_dev *idev) 383 { 384 struct touchscreen_buttons *buttons; 385 386 buttons = dev_get_drvdata(idev->dev.parent); 387 388 mutex_lock(&buttons->mutex); 389 if (buttons && buttons->ts_handle) { 390 if (buttons->ts_handle->open == 0) { 391 queue_work(buttons->workqueue, &buttons->open_task); 392 dev_dbg(idev->dev.parent, "idev opened\n"); 393 } else { 394 dev_info(idev->dev.parent, "idev allready opened\n"); 395 } 396 } else { 397 dev_warn(idev->dev.parent, 398 "Input device opend but touchscreen not opened. %p %p\n", buttons, 399 buttons->ts_handle); 400 } 401 mutex_unlock(&buttons->mutex); 402 return 0; 403 } 404 > 405 void touchscreen_buttons_idev_closed(struct input_dev *idev) 406 { 407 struct touchscreen_buttons *buttons; 408 409 buttons = dev_get_drvdata(idev->dev.parent); 410 411 mutex_lock(&buttons->mutex); 412 if (buttons && buttons->ts_handle && buttons->ts_handle->open != 0) { 413 queue_work(buttons->workqueue, &buttons->close_task); 414 dev_dbg(idev->dev.parent, "idev closed\n"); 415 } 416 mutex_unlock(&buttons->mutex); 417 } 418 --- 0-DAY CI Kernel Test Service, Intel Corporation https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
Hi Carl, Thank you for the patch! Perhaps something to improve: [auto build test WARNING on input/next] [also build test WARNING on v5.10-rc2 next-20201103] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch] url: https://github.com/0day-ci/linux/commits/Carl-Philipp-Klemm/Drivers-input-misc-Add-driver-touchscreen-buttons-to-support-physically-labeled-buttons-on-touch-screen-surfaces/20201030-010622 base: https://git.kernel.org/pub/scm/linux/kernel/git/dtor/input.git next config: x86_64-randconfig-m001-20201103 (attached as .config) compiler: gcc-9 (Debian 9.3.0-15) 9.3.0 If you fix the issue, kindly add following tag as appropriate Reported-by: kernel test robot <lkp@intel.com> smatch warnings: drivers/input/misc/touchscreen-buttons.c:179 touchscreen_buttons_input_event() warn: always true condition '(buttons->queue.lastindex >= 0) => (0-u32max >= 0)' drivers/input/misc/touchscreen-buttons.c:348 touchscreen_buttons_get_devtree_pdata() warn: possible memory leak of 'map' drivers/input/misc/touchscreen-buttons.c:397 touchscreen_buttons_idev_opened() error: we previously assumed 'buttons' could be null (see line 389) vim +179 drivers/input/misc/touchscreen-buttons.c 166 167 static void touchscreen_buttons_input_event(struct input_handle *handle, 168 unsigned int type, unsigned int code, int value) 169 { 170 struct touchscreen_buttons *buttons; 171 172 buttons = handle->private; 173 174 if (type == EV_SYN && code == SYN_REPORT) { 175 if (touchscreen_buttons_process_syn(&buttons->queue, 176 buttons->map, buttons->buttons_idev) == 0) 177 touchscreen_buttons_resend_events(&buttons->queue, buttons->filtered_ts_idev); 178 buttons->queue.lastindex = 0; > 179 } else if (buttons->queue.lastindex < EVENT_QUEUE_SIZE && buttons->queue.lastindex >= 0) { 180 buttons->queue.events[buttons->queue.lastindex].type = type; 181 buttons->queue.events[buttons->queue.lastindex].code = code; 182 buttons->queue.events[buttons->queue.lastindex].value = value; 183 ++buttons->queue.lastindex; 184 } else { 185 dev_warn(buttons->dev, 186 "event_qeue overrun, will not capture events until next SYN_REPORT\n"); 187 } 188 } 189 190 static void touchscreen_buttons_merge_capabilitys(struct input_dev *target, struct input_dev *source) 191 { 192 unsigned int i; 193 194 mutex_lock(&target->mutex); 195 mutex_lock(&source->mutex); 196 for (i = 0; i < BITS_TO_LONGS(INPUT_PROP_CNT); ++i) 197 target->propbit[i] = target->propbit[i] | source->propbit[i]; 198 for (i = 0; i < BITS_TO_LONGS(EV_CNT); ++i) 199 target->evbit[i] = target->evbit[i] | source->evbit[i]; 200 for (i = 0; i < BITS_TO_LONGS(KEY_CNT); ++i) 201 target->keybit[i] = target->keybit[i] | source->keybit[i]; 202 for (i = 0; i < BITS_TO_LONGS(REL_CNT); ++i) 203 target->relbit[i] = target->relbit[i] | source->relbit[i]; 204 for (i = 0; i < BITS_TO_LONGS(ABS_CNT); ++i) 205 target->absbit[i] = target->absbit[i] | source->absbit[i]; 206 for (i = 0; i < BITS_TO_LONGS(MSC_CNT); ++i) 207 target->mscbit[i] = target->mscbit[i] | source->mscbit[i]; 208 for (i = 0; i < BITS_TO_LONGS(LED_CNT); ++i) 209 target->ledbit[i] = target->ledbit[i] | source->ledbit[i]; 210 for (i = 0; i < BITS_TO_LONGS(SND_CNT); ++i) 211 target->sndbit[i] = target->sndbit[i] | source->sndbit[i]; 212 for (i = 0; i < BITS_TO_LONGS(FF_CNT); ++i) 213 target->ffbit[i] = target->ffbit[i] | source->ffbit[i]; 214 for (i = 0; i < BITS_TO_LONGS(SW_CNT); ++i) 215 target->swbit[i] = target->swbit[i] | source->swbit[i]; 216 217 if (*source->evbit & (1 << EV_ABS)) { 218 input_alloc_absinfo(target); 219 for (i = 0; i < ABS_CNT; ++i) 220 target->absinfo[i] = source->absinfo[i]; 221 if (source->mt) { 222 input_mt_init_slots(target, source->mt->num_slots, source->mt->flags); 223 touchscreen_buttons_copy_mt_slots(target, source); 224 } 225 } 226 mutex_unlock(&source->mutex); 227 mutex_unlock(&target->mutex); 228 } 229 230 void merge_task_handler(struct work_struct *work) 231 { 232 struct touchscreen_buttons *buttons = container_of(work, struct touchscreen_buttons, merge_task); 233 234 mutex_lock(&buttons->mutex); 235 if (buttons->ts_handle && buttons->ts_handle->dev) 236 touchscreen_buttons_merge_capabilitys(buttons->filtered_ts_idev, buttons->ts_handle->dev); 237 mutex_unlock(&buttons->mutex); 238 } 239 240 void close_task_handler(struct work_struct *work) 241 { 242 struct touchscreen_buttons *buttons = container_of(work, struct touchscreen_buttons, close_task); 243 244 mutex_lock(&buttons->mutex); 245 if (buttons && buttons->ts_handle && buttons->ts_handle->open != 0) 246 input_close_device(buttons->ts_handle); 247 mutex_unlock(&buttons->mutex); 248 } 249 250 void open_task_handler(struct work_struct *work) 251 { 252 struct touchscreen_buttons *buttons = container_of(work, struct touchscreen_buttons, open_task); 253 int error; 254 255 mutex_lock(&buttons->mutex); 256 if (buttons && buttons->ts_handle) { 257 error = input_open_device(buttons->ts_handle); 258 if (error) { 259 dev_err(buttons->dev, "Failed to open input device, error %d\n", error); 260 input_unregister_handle(buttons->ts_handle); 261 kfree(buttons->ts_handle); 262 buttons->ts_handle = NULL; 263 } 264 } 265 mutex_unlock(&buttons->mutex); 266 } 267 268 static int touchscreen_buttons_input_connect(struct input_handler *handler, 269 struct input_dev *dev, const struct input_device_id *id) 270 { 271 struct touchscreen_buttons *buttons; 272 273 buttons = handler->private; 274 275 mutex_lock(&buttons->mutex); 276 277 if ((!buttons->ts_handle && device_match_of_node(&dev->dev, buttons->map->ts_node)) || 278 (dev->dev.parent && device_match_of_node(dev->dev.parent, buttons->map->ts_node))) { 279 int error; 280 281 dev_info(buttons->dev, "Binding to device: %s\n", dev_name(&dev->dev)); 282 283 buttons->ts_handle = kzalloc(sizeof(*buttons->ts_handle), GFP_KERNEL); 284 if (!buttons->ts_handle) { 285 mutex_unlock(&buttons->mutex); 286 return -ENOMEM; 287 } 288 289 buttons->ts_handle->dev = dev; 290 buttons->ts_handle->handler = handler; 291 buttons->ts_handle->name = "touchscreen-buttons"; 292 buttons->ts_handle->private = handler->private; 293 buttons->queue.lastindex = 0; 294 295 error = input_register_handle(buttons->ts_handle); 296 if (error) { 297 dev_err(buttons->dev, "Failed to register input handler, error %d\n", error); 298 kfree(buttons->ts_handle); 299 buttons->ts_handle = NULL; 300 mutex_unlock(&buttons->mutex); 301 return error; 302 } 303 304 queue_work(buttons->workqueue, &buttons->merge_task); 305 306 if (buttons->filtered_ts_idev->users > 0 && buttons->ts_handle->open == 0) 307 queue_work(buttons->workqueue, &buttons->open_task); 308 } 309 310 mutex_unlock(&buttons->mutex); 311 return 0; 312 } 313 314 static void touchscreen_buttons_input_disconnect(struct input_handle *handle) 315 { 316 struct touchscreen_buttons *buttons; 317 318 buttons = handle->private; 319 320 mutex_lock(&buttons->mutex); 321 if (handle == buttons->ts_handle) { 322 input_close_device(handle); 323 input_unregister_handle(handle); 324 kfree(handle); 325 buttons->ts_handle = NULL; 326 dev_info(buttons->dev, "Touchscreen device disconnected buttons disabled\n"); 327 } else { 328 dev_err(buttons->dev, "Unknown device disconnected, %p should be %p", handle, 329 buttons->ts_handle); 330 } 331 mutex_unlock(&buttons->mutex); 332 } 333 334 static struct touchscreen_button_map 335 *touchscreen_buttons_get_devtree_pdata(struct device *dev) 336 { 337 struct touchscreen_button_map *map; 338 struct fwnode_handle *child_node; 339 struct device_node *node; 340 int i; 341 342 map = kzalloc(sizeof(*map), GFP_KERNEL); 343 if (!map) 344 return ERR_PTR(-ENOMEM); 345 346 map->count = device_get_child_node_count(dev); 347 if (map->count == 0) > 348 return ERR_PTR(-ENODEV); 349 350 map->buttons = kcalloc(map->count, sizeof(*map->buttons), GFP_KERNEL); 351 if (!map->buttons) 352 return ERR_PTR(-ENOMEM); 353 354 node = dev->of_node; 355 map->ts_node = of_parse_phandle(node, "touchscreen_phandle", 0); 356 if (!map->ts_node) { 357 dev_err(dev, "touchscreen_phandle node missing\n"); 358 return ERR_PTR(-ENODEV); 359 } 360 361 dev_info(dev, "Device_node name: %s\n", map->ts_node->name); 362 363 i = 0; 364 device_for_each_child_node(dev, child_node) { 365 struct touchscreen_button *button; 366 367 button = &map->buttons[i]; 368 369 fwnode_property_read_u32(child_node, "x-position", &button->x); 370 fwnode_property_read_u32(child_node, "y-position", &button->y); 371 fwnode_property_read_u32(child_node, "x-size", &button->width); 372 fwnode_property_read_u32(child_node, "y-size", &button->height); 373 fwnode_property_read_u32(child_node, "keycode", &button->keycode); 374 dev_info(dev, 375 "Adding button at x=%u y=%u size %u x %u keycode=%u\n", 376 button->x, button->y, button->width, button->height, button->keycode); 377 ++i; 378 } 379 return map; 380 } 381 --- 0-DAY CI Kernel Test Service, Intel Corporation https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 362e8a01980c..3d487b8e7d8b 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -318,6 +318,14 @@ config INPUT_CPCAP_PWRBUTTON To compile this driver as a module, choose M here. The module will be called cpcap-pwrbutton. +config INPUT_TOUCHSCREEN_BUTTONS + tristate "Touchscreen Buttons" + help + Say Y here if you want to enable buttons on touchscreen devices. + + To compile this driver as a module, choose M here. The module will + be called touchscreen-buttons. + config INPUT_WISTRON_BTNS tristate "x86 Wistron laptop button interface" depends on X86_32 diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index a48e5f2d859d..18032a1b2a9c 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -84,4 +84,5 @@ obj-$(CONFIG_INPUT_WM831X_ON) += wm831x-on.o obj-$(CONFIG_INPUT_XEN_KBDDEV_FRONTEND) += xen-kbdfront.o obj-$(CONFIG_INPUT_YEALINK) += yealink.o obj-$(CONFIG_INPUT_IDEAPAD_SLIDEBAR) += ideapad_slidebar.o +obj-$(CONFIG_INPUT_TOUCHSCREEN_BUTTONS) += touchscreen-buttons.o diff --git a/drivers/input/misc/touchscreen-buttons.c b/drivers/input/misc/touchscreen-buttons.c new file mode 100644 index 000000000000..d7b912ce8ef1 --- /dev/null +++ b/drivers/input/misc/touchscreen-buttons.c @@ -0,0 +1,558 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** + * Touchscreen Virutal Button Input Driver + * + * Copyright (C) 2020 Carl Klemm <carl@uvos.xyz> + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file "COPYING" in the main directory of this + * archive for more details. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/platform_device.h> +#include <linux/errno.h> +#include <linux/input.h> +#include <linux/slab.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/kernel.h> +#include <linux/limits.h> +#include <linux/input/mt.h> +#include <linux/device/bus.h> +#include <linux/string.h> +#include <linux/workqueue.h> + +#define EVENT_QUEUE_SIZE 32 + +struct touchscreen_button { + u32 x; + u32 y; + u32 width; + u32 height; + u32 keycode; + u8 depressed; +}; + +struct touchscreen_button_map { + struct touchscreen_button *buttons; + u32 count; + struct device_node *ts_node; +}; + +struct event { + unsigned int type; + unsigned int code; + int value; +}; + +struct event_queue { + struct event events[EVENT_QUEUE_SIZE]; + unsigned int lastindex; +}; + +struct touchscreen_buttons { + struct device *dev; + struct input_dev *buttons_idev; + struct input_dev *filtered_ts_idev; + struct touchscreen_button_map *map; + struct input_handler *handler; + struct input_handle *ts_handle; + struct event_queue queue; + struct workqueue_struct *workqueue; + struct work_struct open_task; + struct work_struct close_task; + struct work_struct merge_task; + struct mutex mutex; +}; + +static const struct input_device_id touchscreen_buttons_ids[] = { + { + .flags = INPUT_DEVICE_ID_MATCH_EVBIT, + .evbit = {BIT_MASK(EV_ABS)}, + }, + { + .flags = INPUT_DEVICE_ID_MATCH_EVBIT, + .evbit = {BIT_MASK(EV_KEY)}, + }, + { + .flags = INPUT_DEVICE_ID_MATCH_EVBIT, + .evbit = {BIT_MASK(EV_SYN)}, + }, + {}, +}; + +static int touchscreen_buttons_process_syn(const struct event_queue *queue, + const struct touchscreen_button_map *map, + struct input_dev *idev) +{ + u32 i; + int x, y, ret, pressed; + + x = INT_MIN; + y = INT_MIN; + pressed = -1; + ret = 0; + + for (i = 0; i < queue->lastindex; ++i) { + const struct event *ev; + + ev = &queue->events[i]; + if (ev->type == EV_ABS && ev->code == ABS_X) + x = ev->value; + else if (ev->type == EV_ABS && ev->code == ABS_Y) + y = ev->value; + else if (ev->type == EV_KEY && ev->code == BTN_TOUCH) + pressed = ev->value; + } + + for (i = 0; i < map->count; ++i) { + struct touchscreen_button *button = &map->buttons[i]; + + if (pressed == 1 && + button->x <= x && + button->y <= y && + button->width + button->x >= x && + button->height + button->y >= y && + button->depressed == 0) { + input_report_key(idev, button->keycode, 1); + button->depressed = 1; + ret = 1; + } else if (button->depressed == 1) { + if (pressed == 0) { + input_report_key(idev, button->keycode, 0); + button->depressed = 0; + } + ret = 2; + } + } + + if (ret != 0) { + input_event(idev, EV_SYN, SYN_REPORT, 0); + } else if (ret == 0) { + bool buttonpressed = false; + + for (i = 0; i < map->count; ++i) + buttonpressed = buttonpressed || map->buttons[i].depressed; + if (buttonpressed) + ret = 3; + } + + return ret; +} + +static void touchscreen_buttons_resend_events(const struct event_queue *queue, struct input_dev *idev) +{ + u32 i; + + for (i = 0; i < queue->lastindex; ++i) + input_event(idev, queue->events[i].type, queue->events[i].code, queue->events[i].value); + input_event(idev, EV_SYN, SYN_REPORT, 0); +} + +static void touchscreen_buttons_copy_mt_slots(struct input_dev *target, struct input_dev *source) +{ + if (source->mt && target->mt && source->mt->num_slots == target->mt->num_slots) { + memcpy(target->mt->slots, source->mt->slots, + sizeof(struct input_mt_slot) * source->mt->num_slots); + } +} + +static void touchscreen_buttons_input_event(struct input_handle *handle, + unsigned int type, unsigned int code, int value) +{ + struct touchscreen_buttons *buttons; + + buttons = handle->private; + + if (type == EV_SYN && code == SYN_REPORT) { + if (touchscreen_buttons_process_syn(&buttons->queue, + buttons->map, buttons->buttons_idev) == 0) + touchscreen_buttons_resend_events(&buttons->queue, buttons->filtered_ts_idev); + buttons->queue.lastindex = 0; + } else if (buttons->queue.lastindex < EVENT_QUEUE_SIZE && buttons->queue.lastindex >= 0) { + buttons->queue.events[buttons->queue.lastindex].type = type; + buttons->queue.events[buttons->queue.lastindex].code = code; + buttons->queue.events[buttons->queue.lastindex].value = value; + ++buttons->queue.lastindex; + } else { + dev_warn(buttons->dev, + "event_qeue overrun, will not capture events until next SYN_REPORT\n"); + } +} + +static void touchscreen_buttons_merge_capabilitys(struct input_dev *target, struct input_dev *source) +{ + unsigned int i; + + mutex_lock(&target->mutex); + mutex_lock(&source->mutex); + for (i = 0; i < BITS_TO_LONGS(INPUT_PROP_CNT); ++i) + target->propbit[i] = target->propbit[i] | source->propbit[i]; + for (i = 0; i < BITS_TO_LONGS(EV_CNT); ++i) + target->evbit[i] = target->evbit[i] | source->evbit[i]; + for (i = 0; i < BITS_TO_LONGS(KEY_CNT); ++i) + target->keybit[i] = target->keybit[i] | source->keybit[i]; + for (i = 0; i < BITS_TO_LONGS(REL_CNT); ++i) + target->relbit[i] = target->relbit[i] | source->relbit[i]; + for (i = 0; i < BITS_TO_LONGS(ABS_CNT); ++i) + target->absbit[i] = target->absbit[i] | source->absbit[i]; + for (i = 0; i < BITS_TO_LONGS(MSC_CNT); ++i) + target->mscbit[i] = target->mscbit[i] | source->mscbit[i]; + for (i = 0; i < BITS_TO_LONGS(LED_CNT); ++i) + target->ledbit[i] = target->ledbit[i] | source->ledbit[i]; + for (i = 0; i < BITS_TO_LONGS(SND_CNT); ++i) + target->sndbit[i] = target->sndbit[i] | source->sndbit[i]; + for (i = 0; i < BITS_TO_LONGS(FF_CNT); ++i) + target->ffbit[i] = target->ffbit[i] | source->ffbit[i]; + for (i = 0; i < BITS_TO_LONGS(SW_CNT); ++i) + target->swbit[i] = target->swbit[i] | source->swbit[i]; + + if (*source->evbit & (1 << EV_ABS)) { + input_alloc_absinfo(target); + for (i = 0; i < ABS_CNT; ++i) + target->absinfo[i] = source->absinfo[i]; + if (source->mt) { + input_mt_init_slots(target, source->mt->num_slots, source->mt->flags); + touchscreen_buttons_copy_mt_slots(target, source); + } + } + mutex_unlock(&source->mutex); + mutex_unlock(&target->mutex); +} + +void merge_task_handler(struct work_struct *work) +{ + struct touchscreen_buttons *buttons = container_of(work, struct touchscreen_buttons, merge_task); + + mutex_lock(&buttons->mutex); + if (buttons->ts_handle && buttons->ts_handle->dev) + touchscreen_buttons_merge_capabilitys(buttons->filtered_ts_idev, buttons->ts_handle->dev); + mutex_unlock(&buttons->mutex); +} + +void close_task_handler(struct work_struct *work) +{ + struct touchscreen_buttons *buttons = container_of(work, struct touchscreen_buttons, close_task); + + mutex_lock(&buttons->mutex); + if (buttons && buttons->ts_handle && buttons->ts_handle->open != 0) + input_close_device(buttons->ts_handle); + mutex_unlock(&buttons->mutex); +} + +void open_task_handler(struct work_struct *work) +{ + struct touchscreen_buttons *buttons = container_of(work, struct touchscreen_buttons, open_task); + int error; + + mutex_lock(&buttons->mutex); + if (buttons && buttons->ts_handle) { + error = input_open_device(buttons->ts_handle); + if (error) { + dev_err(buttons->dev, "Failed to open input device, error %d\n", error); + input_unregister_handle(buttons->ts_handle); + kfree(buttons->ts_handle); + buttons->ts_handle = NULL; + } + } + mutex_unlock(&buttons->mutex); +} + +static int touchscreen_buttons_input_connect(struct input_handler *handler, + struct input_dev *dev, const struct input_device_id *id) +{ + struct touchscreen_buttons *buttons; + + buttons = handler->private; + + mutex_lock(&buttons->mutex); + + if ((!buttons->ts_handle && device_match_of_node(&dev->dev, buttons->map->ts_node)) || + (dev->dev.parent && device_match_of_node(dev->dev.parent, buttons->map->ts_node))) { + int error; + + dev_info(buttons->dev, "Binding to device: %s\n", dev_name(&dev->dev)); + + buttons->ts_handle = kzalloc(sizeof(*buttons->ts_handle), GFP_KERNEL); + if (!buttons->ts_handle) { + mutex_unlock(&buttons->mutex); + return -ENOMEM; + } + + buttons->ts_handle->dev = dev; + buttons->ts_handle->handler = handler; + buttons->ts_handle->name = "touchscreen-buttons"; + buttons->ts_handle->private = handler->private; + buttons->queue.lastindex = 0; + + error = input_register_handle(buttons->ts_handle); + if (error) { + dev_err(buttons->dev, "Failed to register input handler, error %d\n", error); + kfree(buttons->ts_handle); + buttons->ts_handle = NULL; + mutex_unlock(&buttons->mutex); + return error; + } + + queue_work(buttons->workqueue, &buttons->merge_task); + + if (buttons->filtered_ts_idev->users > 0 && buttons->ts_handle->open == 0) + queue_work(buttons->workqueue, &buttons->open_task); + } + + mutex_unlock(&buttons->mutex); + return 0; +} + +static void touchscreen_buttons_input_disconnect(struct input_handle *handle) +{ + struct touchscreen_buttons *buttons; + + buttons = handle->private; + + mutex_lock(&buttons->mutex); + if (handle == buttons->ts_handle) { + input_close_device(handle); + input_unregister_handle(handle); + kfree(handle); + buttons->ts_handle = NULL; + dev_info(buttons->dev, "Touchscreen device disconnected buttons disabled\n"); + } else { + dev_err(buttons->dev, "Unknown device disconnected, %p should be %p", handle, + buttons->ts_handle); + } + mutex_unlock(&buttons->mutex); +} + +static struct touchscreen_button_map +*touchscreen_buttons_get_devtree_pdata(struct device *dev) +{ + struct touchscreen_button_map *map; + struct fwnode_handle *child_node; + struct device_node *node; + int i; + + map = kzalloc(sizeof(*map), GFP_KERNEL); + if (!map) + return ERR_PTR(-ENOMEM); + + map->count = device_get_child_node_count(dev); + if (map->count == 0) + return ERR_PTR(-ENODEV); + + map->buttons = kcalloc(map->count, sizeof(*map->buttons), GFP_KERNEL); + if (!map->buttons) + return ERR_PTR(-ENOMEM); + + node = dev->of_node; + map->ts_node = of_parse_phandle(node, "touchscreen_phandle", 0); + if (!map->ts_node) { + dev_err(dev, "touchscreen_phandle node missing\n"); + return ERR_PTR(-ENODEV); + } + + dev_info(dev, "Device_node name: %s\n", map->ts_node->name); + + i = 0; + device_for_each_child_node(dev, child_node) { + struct touchscreen_button *button; + + button = &map->buttons[i]; + + fwnode_property_read_u32(child_node, "x-position", &button->x); + fwnode_property_read_u32(child_node, "y-position", &button->y); + fwnode_property_read_u32(child_node, "x-size", &button->width); + fwnode_property_read_u32(child_node, "y-size", &button->height); + fwnode_property_read_u32(child_node, "keycode", &button->keycode); + dev_info(dev, + "Adding button at x=%u y=%u size %u x %u keycode=%u\n", + button->x, button->y, button->width, button->height, button->keycode); + ++i; + } + return map; +} + +int touchscreen_buttons_idev_opened(struct input_dev *idev) +{ + struct touchscreen_buttons *buttons; + + buttons = dev_get_drvdata(idev->dev.parent); + + mutex_lock(&buttons->mutex); + if (buttons && buttons->ts_handle) { + if (buttons->ts_handle->open == 0) { + queue_work(buttons->workqueue, &buttons->open_task); + dev_dbg(idev->dev.parent, "idev opened\n"); + } else { + dev_info(idev->dev.parent, "idev allready opened\n"); + } + } else { + dev_warn(idev->dev.parent, + "Input device opend but touchscreen not opened. %p %p\n", buttons, + buttons->ts_handle); + } + mutex_unlock(&buttons->mutex); + return 0; +} + +void touchscreen_buttons_idev_closed(struct input_dev *idev) +{ + struct touchscreen_buttons *buttons; + + buttons = dev_get_drvdata(idev->dev.parent); + + mutex_lock(&buttons->mutex); + if (buttons && buttons->ts_handle && buttons->ts_handle->open != 0) { + queue_work(buttons->workqueue, &buttons->close_task); + dev_dbg(idev->dev.parent, "idev closed\n"); + } + mutex_unlock(&buttons->mutex); +} + +static int touchscreen_buttons_probe(struct platform_device *pdev) +{ + struct touchscreen_buttons *buttons; + int error, i; + + buttons = kzalloc(sizeof(*buttons), GFP_KERNEL); + if (!buttons) + return -ENOMEM; + + dev_set_drvdata(&pdev->dev, buttons); + + buttons->workqueue = create_singlethread_workqueue("touchscreen-buttons-workqueue"); + INIT_WORK(&buttons->merge_task, merge_task_handler); + INIT_WORK(&buttons->open_task, open_task_handler); + INIT_WORK(&buttons->close_task, close_task_handler); + + mutex_init(&buttons->mutex); + + buttons->queue.lastindex = 0; + buttons->dev = &pdev->dev; + + buttons->map = touchscreen_buttons_get_devtree_pdata(&pdev->dev); + if (IS_ERR(buttons->map)) + return PTR_ERR(buttons->map); + + /*filtered touchscreen device */ + buttons->filtered_ts_idev = input_allocate_device(); + if (!buttons->filtered_ts_idev) + return -ENOMEM; + buttons->filtered_ts_idev->name = "Filtered Touchscreen"; + buttons->filtered_ts_idev->phys = "touchscreen-buttons/input1"; + buttons->filtered_ts_idev->dev.parent = buttons->dev; + buttons->filtered_ts_idev->open = touchscreen_buttons_idev_opened; + buttons->filtered_ts_idev->close = touchscreen_buttons_idev_closed; + + /*buttons input device */ + buttons->buttons_idev = input_allocate_device(); + if (!buttons->buttons_idev) + return -ENOMEM; + buttons->buttons_idev->name = "Touchscreen Buttons"; + buttons->buttons_idev->phys = "touchscreen-buttons/input0"; + buttons->buttons_idev->dev.parent = buttons->dev; + for (i = 0; i < buttons->map->count; ++i) + input_set_capability(buttons->buttons_idev, EV_KEY, buttons->map->buttons[i].keycode); + + /*handler for touchscreen input device */ + buttons->handler = kzalloc(sizeof(*buttons->handler), GFP_KERNEL); + + buttons->handler->event = touchscreen_buttons_input_event; + buttons->handler->connect = touchscreen_buttons_input_connect; + buttons->handler->disconnect = touchscreen_buttons_input_disconnect; + buttons->handler->name = "touchscreen-buttons"; + buttons->handler->id_table = touchscreen_buttons_ids; + buttons->handler->private = buttons; + + error = input_register_handler(buttons->handler); + if (error) { + dev_err(&pdev->dev, "Input handler register failed: %d\n", error); + return error; + } + + error = input_register_device(buttons->buttons_idev); + if (error) { + dev_err(&pdev->dev, "Input device register failed: %d\n", error); + return error; + } + + error = input_register_device(buttons->filtered_ts_idev); + if (error) { + dev_err(&pdev->dev, "Input device register failed: %d\n", error); + return error; + } + + return 0; +} + +static int touchscreen_buttons_remove(struct platform_device *pdev) +{ + struct touchscreen_buttons *buttons; + struct input_handle *ts_handle; + + buttons = dev_get_drvdata(&pdev->dev); + + mutex_lock(&buttons->mutex); + + ts_handle = buttons->ts_handle; + + input_unregister_handler(buttons->handler); + if (buttons->ts_handle) { + if (buttons->ts_handle->open != 0) + input_close_device(buttons->ts_handle); + input_unregister_handle(buttons->ts_handle); + buttons->ts_handle = NULL; + } + + mutex_unlock(&buttons->mutex); + + flush_workqueue(buttons->workqueue); + destroy_workqueue(buttons->workqueue); + + input_unregister_device(buttons->buttons_idev); + input_unregister_device(buttons->filtered_ts_idev); + + kfree(ts_handle); + + if (buttons->map) { + kfree(buttons->map->buttons); + kfree(buttons->map); + } + kfree(buttons->handler); + + kfree(buttons); + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id touchscreen_buttons_dt_match_table[] = { + {.compatible = "touchscreen-buttons"}, + {}, +}; + +MODULE_DEVICE_TABLE(of, touchscreen_buttons_dt_match_table); +#endif + +static struct platform_driver touchscreen_buttons_driver = { + .probe = touchscreen_buttons_probe, + .remove = touchscreen_buttons_remove, + .driver = { + .name = "touchscreen-buttons", + .of_match_table = of_match_ptr(touchscreen_buttons_dt_match_table), + }, +}; + +module_platform_driver(touchscreen_buttons_driver); + +MODULE_ALIAS("platform:touchscreen-buttons"); +MODULE_DESCRIPTION("touchscreen-buttons"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Carl Klemm <carl@uvos.xyz>");
Adds a driver for supporting permanet, physically labeled, buttons on touchscreen surfaces. As are common on android phones designed with android 1.0 to 2.3 in mind. The driver works by attaching to another input device and mirroring its events, while inserting key events and filtering for touches that land on the buttons. Buttons are arbitrary rectangles configurable via dts. Signed-off-by: Carl Philipp Klemm <carl@uvos.xyz> ---