diff options
author | Josh Thielen <jtln@google.com> | 2023-07-06 18:25:46 +0000 |
---|---|---|
committer | Josh Thielen <jtln@google.com> | 2023-07-10 18:39:41 +0000 |
commit | 47177b0378e6c916a305d987177973c62c089d5f (patch) | |
tree | 8e140ec3c4882e5c84a7f91c7c4ac5eabd5c43ba | |
parent | 582ea070fbbc7e8c16e62b0dc1957cedecf6c22a (diff) | |
download | rotary-encoders-47177b0378e6c916a305d987177973c62c089d5f.tar.gz |
Add PETC input filter driver
This driver:
* Finds input (source) devices that have the power key set
as a capability (excluding touch devices)
* Creates clones of the matching input devices
* Filters input events on the source device and
forwards the events on the clone device
* Detects when the system is in false interactive
(MCU display state is On, while AP display state is Off)
* Injects a wakeup key event on the clone device when an
input event occurs while the system is in false interactive
* When in false interactive, queues source events until the
display is on or a timeout occurs
Test: Manual.
* Verified crown button press in ambient transitions Android to
interactive.
* Verified crown button press in false interactive and interactive
launches app list.
* Verified successful removal of device with rmmod.
Bug: b/285402950
Change-Id: I3adc8b3015866220c21fac7a58c4e830c9ecbbdb
Signed-off-by: Josh Thielen <jtln@google.com>
-rw-r--r-- | petc_input_filter/Kbuild | 3 | ||||
-rw-r--r-- | petc_input_filter/Kconfig | 13 | ||||
-rw-r--r-- | petc_input_filter/Makefile | 18 | ||||
-rw-r--r-- | petc_input_filter/petc_input_filter.c | 672 | ||||
-rw-r--r-- | petc_input_filter/petc_input_filter.h | 21 |
5 files changed, 727 insertions, 0 deletions
diff --git a/petc_input_filter/Kbuild b/petc_input_filter/Kbuild new file mode 100644 index 0000000..cb7590a --- /dev/null +++ b/petc_input_filter/Kbuild @@ -0,0 +1,3 @@ +obj-$(CONFIG_INPUT_PETC_INPUT_FILTER) += petc_input_filter.o + +petc-input-filter-y += petc_input_filter.o
\ No newline at end of file diff --git a/petc_input_filter/Kconfig b/petc_input_filter/Kconfig new file mode 100644 index 0000000..8aca385 --- /dev/null +++ b/petc_input_filter/Kconfig @@ -0,0 +1,13 @@ +# +# PETC input filter driver configuration +# + +config INPUT_PETC_INPUT_FILTER + tristate "PETC input filter driver" + depends on INPUT + help + Say Y to enable support for the PETC input filter. + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called petc-if. diff --git a/petc_input_filter/Makefile b/petc_input_filter/Makefile new file mode 100644 index 0000000..517aab7 --- /dev/null +++ b/petc_input_filter/Makefile @@ -0,0 +1,18 @@ +EXTRA_INCLUDE := -I$(KERNEL_SRC)/../google-modules/nanohub/ +EXTRA_SYMBOLS := $(OUT_DIR)/../google-modules/nanohub/Module.symvers + +default: all + +KBUILD_OPTIONS := CONFIG_INPUT_PETC_INPUT_FILTER=m \ + ccflags-y=$(EXTRA_INCLUDE) \ + KBUILD_EXTRA_SYMBOLS="$(EXTRA_SYMBOLS)" + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(M) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 M=$(M) -C $(KERNEL_SRC) modules_install + +clean: + rm -f *.o *.ko *.mod.c *.mod.o *~ .*.cmd Module.symvers + rm -rf .tmp_versions diff --git a/petc_input_filter/petc_input_filter.c b/petc_input_filter/petc_input_filter.c new file mode 100644 index 0000000..c192866 --- /dev/null +++ b/petc_input_filter/petc_input_filter.c @@ -0,0 +1,672 @@ +/* + * Copyright (C) 2023 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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/errno.h> +#include <linux/input.h> +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/major.h> +#include <linux/mm.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/poll.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/soc/qcom/panel_event_notifier.h> +#include <linux/workqueue.h> +#include <linux/version.h> + +#include "petc_input_filter.h" +#include "nanohub_exports.h" + +#define WAKEUP_WAIT_MAX msecs_to_jiffies(500) + +struct petc_input_event { + struct list_head node; + unsigned int type; + unsigned int code; + int value; +}; + +struct petc_if_dev_data { + struct list_head node; + struct input_handle handle; + struct work_struct register_work; + struct work_struct immediate_work; + struct delayed_work delayed_work; + struct list_head event_list; + spinlock_t event_list_slock; + struct input_dev *clone_dev; + struct petc_if_drv_data *drv_data; + bool clone_registered; +}; + +struct petc_if_drv_data { + struct platform_device *pdev; + struct drm_panel *active_panel; + struct work_struct remove_devices_work; + struct list_head device_list; + struct list_head removed_device_list; + spinlock_t device_list_slock; + void *notifier_cookie; + bool ap_screen_on; +}; + +static bool petc_if_is_mcu_screen_on(void) +{ + int ret = nanohub_query_display_state(); + pr_debug("[PETC_IF] read MCU display state = %d\n", ret); + return (ret == MCU_DISPLAY_ON || ret == MCU_DISPLAY_HIGH_BRIGHTNESS); +} + +static bool petc_if_is_ap_screen_on(struct petc_if_drv_data *drv_data) +{ + return drv_data->ap_screen_on; +} + +static bool petc_if_is_false_interactive(struct petc_if_dev_data *dev_data) +{ + return !petc_if_is_ap_screen_on(dev_data->drv_data) && + petc_if_is_mcu_screen_on(); +} + +static void petc_if_add_device(struct petc_if_drv_data *drv_data, + struct petc_if_dev_data *dev_data) +{ + unsigned long flags; + + spin_lock_irqsave(&drv_data->device_list_slock, flags); + list_add_tail(&dev_data->node, &drv_data->device_list); + spin_unlock_irqrestore(&drv_data->device_list_slock, flags); +} + +static void petc_if_queue_event(struct petc_if_dev_data *dev_data, + unsigned int type, unsigned int code, int value) +{ + struct petc_input_event *event; + unsigned long flags; + + event = kmalloc(sizeof(struct petc_input_event), GFP_ATOMIC); + if (!event) { + pr_err("[PETC_IF] Out of memory to queue event\n"); + return; + } + + event->type = type; + event->code = code; + event->value = value; + + spin_lock_irqsave(&dev_data->event_list_slock, flags); + list_add_tail(&event->node, &dev_data->event_list); + spin_unlock_irqrestore(&dev_data->event_list_slock, flags); +} + +struct petc_input_event * +petc_if_get_queued_event(struct petc_if_dev_data *dev_data) +{ + struct petc_input_event *event = NULL; + unsigned long flags; + + spin_lock_irqsave(&dev_data->event_list_slock, flags); + if (!list_empty(&dev_data->event_list)) { + event = list_first_entry(&dev_data->event_list, + struct petc_input_event, node); + list_del(&event->node); + } + spin_unlock_irqrestore(&dev_data->event_list_slock, flags); + + return event; +} + +static void petc_if_process_queued_events(struct petc_if_dev_data *dev_data) +{ + struct petc_input_event *event; + + while ((event = petc_if_get_queued_event(dev_data))) { + pr_debug( + "[PETC_IF] dispatched queued input event: type=%d code=%d value=%d\n", + event->type, event->code, event->value); + input_event(dev_data->clone_dev, event->type, event->code, + event->value); + kfree(event); + } +} + +static void petc_if_free_queued_events(struct petc_if_dev_data *dev_data) +{ + struct petc_input_event *event, *next; + unsigned long flags; + + spin_lock_irqsave(&dev_data->event_list_slock, flags); + list_for_each_entry_safe(event, next, &dev_data->event_list, node) { + kfree(event); + } + spin_unlock_irqrestore(&dev_data->event_list_slock, flags); +} + +static void petc_if_delayed_work_func(struct work_struct *work) +{ + struct petc_if_dev_data *dev_data = + container_of(work, struct petc_if_dev_data, delayed_work.work); + + pr_debug("[PETC_IF] handling delayed work\n"); + + if (!dev_data->clone_registered) { + pr_warn("[PETC_IF] ignoring work. clone not registered\n"); + return; + } + + petc_if_process_queued_events(dev_data); +} + +static void petc_if_send_wakeup_key(struct petc_if_dev_data *dev_data) +{ + input_report_key(dev_data->clone_dev, KEY_WAKEUP, 1); + input_sync(dev_data->clone_dev); + input_report_key(dev_data->clone_dev, KEY_WAKEUP, 0); + input_sync(dev_data->clone_dev); +} + +static void petc_if_immediate_work_func(struct work_struct *work) +{ + struct petc_if_dev_data *dev_data = + container_of(work, struct petc_if_dev_data, immediate_work); + + pr_debug("[PETC_IF] handling immediate work\n"); + + if (!dev_data->clone_registered) { + pr_warn("[PETC_IF] ignoring work. clone not registered\n"); + return; + } + + if (petc_if_is_false_interactive(dev_data)) { + pr_debug("[PETC_IF] injecting wakekey\n"); + petc_if_send_wakeup_key(dev_data); + + pr_debug("[PETC_IF] delayed work scheduled\n"); + schedule_delayed_work(&dev_data->delayed_work, WAKEUP_WAIT_MAX); + } else { + petc_if_process_queued_events(dev_data); + } +} + +static bool petc_if_handle_input(struct petc_if_dev_data *dev_data, + unsigned int type, unsigned int code, + int value) +{ + if (!work_pending(&dev_data->immediate_work) && + !delayed_work_pending(&dev_data->delayed_work)) { + if (petc_if_is_ap_screen_on(dev_data->drv_data)) { + pr_debug( + "[PETC_IF] forwarding event (AP screen on): type=%d code=%d value=%d\n", + type, code, value); + input_event(dev_data->clone_dev, type, code, value); + } else { + pr_debug( + "[PETC_IF] queued event (AP screen off): type=%d code=%d value=%d\n", + type, code, value); + petc_if_queue_event(dev_data, type, code, value); + schedule_work(&dev_data->immediate_work); + pr_debug("[PETC_IF] immediate work scheduled\n"); + } + } else { + pr_debug( + "[PETC_IF] queued event (pending work): type=%d code=%d value=%d\n", + type, code, value); + petc_if_queue_event(dev_data, type, code, value); + if (!delayed_work_pending(&dev_data->delayed_work)) { + pr_debug("[PETC_IF] immediate work scheduled\n"); + schedule_work(&dev_data->immediate_work); + } + } + return true; +} + +static void petc_if_register_work_func(struct work_struct *work) +{ + struct petc_if_dev_data *dev_data = + container_of(work, struct petc_if_dev_data, register_work); + int error; + + pr_debug("[PETC_IF] handling register work\n"); + + if (dev_data->clone_registered) { + pr_debug("[PETC_IF] clone already registered\n"); + return; + } + + error = input_register_device(dev_data->clone_dev); + if (error) { + pr_err("[PETC_IF] Failed to register clone_dev, error %d\n", + error); + input_unregister_handle(&dev_data->handle); + input_free_device(dev_data->clone_dev); + kfree(dev_data); + return; + } + + petc_if_add_device(dev_data->drv_data, dev_data); + + dev_data->clone_registered = true; +} + +static void petc_if_remove_device(struct petc_if_dev_data *dev_data) +{ + cancel_work_sync(&dev_data->immediate_work); + cancel_delayed_work_sync(&dev_data->delayed_work); + petc_if_free_queued_events(dev_data); + input_unregister_device(dev_data->clone_dev); + kfree(dev_data); +} + +struct petc_if_dev_data * +petc_if_get_next_removed_device(struct petc_if_drv_data *drv_data) +{ + struct petc_if_dev_data *dev_data = NULL; + unsigned long flags; + + spin_lock_irqsave(&drv_data->device_list_slock, flags); + if (!list_empty(&drv_data->removed_device_list)) { + dev_data = list_first_entry(&drv_data->removed_device_list, + struct petc_if_dev_data, node); + list_del(&dev_data->node); + } + spin_unlock_irqrestore(&drv_data->device_list_slock, flags); + + return dev_data; +} + +static void petc_if_remove_devices_work_func(struct work_struct *work) +{ + struct petc_if_dev_data *dev_data; + struct petc_if_drv_data *drv_data = + container_of(work, struct petc_if_drv_data, remove_devices_work); + + pr_debug("[PETC_IF] handling remove devices work\n"); + + while ((dev_data = petc_if_get_next_removed_device(drv_data))) { + pr_debug("[PETC_IF] removing device %s\n", dev_data->clone_dev->name); + petc_if_remove_device(dev_data); + } +} + +static bool petc_if_filter(struct input_handle *handle, unsigned int type, + unsigned int code, int value) +{ + struct petc_if_dev_data *dev_data = handle->private; + + if (!dev_data->clone_registered) { + pr_debug( + "[PETC_IF] input received but clone not yet registered\n"); + return false; + } + + input_set_timestamp(dev_data->clone_dev, + handle->dev->timestamp[INPUT_CLK_MONO]); + return petc_if_handle_input(dev_data, type, code, value); +} + +static bool petc_if_match(struct input_handler *handler, struct input_dev *dev) +{ + if (!strcmp(dev->name, PETC_DEV_NAME)) { + /* Don't match on the clone */ + return false; + } + + if (test_bit(EV_ABS, dev->evbit)) { + /* Don't match on a touch device */ + return false; + } + + return true; +} + +static int petc_if_dev_connect(struct input_handler *handler, + struct input_dev *dev, + const struct input_device_id *id) +{ + struct petc_if_dev_data *dev_data; + struct input_dev *clone_dev; + int error; + + pr_debug("[PETC_IF] device connected\n"); + + dev_data = kzalloc(sizeof(struct petc_if_dev_data), GFP_KERNEL); + if (!dev_data) + return -ENOMEM; + + clone_dev = input_allocate_device(); + if (clone_dev == NULL) { + pr_err("[PETC_IF] Failed to allocate clone_dev\n"); + error = -ENOMEM; + goto err_free; + } + + clone_dev->name = PETC_DEV_NAME; + + memcpy(clone_dev->evbit, dev->evbit, sizeof(dev->evbit)); + memcpy(clone_dev->keybit, dev->keybit, sizeof(dev->keybit)); + memcpy(clone_dev->relbit, dev->relbit, sizeof(dev->relbit)); + memcpy(clone_dev->absbit, dev->absbit, sizeof(dev->absbit)); + memcpy(clone_dev->mscbit, dev->mscbit, sizeof(dev->mscbit)); + memcpy(clone_dev->ledbit, dev->ledbit, sizeof(dev->ledbit)); + memcpy(clone_dev->sndbit, dev->sndbit, sizeof(dev->sndbit)); + memcpy(clone_dev->ffbit, dev->ffbit, sizeof(dev->ffbit)); + memcpy(clone_dev->swbit, dev->swbit, sizeof(dev->swbit)); + memcpy(clone_dev->propbit, dev->propbit, sizeof(dev->propbit)); + + input_set_capability(clone_dev, EV_KEY, KEY_WAKEUP); + + dev_data->handle.dev = dev; + dev_data->handle.handler = handler; + dev_data->handle.name = PETC_HANDLE_NAME; + dev_data->handle.private = dev_data; + dev_data->clone_dev = clone_dev; + dev_data->drv_data = handler->private; + + spin_lock_init(&dev_data->event_list_slock); + INIT_LIST_HEAD(&dev_data->event_list); + INIT_WORK(&dev_data->immediate_work, petc_if_immediate_work_func); + INIT_WORK(&dev_data->register_work, petc_if_register_work_func); + INIT_DELAYED_WORK(&dev_data->delayed_work, petc_if_delayed_work_func); + + error = input_register_handle(&dev_data->handle); + if (error) { + pr_err("[PETC_IF] Failed to register input handle, error %d\n", + error); + goto err_free_device; + } + + error = input_open_device(&dev_data->handle); + if (error) { + pr_err("[PETC_IF] Failed to open input device, error %d\n", + error); + goto err_unregister_handle; + } + + /* The device can't be registered here because connect() is called + * while input_mutex is held, so queue it for registration. + */ + + pr_debug("[PETC_IF] work scheduled to register clone device\n"); + schedule_work(&dev_data->register_work); + + return 0; + +err_unregister_handle: + input_unregister_handle(&dev_data->handle); +err_free_device: + input_free_device(clone_dev); +err_free: + kfree(dev_data); + return error; +} + +static void petc_if_dev_disconnect(struct input_handle *handle) +{ + unsigned long flags; + struct petc_if_dev_data *dev_data = handle->private; + struct petc_if_drv_data *drv_data = dev_data->drv_data; + + pr_debug("[PETC_IF] device disconnected\n"); + + input_close_device(handle); + input_unregister_handle(handle); + + /* The device can't be removed here because disconnect() is called + * while input_mutex is held, so queue it for removal. + */ + + spin_lock_irqsave(&drv_data->device_list_slock, flags); + list_del(&dev_data->node); + list_add_tail(&dev_data->node, &drv_data->removed_device_list); + spin_unlock_irqrestore(&drv_data->device_list_slock, flags); + + schedule_work(&drv_data->remove_devices_work); +} + +static const struct input_device_id petc_if_device_ids[] = { + { + .flags = INPUT_DEVICE_ID_MATCH_EVBIT | + INPUT_DEVICE_ID_MATCH_KEYBIT, + .evbit = { [BIT_WORD(EV_KEY)] = BIT_MASK(EV_KEY) }, + .keybit = { [BIT_WORD(KEY_POWER)] = BIT_MASK(KEY_POWER) }, + }, /* match on devices with a power key */ + {}, /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(input, petc_if_device_ids); + +static struct input_handler petc_if_handler = { + .filter = petc_if_filter, + .match = petc_if_match, + .connect = petc_if_dev_connect, + .disconnect = petc_if_dev_disconnect, + .name = PETC_DEV_NAME, + .id_table = petc_if_device_ids, +}; + +void petc_if_display_suspend(struct petc_if_drv_data *drv_data) +{ + pr_debug("[PETC_IF] display suspend\n"); + drv_data->ap_screen_on = false; +} + +void petc_if_display_resume(struct petc_if_drv_data *drv_data) +{ + struct petc_if_dev_data *dev_data; + unsigned long flags; + + pr_debug("[PETC_IF] display resume\n"); + drv_data->ap_screen_on = true; + + /* Immediately dispatch queued events on clone devices */ + + spin_lock_irqsave(&drv_data->device_list_slock, flags); + + list_for_each_entry(dev_data, &drv_data->device_list, node) { + if (cancel_delayed_work(&dev_data->delayed_work)) { + pr_debug( + "[PETC_IF] delayed work scheduled immediately\n"); + schedule_work(&dev_data->immediate_work); + } + } + + spin_unlock_irqrestore(&drv_data->device_list_slock, flags); +} + +static void petc_if_drm_panel_notifier_callback( + enum panel_event_notifier_tag tag, + struct panel_event_notification *notification, void *data) +{ + struct petc_if_drv_data *drv_data = data; + + if (!notification) { + return; + } + + switch (notification->notif_type) { + case DRM_PANEL_EVENT_UNBLANK: + if (!notification->notif_data.early_trigger) { + petc_if_display_resume(drv_data); + } + break; + + case DRM_PANEL_EVENT_BLANK: + case DRM_PANEL_EVENT_BLANK_LP: + if (notification->notif_data.early_trigger) { + petc_if_display_suspend(drv_data); + } + break; + default: + break; + } + + return; +} + +static int petc_if_register_panel_notifier(struct petc_if_drv_data *drv_data) +{ + struct device_node *np = drv_data->pdev->dev.of_node; + struct device_node *pnode; + struct drm_panel *panel; + void *cookie = NULL; + int i, count, ret; + + count = of_count_phandle_with_args(np, "display-panels", NULL); + if (count <= 0) { + pr_err("[PETC_IF] failed to find display-panels, count=%d\n", + count); + return -ENODEV; + } + + for (i = 0; i < count; i++) { + pnode = of_parse_phandle(np, "display-panels", i); + if (!pnode) { + pr_err("[PETC_IF] failed to parse display-panels[%d]\n", + i); + return -ENODEV; + } + + panel = of_drm_find_panel(pnode); + of_node_put(pnode); + if (!IS_ERR(panel)) { + drv_data->active_panel = panel; + break; + } + } + + if (!drv_data->active_panel) { + pr_err("[PETC_IF] failed to find active panel\n"); + return -ENODEV; + } + +#if defined(CONFIG_DRM_PANEL) +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0) +#error "Registration with drm_panel_notifier_register not implemented" +#else +#if IS_ENABLED(CONFIG_QCOM_PANEL_EVENT_NOTIFIER) + cookie = panel_event_notifier_register( + PANEL_EVENT_NOTIFICATION_PRIMARY, + PANEL_EVENT_NOTIFIER_CLIENT_INPUT_FILTER, drv_data->active_panel, + &petc_if_drm_panel_notifier_callback, drv_data); + if (IS_ERR(cookie)) { + ret = PTR_ERR(cookie); + pr_err("[PETC_IF] failed to register panel event notifier, ret=%d\n", + ret); + return ret; + } +#endif +#endif +#elif defined(_MSM_DRM_NOTIFY_H_) +#error "Registration with msm_drm_register_client not implemented" +#elif defined(CONFIG_FB) +#error "Registration with fb_register_client not implemented" +#elif defined(CONFIG_HAS_EARLYSUSPEND) +#error "Registration with register_early_suspend not implemented" +#else +#error "Unknown display panel notification registration method" +#endif + + pr_debug("[PETC_IF] register panel notifier successful\n"); + drv_data->notifier_cookie = cookie; + return 0; +} + +static void petc_if_unregister_panel_notifier(struct petc_if_drv_data *drv_data) +{ + if (drv_data->notifier_cookie) + panel_event_notifier_unregister(drv_data->notifier_cookie); +} + +static int petc_if_probe(struct platform_device *pdev) +{ + struct petc_if_drv_data *drv_data; + int ret; + + pr_debug("[PETC_IF] loading driver\n"); + + drv_data = kzalloc(sizeof(struct petc_if_drv_data), GFP_KERNEL); + if (!drv_data) + return -ENOMEM; + + platform_set_drvdata(pdev, drv_data); + drv_data->pdev = pdev; + + spin_lock_init(&drv_data->device_list_slock); + INIT_LIST_HEAD(&drv_data->device_list); + INIT_LIST_HEAD(&drv_data->removed_device_list); + INIT_WORK(&drv_data->remove_devices_work, + petc_if_remove_devices_work_func); + + ret = petc_if_register_panel_notifier(drv_data); + if (ret) { + pr_err("[PETC_IF] failed to register panel notifier, errno:%d\n", + ret); + goto err_free; + } + + petc_if_handler.private = drv_data; + + ret = input_register_handler(&petc_if_handler); + if (ret) { + pr_err("[PETC_IF] failed to register input handler, errno:%d\n", + ret); + petc_if_handler.private = NULL; + goto err_unregister_notifier; + } + + return 0; + +err_unregister_notifier: + petc_if_unregister_panel_notifier(drv_data); +err_free: + kfree(drv_data); + return ret; +} + +static int petc_if_remove(struct platform_device *pdev) +{ + struct petc_if_drv_data *drv_data = platform_get_drvdata(pdev); + petc_if_handler.private = NULL; + + pr_debug("[PETC_IF] unloading driver\n"); + + input_unregister_handler(&petc_if_handler); + petc_if_unregister_panel_notifier(drv_data); + flush_work(&drv_data->remove_devices_work); + kfree(drv_data); + + return 0; +} + +static const struct of_device_id petc_if_dt_match[] = { + { .compatible = "google,petc_if" }, + {}, +}; +MODULE_DEVICE_TABLE(of, petc_if_match); + +static struct platform_driver petc_if_driver = { + .driver = { + .name = PETC_DEV_NAME, + .of_match_table = of_match_ptr(petc_if_dt_match), + }, + .probe = petc_if_probe, + .remove = petc_if_remove}; + +module_platform_driver(petc_if_driver); + +MODULE_AUTHOR("Google"); +MODULE_DESCRIPTION("Google PETC input filter driver"); +MODULE_LICENSE("GPL"); diff --git a/petc_input_filter/petc_input_filter.h b/petc_input_filter/petc_input_filter.h new file mode 100644 index 0000000..56a4694 --- /dev/null +++ b/petc_input_filter/petc_input_filter.h @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2023 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#ifndef _PETC_INPUT_FILTER_H_ +#define _PETC_INPUT_FILTER_H_ + +#define PETC_DEV_NAME "petc" +#define PETC_HANDLE_NAME "petc-if" + +#endif |