dlkm: Add focaltech fps module

Add focaltech fps module to bring up side fps
FT9391.

Change-Id: I52a4b1a328a7e7135ab8743cdad76c513a4ff9eb
Signed-off-by: Yan Zhang <zhangy26@motorola.com>
Reviewed-on: https://gerrit.mot.com/2085146
SME-Granted: SME Approvals Granted
SLTApproved: Slta Waiver
Tested-by: Jira Key
Reviewed-by: Xiangpo Zhao <zhaoxp3@motorola.com>
Submit-Approved: Jira Key
This commit is contained in:
Yan Zhang 2021-10-08 12:16:55 +08:00
commit da19276c28
8 changed files with 1321 additions and 0 deletions

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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 <linux/module.h>
#include <linux/printk.h>
#include <linux/signal.h>
#include <linux/init.h>
#include <linux/poll.h>
#include <linux/bug.h>
#include <linux/types.h>
#include <linux/param.h>
#include <linux/errno.h>
#include <linux/input.h>
#include <linux/workqueue.h>
#include <linux/interrupt.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/miscdevice.h>
#include <linux/fb.h>
#include <linux/notifier.h>
#if 0
#ifdef CONFIG_PM_WAKELOCKS
#include <linux/pm_wakeup.h>
#else
#include <linux/wakelock.h>
#endif
#endif
#ifdef CONFIG_COMPAT
#include <linux/compat.h>
#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");

View file

@ -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 <linux/ioctl.h>
/* 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__ */

View file

@ -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__ */

View file

@ -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 <tag>[: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__ */

View file

@ -0,0 +1,186 @@
/**
* plat-msm.c
*
**/
#include <linux/stddef.h>
#include <linux/bug.h>
#include <linux/delay.h>
#include <linux/pinctrl/consumer.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
#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";
}