diff --git a/drivers/input/misc/focal_fps_mmi/Android.mk b/drivers/input/misc/focal_fps_mmi/Android.mk new file mode 100644 index 000000000000..9a9b5a929fa4 --- /dev/null +++ b/drivers/input/misc/focal_fps_mmi/Android.mk @@ -0,0 +1,16 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +ifeq ($(DLKM_INSTALL_TO_VENDOR_OUT),true) +FOCAL_FPS_MMI_MODULE_PATH := $(TARGET_OUT_VENDOR)/lib/modules/ +else +FOCAL_FPS_MMI_MODULE_PATH := $(KERNEL_MODULES_OUT) +endif + + +include $(CLEAR_VARS) +LOCAL_MODULE := focal_fps_mmi.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(FOCAL_FPS_MMI_MODULE_PATH) +include $(DLKM_DIR)/AndroidKernelModule.mk + diff --git a/drivers/input/misc/focal_fps_mmi/Kbuild b/drivers/input/misc/focal_fps_mmi/Kbuild new file mode 100644 index 000000000000..283ba2bb2662 --- /dev/null +++ b/drivers/input/misc/focal_fps_mmi/Kbuild @@ -0,0 +1,41 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include \ + -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/input/misc/focal_fps_mmi + + + + + + + +# Early-logging level for ff_hal, ff_ta and driver as well. Note that this +# logging level only takes effect before ff_config module is loaded. +# +# Options: +# 0 - FF_LOG_LEVEL_ALL, All +# 1 - FF_LOG_LEVEL_VBS, Verbose +# 2 - FF_LOG_LEVEL_DBG, Debug +# 3 - FF_LOG_LEVEL_INF, Info +# 4 - FF_LOG_LEVEL_WRN, Warning +# 5 - FF_LOG_LEVEL_ERR, Error +EXTRA_CFLAGS += -D__FF_BUILD_DATE=\"$(shell date +%Y%m%d)\" +EXTRA_CFLAGS += -D__FF_EARLY_LOG_LEVEL=2 + + +focal_fps_mmi-y += ff_ctl.o plat-msm.o + +ifneq ($(filter m y,$(CONFIG_INPUT_EGISTEC_FPS_NAVI)),) + EXTRA_CFLAGS += -DCONFIG_INPUT_EGISTEC_FPS_NAVI + ets_fps_mmi-y += ets_navi_input.o +endif +ifneq ($(filter m y,$(CONFIG_INPUT_EGISTEC_FPS_NAVI_VERTICAL)),) + EXTRA_CFLAGS += -DCONFIG_INPUT_EGISTEC_FPS_NAVI_VERTICAL +endif +ifeq ($(TARGET_BUILD_VARIANT),userdebug) + EXTRA_CFLAGS += -DFP_SPI_DEBUG +endif + +obj-m += focal_fps_mmi.o + + diff --git a/drivers/input/misc/focal_fps_mmi/Makefile b/drivers/input/misc/focal_fps_mmi/Makefile new file mode 100644 index 000000000000..e2e207607578 --- /dev/null +++ b/drivers/input/misc/focal_fps_mmi/Makefile @@ -0,0 +1,13 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean + + + diff --git a/drivers/input/misc/focal_fps_mmi/ff_ctl.c b/drivers/input/misc/focal_fps_mmi/ff_ctl.c new file mode 100644 index 000000000000..8b674b4091cc --- /dev/null +++ b/drivers/input/misc/focal_fps_mmi/ff_ctl.c @@ -0,0 +1,737 @@ +/** + * The device control driver for FocalTech's fingerprint sensor. + * + * Copyright (C) 2016-2017 FocalTech Systems Co., Ltd. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if 0 +#ifdef CONFIG_PM_WAKELOCKS +#include +#else +#include +#endif +#endif + +#ifdef CONFIG_COMPAT +#include +#endif + +#include "ff_log.h" +#include "ff_err.h" +#include "ff_ctl.h" + +/* + * Define the driver version string. + */ +#define FF_DRV_VERSION "v2.1.2" + +/* + * Define the driver name. + */ +#define FF_DRV_NAME "focaltech_fp" + +/* + * Driver context definition and its singleton instance. + */ +typedef struct { + struct miscdevice miscdev; + int irq_num; + struct work_struct work_queue; + struct fasync_struct *async_queue; + struct input_dev *input; + struct notifier_block fb_notifier; +#if 0 + struct wakeup_source *wake_lock; +//#else + struct wake_lock *wake_lock; +#endif + bool b_driver_inited; + bool b_config_dirtied; +} ff_ctl_context_t; +static ff_ctl_context_t *g_context = NULL; + +/* + * Driver configuration. + */ +static ff_driver_config_t driver_config; +ff_driver_config_t *g_config = NULL; +//////////////////////////////////////////////////////////////////////////////// +/// Logging driver to logcat through uevent mechanism. + +# undef LOG_TAG +#define LOG_TAG "ff_ctl" + +/* + * Log level can be runtime configurable by 'FF_IOC_SYNC_CONFIG'. + */ +ff_log_level_t g_log_level = 3;//__FF_EARLY_LOG_LEVEL; +#define MAX_UP_TIME (1 * MSEC_PER_SEC) +int ff_log_printf(ff_log_level_t level, const char *tag, const char *fmt, ...) +{ + /* Prepare the storage. */ + va_list ap; + static char uevent_env_buf[128]; + char *ptr = uevent_env_buf; + int n, available = sizeof(uevent_env_buf); + + /* Fill logging level. */ + available -= sprintf(uevent_env_buf, "FF_LOG=%1d", level); + ptr += strlen(uevent_env_buf); + + /* Fill logging message. */ + va_start(ap, fmt); + vsnprintf(ptr, available, fmt, ap); + va_end(ap); + + /* Send to ff_device. */ + if (likely(g_context) && likely(g_config) && unlikely(g_config->logcat_driver)) { + char *uevent_env[2] = {uevent_env_buf, NULL}; + kobject_uevent_env(&g_context->miscdev.this_device->kobj, KOBJ_CHANGE, uevent_env); + } + + /* Native output. */ + switch (level) { + case FF_LOG_LEVEL_ERR: + n = printk(KERN_ERR FF_DRV_NAME": %s\n", ptr); + break; + case FF_LOG_LEVEL_WRN: + n = printk(KERN_WARNING FF_DRV_NAME": %s\n", ptr); + break; + case FF_LOG_LEVEL_INF: + n = printk(KERN_INFO FF_DRV_NAME": %s\n", ptr); + break; + case FF_LOG_LEVEL_DBG: + case FF_LOG_LEVEL_VBS: + default: + n = printk(KERN_DEBUG FF_DRV_NAME": %s\n", ptr); + break; + } + return n; +} + +const char *ff_err_strerror(int err) +{ + static char errstr[32] = {'\0', }; + + switch (err) { + case FF_SUCCESS : return "Success"; + case FF_ERR_INTERNAL: return "Internal error"; + + /* Base on unix errno. */ + case FF_ERR_NOENT: return "No such file or directory"; + case FF_ERR_INTR : return "Interrupted"; + case FF_ERR_IO : return "I/O error"; + case FF_ERR_AGAIN: return "Try again"; + case FF_ERR_NOMEM: return "Out of memory"; + case FF_ERR_BUSY : return "Resource busy / Timeout"; + + /* Common error. */ + case FF_ERR_BAD_PARAMS : return "Bad parameter(s)"; + case FF_ERR_NULL_PTR : return "Null pointer"; + case FF_ERR_BUF_OVERFLOW: return "Buffer overflow"; + case FF_ERR_BAD_PROTOCOL: return "Bad protocol"; + case FF_ERR_SENSOR_SIZE : return "Wrong sensor dimension"; + case FF_ERR_NULL_DEVICE : return "Device not found"; + case FF_ERR_DEAD_DEVICE : return "Device is dead"; + case FF_ERR_REACH_LIMIT : return "Up to the limit"; + case FF_ERR_REE_TEMPLATE: return "Template store in REE"; + case FF_ERR_NOT_TRUSTED : return "Untrusted enrollment"; + + default: + sprintf(errstr, "%d", err); + break; + } + + return (const char *)errstr; +} + +//////////////////////////////////////////////////////////////////////////////// + +/* See plat-xxxx.c for platform dependent implementation. */ +extern int ff_ctl_init_pins(int *irq_num); +extern int ff_ctl_free_pins(void); +#ifdef FF_SPI_SET +extern int ff_ctl_enable_spiclk(bool on); +#endif +extern int ff_ctl_enable_power(bool on); +extern int ff_ctl_reset_device(void); +extern const char *ff_ctl_arch_str(void); + + +#ifdef CHIP_TYPE_FT9304 +/* See chip-xxxx.c for chip dependent implementation. */ +extern int ff_chip_init(void); +#endif + +static int ff_ctl_enable_irq(bool on) +{ + int err = 0; + FF_LOGV("'%s' enter.", __func__); + FF_LOGD("irq: '%s'.", on ? "enable" : "disabled"); + + if (unlikely(!g_context)) { + return (-ENOSYS); + } + + if (on) { + enable_irq(g_context->irq_num); + } else { + disable_irq(g_context->irq_num); + } + + FF_LOGV("'%s' leave.", __func__); + return err; +} + +static void ff_ctl_device_event(struct work_struct *ws) +{ + ff_ctl_context_t *ctx = container_of(ws, ff_ctl_context_t, work_queue); + char *uevent_env[2] = {"FF_INTERRUPT", NULL}; + FF_LOGV("'%s' enter.", __func__); + + FF_LOGD("%s(irq = %d, ..) toggled.", __func__, ctx->irq_num); +#if 0 + __pm_wakeup_event(g_context->wake_lock, jiffies_to_msecs(2*HZ)); +//#else + wake_lock_timeout(g_context->wake_lock, 2 * HZ); // 2 seconds. +#endif + pm_wakeup_event(g_context->miscdev.this_device, MAX_UP_TIME); + kobject_uevent_env(&ctx->miscdev.this_device->kobj, KOBJ_CHANGE, uevent_env); + + FF_LOGV("'%s' leave.", __func__); +} + +static irqreturn_t ff_ctl_device_irq(int irq, void *dev_id) +{ + ff_ctl_context_t *ctx = (ff_ctl_context_t *)dev_id; + disable_irq_nosync(irq); + + if (likely(irq == ctx->irq_num)) { + if (g_config && g_config->enable_fasync && g_context->async_queue) { + kill_fasync(&g_context->async_queue, SIGIO, POLL_IN); + } else { + schedule_work(&ctx->work_queue); + } + } + + enable_irq(irq); + return IRQ_HANDLED; +} + +static int ff_ctl_report_key_event(struct input_dev *input, ff_key_event_t *kevent) +{ + int err = 0; + FF_LOGV("'%s' enter.", __func__); + + input_report_key(input, kevent->code, kevent->value); + input_sync(input); + + FF_LOGV("'%s' leave.", __func__); + return err; +} + +static const char *ff_ctl_get_version(void) +{ + static char version[FF_DRV_VERSION_LEN] = {'\0', }; + FF_LOGV("'%s' enter.", __func__); + + /* Version info. */ + version[0] = '\0'; + strcat(version, FF_DRV_VERSION); +#ifdef __FF_SVN_REV + if (strlen(__FF_SVN_REV) > 0) { + sprintf(version, "%s-r%s", version, __FF_SVN_REV); + } +#endif +#ifdef __FF_BUILD_DATE + strcat(version, "-"__FF_BUILD_DATE); +#endif + sprintf(version, "%s-%s", version, ff_ctl_arch_str()); + FF_LOGD("version: '%s'.", version); + + FF_LOGV("'%s' leave.", __func__); + return (const char *)version; +} +static char screen_state[1]; +static int ff_ctl_fb_notifier_callback(struct notifier_block *nb, unsigned long action, void *data) +{ + struct fb_event *event; + int blank; + char *uevent_env[2]; + + /* If we aren't interested in this event, skip it immediately ... */ + if (action != FB_EVENT_BLANK /* FB_EARLY_EVENT_BLANK */) { + return NOTIFY_DONE; + } + + FF_LOGV("'%s' enter.", __func__); + + event = (struct fb_event *)data; + blank = *(int *)event->data; + + switch (blank) { + case FB_BLANK_UNBLANK: + uevent_env[0] = "FF_SCREEN_ON"; + screen_state[0] = 0; + break; + case FB_BLANK_POWERDOWN: + uevent_env[0] = "FF_SCREEN_OFF"; + screen_state[0] = 1; + break; + default: + uevent_env[0] = "FF_SCREEN_??"; + break; + } + uevent_env[1] = NULL; +// kobject_uevent_env(&g_context->miscdev.this_device->kobj, KOBJ_CHANGE, uevent_env); + kill_fasync(&g_context->async_queue, SIGIO, POLL_IN); + + FF_LOGV("'%s' leave.", __func__); + return NOTIFY_OK; +} + +static int ff_ctl_register_input(void) +{ + int err = 0; + FF_LOGV("'%s' enter.", __func__); + + /* Allocate the input device. */ + g_context->input = input_allocate_device(); + if (!g_context->input) { + FF_LOGE("input_allocate_device() failed."); + return (-ENOMEM); + } + + /* Register the key event capabilities. */ + if (g_config) { + input_set_capability(g_context->input, EV_KEY, g_config->keycode_nav_left ); + input_set_capability(g_context->input, EV_KEY, g_config->keycode_nav_right ); + input_set_capability(g_context->input, EV_KEY, g_config->keycode_nav_up ); + input_set_capability(g_context->input, EV_KEY, g_config->keycode_nav_down ); + input_set_capability(g_context->input, EV_KEY, g_config->keycode_double_click); + input_set_capability(g_context->input, EV_KEY, g_config->keycode_click ); + input_set_capability(g_context->input, EV_KEY, g_config->keycode_long_press ); + input_set_capability(g_context->input, EV_KEY, g_config->keycode_simulation ); + } + + /* Register the allocated input device. */ + g_context->input->name = "uinput-focal"; + err = input_register_device(g_context->input); + if (err) { + FF_LOGE("input_register_device(..) = %d.", err); + input_free_device(g_context->input); + g_context->input = NULL; + return (-ENODEV); + } + + FF_LOGV("'%s' leave.", __func__); + return err; +} + +static int ff_ctl_free_driver(void) +{ + int err = 0; + FF_LOGV("'%s' enter.", __func__); + + /* Unregister framebuffer event notifier. */ + err = fb_unregister_client(&g_context->fb_notifier); + +#ifdef FF_SPI_SET + /* Disable SPI clock. */ + err = ff_ctl_enable_spiclk(0); +#endif + + /* Disable the fingerprint module's power. */ + err = ff_ctl_enable_power(0); + +#ifdef FF_SPI_SET + /* Unregister the spidev device. */ + err = ff_spi_free(); +#endif + + /* De-initialize the input subsystem. */ + if (g_context->input) { + /* + * Once input device was registered use input_unregister_device() and + * memory will be freed once last reference to the device is dropped. + */ + input_unregister_device(g_context->input); + g_context->input = NULL; + } + + /* Release IRQ resource. */ + if (g_context->irq_num > 0) { + err = disable_irq_wake(g_context->irq_num); + if (err) { + FF_LOGE("disable_irq_wake(%d) = %d.", g_context->irq_num, err); + } + free_irq(g_context->irq_num, (void*)g_context); + g_context->irq_num = -1; + } + + /* Release pins resource. */ + err = ff_ctl_free_pins(); + + FF_LOGV("'%s' leave.", __func__); + return err; +} + +static int ff_ctl_init_driver(void) +{ + int err = 0; + FF_LOGV("'%s' enter.", __func__); + + if (unlikely(!g_context)) { + return (-ENOSYS); + } + + do { + /* Initialize the PWR/SPI/RST/INT pins resource. */ + err = ff_ctl_init_pins(&g_context->irq_num); + if (err > 0) { + g_context->b_config_dirtied = true; + } else \ + if (err) { + FF_LOGE("ff_ctl_init_pins(..) = %d.", err); + break; + } + + /* Register IRQ. */ + err = request_irq(g_context->irq_num, ff_ctl_device_irq, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, "ff_irq", (void*)g_context); + if (err) { + FF_LOGE("request_irq(..) = %d.", err); + break; + } + + /* Wake up the system while receiving the interrupt. */ + err = enable_irq_wake(g_context->irq_num); + if (err) { + FF_LOGE("enable_irq_wake(%d) = %d.", g_context->irq_num, err); + } + + /* Register spidev device. For REE-Emulation solution only. */ + if (g_config && g_config->enable_spidev) { +#ifdef FF_SPI_SET + err = ff_spi_init(); + if (err) { + FF_LOGE("ff_spi_init(..) = %d.", err); + break; + } +#endif + } + } while (0); + + if (err) { + ff_ctl_free_driver(); + return err; + } + + /* Initialize the input subsystem. */ + err = ff_ctl_register_input(); + if (err) { + FF_LOGE("ff_ctl_init_input() = %d.", err); + //return err; + } + + /* Enable the fingerprint module's power at system startup. */ + err = ff_ctl_enable_power(1); + +#ifdef FF_SPI_SET + /* Enable SPI clock. */ + err = ff_ctl_enable_spiclk(1); +#endif + + /* Register screen on/off callback. */ + g_context->fb_notifier.notifier_call = ff_ctl_fb_notifier_callback; + err = fb_register_client(&g_context->fb_notifier); + + + g_context->b_driver_inited = true; + FF_LOGV("'%s' leave.", __func__); + return err; +} + +//////////////////////////////////////////////////////////////////////////////// +// struct file_operations fields. + +static int ff_ctl_fasync(int fd, struct file *filp, int mode) +{ + int err = 0; + FF_LOGV("'%s' enter.", __func__); + + FF_LOGD("%s: mode = 0x%08x.", __func__, mode); + err = fasync_helper(fd, filp, mode, &g_context->async_queue); + + FF_LOGV("'%s' leave.", __func__); + return err; +} + +static long ff_ctl_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + int err = 0; + struct miscdevice *dev = (struct miscdevice *)filp->private_data; + ff_ctl_context_t *ctx = container_of(dev, ff_ctl_context_t, miscdev); + FF_LOGV("'%s' enter.", __func__); + +#if 1 + if (g_log_level <= FF_LOG_LEVEL_DBG) { + static const char *cmd_names[] = { + "FF_IOC_INIT_DRIVER", "FF_IOC_FREE_DRIVER", + "FF_IOC_RESET_DEVICE", + "FF_IOC_ENABLE_IRQ", "FF_IOC_DISABLE_IRQ", + "FF_IOC_ENABLE_SPI_CLK", "FF_IOC_DISABLE_SPI_CLK", + "FF_IOC_ENABLE_POWER", "FF_IOC_DISABLE_POWER", + "FF_IOC_REPORT_KEY_EVENT", "FF_IOC_SYNC_CONFIG", + "FF_IOC_GET_VERSION", "unknown", + }; + unsigned int _cmd = _IOC_NR(cmd); + if (_cmd > FF_IOC_GET_VERSION) { + _cmd = FF_IOC_GET_VERSION + 1; + } + FF_LOGD("%s(.., %s, ..) invoke.", __func__, cmd_names[_cmd]); + } +#endif + + switch (cmd) { + case FF_IOC_INIT_DRIVER: { + if (g_context->b_driver_inited) { + err = ff_ctl_free_driver(); + } + if (!err) { + err = ff_ctl_init_driver(); + // TODO: Sync the dirty configuration back to HAL. + } + break; + } + case FF_IOC_FREE_DRIVER: + if (g_context->b_driver_inited) { + err = ff_ctl_free_driver(); + g_context->b_driver_inited = false; + } + break; + case FF_IOC_RESET_DEVICE: + err = ff_ctl_reset_device(); + break; + case FF_IOC_ENABLE_IRQ: + err = ff_ctl_enable_irq(1); + break; + case FF_IOC_DISABLE_IRQ: + err = ff_ctl_enable_irq(0); + break; + case FF_IOC_ENABLE_SPI_CLK: +#ifdef FF_SPI_SET + err = ff_ctl_enable_spiclk(1); +#endif + break; + case FF_IOC_DISABLE_SPI_CLK: +#ifdef FF_SPI_SET + err = ff_ctl_enable_spiclk(0); +#endif + break; + case FF_IOC_ENABLE_POWER: + err = ff_ctl_enable_power(1); + break; + case FF_IOC_DISABLE_POWER: + err = ff_ctl_enable_power(0); + break; + case FF_IOC_REPORT_KEY_EVENT: { + ff_key_event_t kevent; + if (copy_from_user(&kevent, (ff_key_event_t *)arg, sizeof(ff_key_event_t))) { + FF_LOGE("copy_from_user(..) failed."); + err = (-EFAULT); + break; + } + err = ff_ctl_report_key_event(ctx->input, &kevent); + break; + } + case FF_IOC_SYNC_CONFIG: { + if (copy_from_user(&driver_config, (ff_driver_config_t *)arg, sizeof(ff_driver_config_t))) { + FF_LOGE("copy_from_user(..) failed."); + err = (-EFAULT); + break; + } + g_config = &driver_config; + + /* Take logging level effect. */ + g_log_level = 3;//g_config->log_level; + break; + } + case FF_IOC_GET_VERSION: { + if (copy_to_user((void *)arg, ff_ctl_get_version(), FF_DRV_VERSION_LEN)) { + FF_LOGE("copy_to_user(..) failed."); + err = (-EFAULT); + break; + } + break; + } + case FF_IOC_GET_SCREEN_STATE: { + if (copy_to_user((void *)arg, (const char*)screen_state, 1)) { + FF_LOGE("copy_to_user(..) failed."); + err = (-EFAULT); + break; + } + break; + } + default: + err = (-EINVAL); + break; + } + + FF_LOGV("'%s' leave.", __func__); + return err; +} + +static int ff_ctl_open(struct inode *inode, struct file *filp) +{ + FF_LOGD("'%s' nothing to do.", __func__); + return 0; +} + +static int ff_ctl_release(struct inode *inode, struct file *filp) +{ + int err = 0; + FF_LOGV("'%s' enter.", __func__); + + /* Remove this filp from the asynchronously notified filp's. */ + err = ff_ctl_fasync(-1, filp, 0); + + FF_LOGV("'%s' leave.", __func__); + return err; +} + +#ifdef CONFIG_COMPAT +static long ff_ctl_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + int err = 0; + FF_LOGV("focal '%s' enter.\n", __func__); + + err = ff_ctl_ioctl(filp, cmd, (unsigned long)compat_ptr(arg)); + + FF_LOGV("'%s' leave.", __func__); + return err; +} +#endif +/////////////////////////////////////////////////////////////////////////////// + +static struct file_operations ff_ctl_fops = { + .owner = THIS_MODULE, + .fasync = ff_ctl_fasync, + .unlocked_ioctl = ff_ctl_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = ff_ctl_compat_ioctl, +#endif + .open = ff_ctl_open, + .release = ff_ctl_release, +}; + +static ff_ctl_context_t ff_ctl_context = { + .miscdev = { + .minor = MISC_DYNAMIC_MINOR, + .name = FF_DRV_NAME, + .fops = &ff_ctl_fops, + }, 0, +}; + +static int __init ff_ctl_driver_init(void) +{ + int err = 0; + FF_LOGV("'%s' enter.", __func__); + + /* Register as a miscellaneous device. */ + err = misc_register(&ff_ctl_context.miscdev); + if (err) { + FF_LOGE("misc_register(..) = %d.", err); + return err; + } + + /* Init the interrupt workqueue. */ + INIT_WORK(&ff_ctl_context.work_queue, ff_ctl_device_event); + +#if 0 + ff_ctl_context.wake_lock = wakeup_source_create("ff_wake_lock"); + if (!ff_ctl_context.wake_lock) { + FF_LOGE("wakeup_source_create failed."); + return -ENOMEM; + } else { + wakeup_source_add(ff_ctl_context.wake_lock); + } +//#else + wake_lock_init(ff_ctl_context.wake_lock, WAKE_LOCK_SUSPEND, "ff_wake_lock"); +#endif + + /* Assign the context instance. */ + g_context = &ff_ctl_context; + + g_context->b_driver_inited = false; + /* Init the wake lock. */ + err = device_init_wakeup(g_context->miscdev.this_device, true); + FF_LOGI("FocalTech fingerprint device control driver registered err=%d.", err); + FF_LOGV("'%s' leave.", __func__); + return err; +} + +static void __exit ff_ctl_driver_exit(void) +{ + int err = 0; + FF_LOGV("'%s' enter.", __func__); + + /* Release the HW resources if needs. */ + if (g_context->b_driver_inited) { + err = ff_ctl_free_driver(); + g_context->b_driver_inited = false; + } + + /* De-init the wake lock. */ + err = device_init_wakeup(g_context->miscdev.this_device, false); + #if 0 + wakeup_source_destroy(g_context->wake_lock); +//#else + wake_lock_destroy(g_context->wake_lock); +#endif + + /* Unregister the miscellaneous device. */ + misc_deregister(&g_context->miscdev); + + /* 'g_context' could not use any more. */ + g_context = NULL; + + FF_LOGI("FocalTech fingerprint device control driver released."); + FF_LOGV("'%s' leave.", __func__); +} + +module_init(ff_ctl_driver_init); +module_exit(ff_ctl_driver_exit); + +MODULE_DESCRIPTION("The device control driver for FocalTech's fingerprint sensor."); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_AUTHOR("FocalTech Fingerprint R&D department"); + diff --git a/drivers/input/misc/focal_fps_mmi/ff_ctl.h b/drivers/input/misc/focal_fps_mmi/ff_ctl.h new file mode 100644 index 000000000000..7bec3ceeb316 --- /dev/null +++ b/drivers/input/misc/focal_fps_mmi/ff_ctl.h @@ -0,0 +1,118 @@ +/** + * User space driver API for FocalTech's fingerprint device. + * ATTENTION: Do NOT edit this file unless the corresponding driver changed. + * + * Copyright (C) 2016-2017 FocalTech Systems Co., Ltd. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * 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 __FF_CTRL_API_H__ +#define __FF_CTRL_API_H__ + +#include + +/* Device node. */ +#define FF_CTL_DEV_NAME "/dev/focaltech_fp" + +/* Max driver version buffer length. */ +#define FF_DRV_VERSION_LEN 32 + +/* set chip type */ +/* +#define CHIP_TYPE_FT9304 +*/ +#define CHIP_TYPE_MSM + +/* set spi */ +//#define FF_SPI_SET + +/* set vdd gpio */ +//#define FF_VDD_GPIO + +/* set iovcc gpio */ +//#define FF_IOVCC_GPIO + +typedef struct { + unsigned int code; + int value; +} ff_key_event_t; + +typedef struct { + /* Using asynchronous notification mechanism instead of NETLINK. */ + bool enable_fasync; + + /* Gesture(Key emulation & Navigation) key codes. */ + int32_t keycode_nav_left; + int32_t keycode_nav_right; + int32_t keycode_nav_up; + int32_t keycode_nav_down; + int32_t keycode_double_click; + int32_t keycode_click; + int32_t keycode_long_press; + int32_t keycode_simulation; + + /* For '/dev/spidevB.C' of REE-Emulation. */ + bool enable_spidev; + int32_t spidev_bus; + int32_t spidev_c_s; + + /* For obsolete driver that doesn't support device tree. */ + int32_t gpio_mosi_pin; + int32_t gpio_miso_pin; + int32_t gpio_ck_pin; + int32_t gpio_cs_pin; + int32_t gpio_rst_pin; + int32_t gpio_int_pin; + int32_t gpio_power_pin; + int32_t gpio_iovcc_pin; + + + /* Logging driver to logcat through uevent mechanism. */ + int32_t log_level; + bool logcat_driver; +} ff_driver_config_t; + +/* Magic code for IOCTL-subsystem, 'f'(0x66) means '[F]ocalTech'. */ +#define FF_IOC_MAGIC 'f' + +/* Allocate/Release driver resource (GPIO/SPI etc.). */ +#define FF_IOC_INIT_DRIVER _IO(FF_IOC_MAGIC, 0x00) +#define FF_IOC_FREE_DRIVER _IO(FF_IOC_MAGIC, 0x01) + +/* HW reset the fingerprint module. */ +#define FF_IOC_RESET_DEVICE _IO(FF_IOC_MAGIC, 0x02) + +/* Low-level IRQ control. */ +#define FF_IOC_ENABLE_IRQ _IO(FF_IOC_MAGIC, 0x03) +#define FF_IOC_DISABLE_IRQ _IO(FF_IOC_MAGIC, 0x04) + +/* SPI bus clock control, for power-saving purpose. */ +#define FF_IOC_ENABLE_SPI_CLK _IO(FF_IOC_MAGIC, 0x05) +#define FF_IOC_DISABLE_SPI_CLK _IO(FF_IOC_MAGIC, 0x06) + +/* Fingerprint module power control. */ +#define FF_IOC_ENABLE_POWER _IO(FF_IOC_MAGIC, 0x07) +#define FF_IOC_DISABLE_POWER _IO(FF_IOC_MAGIC, 0x08) + +/* Androind system-wide key event, for navigation purpose. */ +#define FF_IOC_REPORT_KEY_EVENT _IOW(FF_IOC_MAGIC, 0x09, ff_key_event_t) + +/* Sync 'ff_driver_config_t', the driver configuration. */ +#define FF_IOC_SYNC_CONFIG _IOWR(FF_IOC_MAGIC, 0x0a, ff_driver_config_t) + +/* Query the driver version string. */ +#define FF_IOC_GET_VERSION _IOR(FF_IOC_MAGIC, 0x0b, const char) + +/* get screen state. */ +#define FF_IOC_GET_SCREEN_STATE _IOR(FF_IOC_MAGIC, 0x0c, const char) + +#endif /* __FF_CTRL_API_H__ */ diff --git a/drivers/input/misc/focal_fps_mmi/ff_err.h b/drivers/input/misc/focal_fps_mmi/ff_err.h new file mode 100644 index 000000000000..fce3ac3ae277 --- /dev/null +++ b/drivers/input/misc/focal_fps_mmi/ff_err.h @@ -0,0 +1,101 @@ +/** + * ${ANDROID_BUILD_TOP}/vendor/focaltech/src/base/focaltech/ff_err.h + * + * Copyright (C) 2014-2017 FocalTech Systems Co., Ltd. All Rights Reserved. + * +**/ + +#ifndef __FF_ERROR_H__ +#define __FF_ERROR_H__ + +#include "ff_log.h" /* FF_LOGE(..) */ + +// +// All the following macros will return while error. +// + +#define FF_CHECK_ERR(expr) \ + do { \ + int __e = (expr); \ + if (__e) { \ + FF_LOGE("'%s'.", ff_err_strerror(__e)); \ + return (__e); \ + } \ + } while (0) +/* End of FF_CHECK_ERR */ + +#define FF_CHECK_PTR(ptr) \ + do { \ + if ((ptr) == NULL) { \ + FF_CHECK_ERR(FF_ERR_NULL_PTR); \ + } \ + } while (0) +/* End of FF_CHECK_PTR */ + +#define FF_FAILURE_RETRY(expr, retry) \ + do { \ + int __e, i = 0, j = retry; \ + do { \ + __e = (expr); \ + if (!__e) break; \ + if (++i <= j) { \ + FF_LOGW("'"#expr"' failed, try again (%d/%d).", i, j); \ + } else { \ + FF_LOGE("'%s'.", ff_err_strerror(__e)); \ + return (__e); \ + } \ + } while (true); \ + } while (0) +/* End of FF_FAILURE_RETRY */ + +typedef enum { + FF_SUCCESS = 0, /* No error. */ + FF_ERR_INTERNAL = -1, /* Generic error. */ + + /* Base on unix errno. */ + FF_ERR_NOENT = -2, + FF_ERR_INTR = -4, + FF_ERR_IO = -5, + FF_ERR_AGAIN = -11, + FF_ERR_NOMEM = -12, + FF_ERR_BUSY = -16, + + /* Common error. */ + FF_ERR_BAD_PARAMS = -200, + FF_ERR_NULL_PTR = -201, + FF_ERR_BUF_OVERFLOW = -202, + FF_ERR_BAD_PROTOCOL = -203, + FF_ERR_SENSOR_SIZE = -204, + FF_ERR_NULL_DEVICE = -205, + FF_ERR_DEAD_DEVICE = -206, + FF_ERR_REACH_LIMIT = -207, + FF_ERR_REE_TEMPLATE = -208, + FF_ERR_NOT_TRUSTED = -209, +} ff_err_t; + +/* + * For some functions that don't return an int(ff_err_t) type error + * code, 'ff_last_error' shall be set if there is an error happened. + */ +extern ff_err_t ff_last_error; + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Return an error description string corresponding to $err. + * + * @params + * err: Error number. + * + * @return + * Static error description string. + */ +const char *ff_err_strerror(int err); + +#ifdef __cplusplus +} +#endif + +#endif /* __FF_ERROR_H__ */ diff --git a/drivers/input/misc/focal_fps_mmi/ff_log.h b/drivers/input/misc/focal_fps_mmi/ff_log.h new file mode 100644 index 000000000000..20bfb9f6736e --- /dev/null +++ b/drivers/input/misc/focal_fps_mmi/ff_log.h @@ -0,0 +1,109 @@ +/** + * ${ANDROID_BUILD_TOP}/vendor/focaltech/src/base/focaltech/ff_log.h + * + * Copyright (C) 2014-2017 FocalTech Systems Co., Ltd. All Rights Reserved. + * +**/ + +#ifndef __FF_LOGGING_H__ +#define __FF_LOGGING_H__ + +/* + * The default LOG_TAG is 'focaltech', using these two lines to define newtag. + * # undef LOG_TAG + * #define LOG_TAG "newtag" + */ +#define LOG_TAG "focaltech" + +/* + * Log level can be used in 'logcat [:priority]', and also be + * used in output control while '__FF_EARLY_LOG_LEVEL' is defined. + */ +typedef enum { + FF_LOG_LEVEL_ALL = 0, + FF_LOG_LEVEL_VBS = 1, /* Verbose */ + FF_LOG_LEVEL_DBG = 2, /* Debug */ + FF_LOG_LEVEL_INF = 3, /* Info */ + FF_LOG_LEVEL_WRN = 4, /* Warning */ + FF_LOG_LEVEL_ERR = 5, /* Error */ +} ff_log_level_t; + +/* + * __FF_EARLY_LOG_LEVEL can be defined as compilation option. + * default level is FF_LOG_LEVEL_ALL(all the logs will be output). + */ +#ifndef __FF_EARLY_LOG_LEVEL +#define __FF_EARLY_LOG_LEVEL FF_LOG_LEVEL_ALL +#endif + +/* + * Log level can be runtime configurable. + */ +extern ff_log_level_t g_log_level; /* = __FF_EARLY_LOG_LEVEL */ + +/* + * Using the following five macros for conveniently logging. + */ +#define FF_LOGV(...) \ + do { \ + if (g_log_level <= FF_LOG_LEVEL_VBS) { \ + ff_log_printf(FF_LOG_LEVEL_VBS, LOG_TAG, __VA_ARGS__); \ + }; \ + } while (0) + +#define FF_LOGD(...) \ + do { \ + if (g_log_level <= FF_LOG_LEVEL_DBG) { \ + ff_log_printf(FF_LOG_LEVEL_DBG, LOG_TAG, __VA_ARGS__); \ + }; \ + } while (0) + +#define FF_LOGI(...) \ + do { \ + if (g_log_level <= FF_LOG_LEVEL_INF) { \ + ff_log_printf(FF_LOG_LEVEL_INF, LOG_TAG, __VA_ARGS__); \ + }; \ + } while (0) + +#define FF_LOGW(...) \ + do { \ + if (g_log_level <= FF_LOG_LEVEL_WRN) { \ + ff_log_printf(FF_LOG_LEVEL_WRN, LOG_TAG, __VA_ARGS__); \ + }; \ + } while (0) + +#define FF_LOGE(format, ...) \ + do { \ + if (g_log_level <= FF_LOG_LEVEL_ERR) { \ + const char *__fname__ = __FILE__, *s = __fname__; \ + do { if (*s == '/') __fname__ = s + 1; } while (*s++); \ + ff_log_printf(FF_LOG_LEVEL_ERR, LOG_TAG, \ + "error at %s[%s:%d]: " format, __FUNCTION__, \ + __fname__, __LINE__, ##__VA_ARGS__); \ + }; \ + } while (0) + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Logging API. Do NOT use it directly but FF_LOF* instead. + * + * @params + * level: Logging level for logcat. + * tag : Logging tag for logcat. + * fmt : See POSIX printf(..). + * ... : See POSIX printf(..). + * + * @return + * The number of characters printed, or a negative value if there + * was an output error. + */ +int ff_log_printf(ff_log_level_t level, const char *tag, const char *fmt, ...); + +#ifdef __cplusplus +} +#endif + +#endif /* __FF_LOGGING_H__ */ diff --git a/drivers/input/misc/focal_fps_mmi/plat-msm.c b/drivers/input/misc/focal_fps_mmi/plat-msm.c new file mode 100644 index 000000000000..8d092149a4cf --- /dev/null +++ b/drivers/input/misc/focal_fps_mmi/plat-msm.c @@ -0,0 +1,186 @@ +/** + * plat-msm.c + * +**/ + +#include +#include +#include +#include +#include +#include +#include + +#include "ff_log.h" +#include "ff_ctl.h" + +# undef LOG_TAG +#define LOG_TAG "msm" + +#define FF_COMPATIBLE_NODE "focaltech,fingerprint" + +/* Define pinctrl state types. */ +typedef enum { + FF_PINCTRL_STATE_PWR_ACT, + FF_PINCTRL_STATE_PWR_CLR, + FF_PINCTRL_STATE_RST_CLR, + FF_PINCTRL_STATE_RST_ACT, + FF_PINCTRL_STATE_INT_ACT, + FF_PINCTRL_STATE_MAXIMUM /* Array size */ +} ff_pinctrl_state_t; + +/* Define pinctrl state names. */ +static const char *g_pinctrl_state_names[FF_PINCTRL_STATE_MAXIMUM] = { + "fpsensor_finger_power_high","fpsensor_finger_power_low","fpsensor_finger_rst_low","fpsensor_finger_rst_high","fpsensor_eint_as_int" +}; + +/* Native context and its singleton instance. */ +typedef struct { + struct pinctrl *pinctrl; + struct pinctrl_state *pin_states[FF_PINCTRL_STATE_MAXIMUM]; +} ff_msm_context_t; +static ff_msm_context_t ff_msm_context, *g_context = &ff_msm_context; + +int ff_ctl_enable_power(bool on); + +int ff_ctl_init_pins(int *irq_num) +{ + int err = 0, i; + int irq_num1 = 0; + struct device_node *dev_node = NULL; + struct platform_device *pdev = NULL; + + printk("'%s' enter.", __func__); + + /* Find device tree node. */ + dev_node = of_find_compatible_node(NULL, NULL, FF_COMPATIBLE_NODE); + if (!dev_node) { + printk("of_find_compatible_node(.., '%s') failed.", FF_COMPATIBLE_NODE); + return (-ENODEV); + } + printk("dev_node :%s",dev_node->name); + + irq_num1 = irq_of_parse_and_map(dev_node, 0); + *irq_num = irq_num1; + printk("irq number is %d.", irq_num1); + + /* Convert to platform device. */ + pdev = of_find_device_by_node(dev_node); + if (!pdev) { + printk("of_find_device_by_node(..) failed."); + return (-ENODEV); + } + + /* Retrieve the pinctrl handler. */ + g_context->pinctrl = devm_pinctrl_get(&pdev->dev); + if (!g_context->pinctrl) { + printk("devm_pinctrl_get(..) failed."); + return (-ENODEV); + } + + printk("register pins."); + /* Register all pins. */ + for (i = 0; i < FF_PINCTRL_STATE_MAXIMUM; ++i) { + g_context->pin_states[i] = pinctrl_lookup_state(g_context->pinctrl, g_pinctrl_state_names[i]); + if (!g_context->pin_states[i]) { + printk("can't find pinctrl state for '%s'.", g_pinctrl_state_names[i]); + err = (-ENODEV); + break; + } + } + if (i < FF_PINCTRL_STATE_MAXIMUM) { + return (-ENODEV); + } + + /* Initialize the INT pin. */ + printk("init int pin."); + err = pinctrl_select_state(g_context->pinctrl, g_context->pin_states[FF_PINCTRL_STATE_INT_ACT]); + + /* Initialize the RST pin. */ + pinctrl_select_state(g_context->pinctrl, g_context->pin_states[FF_PINCTRL_STATE_RST_ACT]); + + ff_ctl_enable_power(true); + + printk("'%s' leave.", __func__); + return err; +} + +int ff_ctl_free_pins(void) +{ + int err = 0; + printk("'%s' enter.", __func__); + + // TODO: + if (g_context->pinctrl) { + pinctrl_put(g_context->pinctrl); + g_context->pinctrl = NULL; + } + printk("'%s' leave.", __func__); + return err; +} + +int ff_ctl_enable_spiclk(bool on) +{ + int err = 0; + FF_LOGV("'%s' enter.", __func__); + FF_LOGD("clock: '%s'.", on ? "enable" : "disabled"); + + if (on) { + // TODO: + } else { + // TODO: + } + + FF_LOGV("'%s' leave.", __func__); + return err; +} + +int ff_ctl_enable_power(bool on) +{ + int err = 0; + printk("'%s' enter.", __func__); + printk("power: '%s'.", on ? "on" : "off"); + + if (unlikely(!g_context->pinctrl)) { + return (-ENOSYS); + } + + if (on) { + err = pinctrl_select_state(g_context->pinctrl, g_context->pin_states[FF_PINCTRL_STATE_PWR_ACT]); + } else { + err = pinctrl_select_state(g_context->pinctrl, g_context->pin_states[FF_PINCTRL_STATE_PWR_CLR]); + } + + printk("'%s' leave.", __func__); + return err; +} + +int ff_ctl_reset_device(void) +{ + int err = 0; + printk("'%s' enter.", __func__); + + if (unlikely(!g_context->pinctrl)) { + return (-ENOSYS); + } + + err = pinctrl_select_state(g_context->pinctrl, g_context->pin_states[FF_PINCTRL_STATE_RST_ACT]); + mdelay(1); + /* 3-1: Pull down RST pin. */ + err = pinctrl_select_state(g_context->pinctrl, g_context->pin_states[FF_PINCTRL_STATE_RST_CLR]); + + /* 3-2: Delay for 10ms. */ + mdelay(10); + + /* Pull up RST pin. */ + err = pinctrl_select_state(g_context->pinctrl, g_context->pin_states[FF_PINCTRL_STATE_RST_ACT]); + + printk("'%s' leave.", __func__); + return err; +} + +const char *ff_ctl_arch_str(void) +{ + return "msm"; +} +