1028 lines
28 KiB
C
Executable file
1028 lines
28 KiB
C
Executable file
/* Copyright (C) MicroArray
|
||
oALOGD("start");
|
||
* MicroArray Fprint Driver Code for REE enviroment
|
||
* madev.c
|
||
* Date: 2017-3-15
|
||
* Version: v4.0.06
|
||
* Author: guq
|
||
* Contact: guq@microarray.com.cn
|
||
*/
|
||
#include "madev.h"
|
||
|
||
#ifdef TEE_ID_COMPATIBLE_MICROTRUST
|
||
// #include <fp_vendor.h>
|
||
// #include "teei_fp.h"
|
||
// #include "tee_client_api.h"
|
||
|
||
struct TEEC_UUID {
|
||
uint32_t timeLow;
|
||
uint16_t timeMid;
|
||
uint16_t timeHiAndVersion;
|
||
uint8_t clockSeqAndNode[8];
|
||
};
|
||
|
||
extern struct TEEC_UUID uuid_fp;
|
||
|
||
#endif
|
||
#if defined(CONFIG_PRIZE_FP_USE_VFP)
|
||
struct regulator *vdd_ldo;
|
||
#endif
|
||
|
||
//prize add by wangyongsheng 20210329 start
|
||
#if IS_ENABLED(CONFIG_PRIZE_HARDWARE_INFO)
|
||
#include "../../../misc/mediatek/prize/hardware_info/hardware_info.h"
|
||
extern struct hardware_info current_fingerprint_info;
|
||
#endif
|
||
//prize add by wangyongsheng 20210329 end
|
||
|
||
//spdev use for recording the data for other use
|
||
static unsigned int irq, ret;
|
||
static unsigned int ma_drv_reg;
|
||
static unsigned int ma_speed;
|
||
static unsigned int is_screen_on;
|
||
static struct notifier_block notifier;
|
||
static unsigned int int_pin_state;
|
||
static unsigned int compatible;
|
||
static unsigned int screen_flag;
|
||
static DECLARE_WAIT_QUEUE_HEAD(screenwaitq);
|
||
static DECLARE_WAIT_QUEUE_HEAD(gWaitq);
|
||
static DECLARE_WAIT_QUEUE_HEAD(U1_Waitq);
|
||
static DECLARE_WAIT_QUEUE_HEAD(U2_Waitq);
|
||
// #ifdef CONFIG_PM_WAKELOCKS
|
||
struct wakeup_source *gProcessWakeLock;
|
||
// #else
|
||
// struct wake_lock gProcessWakeLock;
|
||
// #endif
|
||
|
||
struct work_struct gWork;
|
||
struct workqueue_struct *gWorkq;
|
||
//
|
||
static LIST_HEAD(dev_list);
|
||
static DEFINE_MUTEX(dev_lock);
|
||
static DEFINE_MUTEX(drv_lock);
|
||
static DEFINE_MUTEX(ioctl_lock);
|
||
#ifdef COMPATIBLE_VERSION3
|
||
static DECLARE_WAIT_QUEUE_HEAD(drv_waitq);
|
||
#endif
|
||
|
||
static struct fprint_dev *sdev = NULL;
|
||
static struct fprint_spi *smas = NULL;
|
||
|
||
static u8* stxb;
|
||
static u8* srxb;
|
||
|
||
#define IMAGE_SIZE 13312
|
||
#define IMAGE_DMA_SIZE 32*1024
|
||
|
||
static void mas_work(struct work_struct *pws) {
|
||
smas->f_irq = 1;
|
||
wake_up(&gWaitq);
|
||
#ifdef COMPATIBLE_VERSION3
|
||
wake_up(&drv_waitq);
|
||
#endif
|
||
}
|
||
|
||
static irqreturn_t mas_interrupt(int irq, void *dev_id) {
|
||
#ifdef DOUBLE_EDGE_IRQ
|
||
if(mas_get_interrupt_gpio(0)==1){
|
||
//TODO IRQF_TRIGGER_RISING
|
||
}else{
|
||
//TODO IRQF_TRIGGER_FALLING
|
||
}
|
||
#else
|
||
|
||
queue_work(gWorkq, &gWork);
|
||
#endif
|
||
return IRQ_HANDLED;
|
||
}
|
||
|
||
|
||
/*---------------------------------- fops ------------------------------------*/
|
||
|
||
/* 读写数据
|
||
* @buf 数据
|
||
* @len 长度
|
||
* @返回值:0成功,否则失败
|
||
*/
|
||
int mas_sync(u8 *txb, u8 *rxb, int len) {
|
||
int ret = 0;
|
||
|
||
|
||
mutex_lock(&dev_lock);
|
||
//mas_select_transfer(smas->spi, len);
|
||
smas->xfer.tx_nbits=SPI_NBITS_SINGLE;
|
||
smas->xfer.tx_buf = txb;
|
||
smas->xfer.rx_nbits=SPI_NBITS_SINGLE;
|
||
smas->xfer.rx_buf = rxb;
|
||
// smas->xfer.delay_usecs = 1;
|
||
smas->xfer.len = len;
|
||
smas->xfer.bits_per_word = 8;
|
||
smas->xfer.speed_hz = smas->spi->max_speed_hz;
|
||
spi_message_init(&smas->msg);
|
||
spi_message_add_tail(&smas->xfer, &smas->msg);
|
||
ret = spi_sync(smas->spi, &smas->msg);
|
||
mutex_unlock(&dev_lock);
|
||
|
||
return ret;
|
||
|
||
|
||
}
|
||
|
||
|
||
|
||
/* 读数据
|
||
* @return 成功:count, -1count太大,-2通讯失败, -3拷贝失败
|
||
*/
|
||
static ssize_t mas_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) {
|
||
int val, ret = 0;
|
||
//MALOGD("start");
|
||
//printk("zzzz count - %d\n", (int)count);
|
||
//printk("cdw---->mas_read\n");
|
||
|
||
ret = mas_sync(stxb, srxb, count);
|
||
if(ret) {
|
||
MALOGW("mas_sync failed.");
|
||
return -2;
|
||
}
|
||
ret = copy_to_user(buf, srxb, count);
|
||
if(!ret) val = count;
|
||
else {
|
||
val = -3;
|
||
MALOGW("copy_to_user failed.");
|
||
}
|
||
//MALOGD("end.");
|
||
return val;
|
||
}
|
||
|
||
|
||
static void mas_set_input(void) {
|
||
struct input_dev *input = NULL;
|
||
input = input_allocate_device();
|
||
if (!input) {
|
||
MALOGW("input_allocate_device failed.");
|
||
return ;
|
||
}
|
||
set_bit(EV_KEY, input->evbit);
|
||
//set_bit(EV_ABS, input->evbit);
|
||
set_bit(EV_SYN, input->evbit);
|
||
set_bit(FINGERPRINT_SWIPE_UP, input->keybit); //单触
|
||
set_bit(FINGERPRINT_SWIPE_DOWN, input->keybit);
|
||
set_bit(FINGERPRINT_SWIPE_LEFT, input->keybit);
|
||
set_bit(FINGERPRINT_SWIPE_RIGHT, input->keybit);
|
||
set_bit(KEY_F11, input->keybit);
|
||
// set_bit(FINGERPRINT_DTAP, input->keybit);
|
||
// set_bit(FINGERPRINT_LONGPRESS, input->keybit);
|
||
|
||
set_bit(KEY_POWER, input->keybit);
|
||
|
||
input->name = MA_CHR_DEV_NAME;
|
||
input->id.bustype = BUS_SPI;
|
||
ret = input_register_device(input);
|
||
if (ret) {
|
||
input_free_device(input);
|
||
MALOGW("failed to register input device.");
|
||
return;
|
||
}
|
||
smas->input = input;
|
||
}
|
||
|
||
|
||
|
||
//static int mas_ioctl (struct inode *node, struct file *filp, unsigned int cmd, uns igned long arg)
|
||
//this function only supported while the linux kernel version under v2.6.36,while the kernel version under v2.6.36, use this line
|
||
static long mas_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) {
|
||
//MALOGF("start");
|
||
int tmp;
|
||
//printk("cdw-->mas_ioctl-->cmd=[%d]\n",cmd);
|
||
switch(cmd){
|
||
case TIMEOUT_WAKELOCK: //延时锁 timeout lock
|
||
// #ifdef CONFIG_PM_WAKELOCKS
|
||
__pm_wakeup_event(gProcessWakeLock, msecs_to_jiffies(5000));
|
||
// #else
|
||
// wake_lock_timeout(&gProcessWakeLock, 5 * HZ);
|
||
// #endif
|
||
break;
|
||
case SLEEP: //remove the process out of the runqueue
|
||
if(mas_get_interrupt_gpio(0) == 1) {
|
||
MALOGF("int pull down failed!");
|
||
return 0;
|
||
}
|
||
smas->f_irq = 0;
|
||
ret = wait_event_freezable(gWaitq, smas->f_irq != 0);
|
||
break;
|
||
case WAKEUP: //wake up, schedule the process into the runqueue
|
||
smas->f_irq = 1;
|
||
wake_up(&gWaitq);
|
||
break;
|
||
case ENABLE_CLK:
|
||
mas_enable_spi_clock(smas->spi); //if the spi clock is not opening always, do this methods
|
||
break;
|
||
case DISABLE_CLK:
|
||
mas_disable_spi_clock(smas->spi); //disable the spi clock
|
||
break;
|
||
case ENABLE_INTERRUPT:
|
||
enable_irq(irq); //enable the irq,in fact, you can make irq enable always
|
||
break;
|
||
case DISABLE_INTERRUPT:
|
||
disable_irq(irq); //disable the irq
|
||
break;
|
||
case TAP_DOWN:
|
||
input_report_key(smas->input, KEY_F11, 1);
|
||
input_sync(smas->input); //tap down
|
||
break;
|
||
case TAP_UP:
|
||
input_report_key(smas->input, KEY_F11, 0);
|
||
input_sync(smas->input); //tap up
|
||
break;
|
||
case SINGLE_TAP:
|
||
// input_report_key(smas->input, FINGERPRINT_TAP, 1);
|
||
// input_sync(smas->input);
|
||
// input_report_key(smas->input, FINGERPRINT_TAP, 0);
|
||
// input_sync(smas->input); //single tap
|
||
break;
|
||
case DOUBLE_TAP:
|
||
// input_report_key(smas->input, FINGERPRINT_DTAP, 1);
|
||
// input_sync(smas->input);
|
||
// input_report_key(smas->input, FINGERPRINT_DTAP, 0);
|
||
// input_sync(smas->input); //double tap
|
||
break;
|
||
case LONG_TAP:
|
||
// input_report_key(smas->input, FINGERPRINT_LONGPRESS, 1);
|
||
// input_sync(smas->input);
|
||
// input_report_key(smas->input, FINGERPRINT_LONGPRESS, 0);
|
||
// input_sync(smas->input); //long tap
|
||
break;
|
||
case MA_KEY_UP:
|
||
input_report_key(smas->input, FINGERPRINT_SWIPE_UP, 1);
|
||
input_sync(smas->input);
|
||
input_report_key(smas->input, FINGERPRINT_SWIPE_UP, 0);
|
||
input_sync(smas->input);
|
||
break;
|
||
case MA_KEY_LEFT:
|
||
input_report_key(smas->input, FINGERPRINT_SWIPE_LEFT, 1);
|
||
input_sync(smas->input);
|
||
input_report_key(smas->input, FINGERPRINT_SWIPE_LEFT, 0);
|
||
input_sync(smas->input);
|
||
break;
|
||
case MA_KEY_DOWN:
|
||
input_report_key(smas->input, FINGERPRINT_SWIPE_DOWN, 1);
|
||
input_sync(smas->input);
|
||
input_report_key(smas->input, FINGERPRINT_SWIPE_DOWN, 0);
|
||
input_sync(smas->input);
|
||
break;
|
||
case MA_KEY_RIGHT:
|
||
input_report_key(smas->input, FINGERPRINT_SWIPE_RIGHT, 1);
|
||
input_sync(smas->input);
|
||
input_report_key(smas->input, FINGERPRINT_SWIPE_RIGHT, 0);
|
||
input_sync(smas->input);
|
||
break;
|
||
case SET_MODE:
|
||
mutex_lock(&ioctl_lock);
|
||
ret = copy_from_user(&ma_drv_reg, (unsigned int*)arg, sizeof(unsigned int));
|
||
mutex_unlock(&ioctl_lock);
|
||
break;
|
||
case GET_MODE:
|
||
mutex_lock(&ioctl_lock);
|
||
ret = copy_to_user((unsigned int*)arg, &ma_drv_reg, sizeof(unsigned int));
|
||
mutex_unlock(&ioctl_lock);
|
||
break;
|
||
case MA_IOC_GVER:
|
||
{
|
||
unsigned int ma_drv_version = MA_DRV_VERSION;
|
||
mutex_lock(&ioctl_lock);
|
||
if (copy_to_user((void *)arg, &ma_drv_version, sizeof(unsigned int))) {
|
||
MALOGE("copy_to_user(..) failed.\n");
|
||
ret = (-EFAULT);
|
||
}
|
||
mutex_unlock(&ioctl_lock);
|
||
}
|
||
break;
|
||
case SCREEN_ON:
|
||
mas_switch_power(1);
|
||
break;
|
||
case SCREEN_OFF:
|
||
mas_switch_power(0);
|
||
break;
|
||
case MA_RESET:
|
||
printk("microarray pri liaojie , set reset \n");
|
||
mas_finger_set_reset(arg);
|
||
break;
|
||
case GET_MA_RESET_STATE:
|
||
break;
|
||
|
||
case SET_SPI_SPEED:
|
||
ret = copy_from_user(&ma_speed, (unsigned int*)arg, sizeof(unsigned int));
|
||
//ma_spi_change(smas->spi, ma_speed, 0);
|
||
break;
|
||
case WAIT_FACTORY_CMD:
|
||
smas->u2_flag = 0;
|
||
ret = wait_event_freezable(U2_Waitq, smas->u2_flag != 0);
|
||
break;
|
||
case WAKEUP_FINGERPRINTD:
|
||
smas->u2_flag = 1;
|
||
wake_up(&U2_Waitq);
|
||
break;
|
||
case WAIT_FINGERPRINTD_RESPONSE:
|
||
smas->u1_flag = 0;
|
||
ret = wait_event_freezable(U1_Waitq, smas->u1_flag != 0);
|
||
mutex_lock(&ioctl_lock);
|
||
tmp = copy_to_user((unsigned int*)arg, &ma_drv_reg, sizeof(unsigned int));
|
||
mutex_unlock(&ioctl_lock);
|
||
break;
|
||
case WAKEUP_FACTORY_TEST_SEND_FINGERPRINTD_RESPONSE:
|
||
mutex_lock(&ioctl_lock);
|
||
ret = copy_from_user(&ma_drv_reg, (unsigned int*)arg, sizeof(unsigned int));
|
||
mutex_unlock(&ioctl_lock);
|
||
msleep(4);
|
||
smas->u1_flag = 1;
|
||
wake_up(&U1_Waitq);
|
||
break;
|
||
case WAIT_SCREEN_STATUS_CHANGE:
|
||
screen_flag = 0;
|
||
ret = wait_event_freezable(screenwaitq, screen_flag != 0);
|
||
mutex_lock(&ioctl_lock);
|
||
tmp = copy_to_user((unsigned int*)arg, &is_screen_on, sizeof(unsigned int));
|
||
mutex_unlock(&ioctl_lock);
|
||
break;
|
||
case GET_INTERRUPT_STATUS:
|
||
int_pin_state = mas_get_interrupt_gpio(0);
|
||
mutex_lock(&ioctl_lock);
|
||
tmp = copy_to_user((unsigned int*)arg, &int_pin_state, sizeof(unsigned int));
|
||
mutex_unlock(&ioctl_lock);
|
||
break;
|
||
case GET_SCREEN_STATUS:
|
||
mutex_lock(&ioctl_lock);
|
||
ret = copy_to_user((unsigned int*)arg, &is_screen_on, sizeof(unsigned int));
|
||
mutex_unlock(&ioctl_lock);
|
||
break;
|
||
default:
|
||
ret = -EINVAL;
|
||
MALOGW("mas_ioctl no such cmd");
|
||
}
|
||
//MALOGF("end");
|
||
return ret;
|
||
}
|
||
|
||
#ifdef CONFIG_COMPAT
|
||
static long mas_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||
{
|
||
//printk("cdw-->mas_compat_ioctl-->cmd=[%d]\n",cmd);
|
||
int retval = 0;
|
||
//printk("cdw-->mas_compat_ioctl-->cmd=[%d]\n",cmd);
|
||
retval = filp->f_op->unlocked_ioctl(filp, cmd, arg);
|
||
return retval;
|
||
}
|
||
#endif
|
||
|
||
#ifdef COMPATIBLE_VERSION3
|
||
int version3_ioctl(int cmd, int arg){
|
||
int ret = 0;
|
||
|
||
//printk("cdw-->version3_ioctl-->cmd=[%d]\n",cmd);
|
||
printd("%s: start cmd=0x%.3x arg=%d\n", __func__, cmd, arg);
|
||
|
||
switch (cmd) {
|
||
case IOCTL_DEBUG:
|
||
sdeb = (u8) arg;
|
||
break;
|
||
case IOCTL_IRQ_ENABLE:
|
||
break;
|
||
case IOCTL_SPI_SPEED:
|
||
smas->spi->max_speed_hz = (u32) arg;
|
||
spi_setup(smas->spi);
|
||
break;
|
||
case IOCTL_COVER_NUM:
|
||
ret = COVER_NUM;
|
||
break;
|
||
case IOCTL_GET_VDATE:
|
||
ret = 20160425;
|
||
break;
|
||
case IOCTL_CLR_INTF:
|
||
smas->f_irq = FALSE;
|
||
break;
|
||
case IOCTL_GET_INTF:
|
||
ret = smas->f_irq;
|
||
break;
|
||
case IOCTL_REPORT_FLAG:
|
||
smas->f_repo = arg;
|
||
break;
|
||
case IOCTL_REPORT_KEY:
|
||
input_report_key(smas->input, arg, 1);
|
||
input_sync(smas->input);
|
||
input_report_key(smas->input, arg, 0);
|
||
input_sync(smas->input);
|
||
break;
|
||
case IOCTL_SET_WORK:
|
||
smas->do_what = arg;
|
||
break;
|
||
case IOCTL_GET_WORK:
|
||
ret = smas->do_what;
|
||
break;
|
||
case IOCTL_SET_VALUE:
|
||
smas->value = arg;
|
||
break;
|
||
case IOCTL_GET_VALUE:
|
||
ret = smas->value;
|
||
break;
|
||
case IOCTL_TRIGGER:
|
||
smas->f_wake = TRUE;
|
||
wake_up_interruptible(&drv_waitq);
|
||
break;
|
||
case IOCTL_WAKE_LOCK:
|
||
#ifdef CONFIG_PM_WAKELOCKS
|
||
__pm_stay_awake(&smas->wl);
|
||
#else
|
||
wake_lock(&smas->wl);
|
||
#endif
|
||
break;
|
||
case IOCTL_WAKE_UNLOCK:
|
||
#ifdef CONFIG_PM_WAKELOCKS
|
||
__pm_relax(&smas->wl);
|
||
#else
|
||
wake_unlock(&smas->wl);
|
||
#endif
|
||
break;
|
||
case IOCTL_KEY_DOWN:
|
||
input_report_key(smas->input, KEY_F11 1);
|
||
input_sync(smas->input);
|
||
break;
|
||
case IOCTL_KEY_UP:
|
||
input_report_key(smas->input, KEY_F11, 0);
|
||
input_sync(smas->input);
|
||
break;
|
||
}
|
||
|
||
printd("%s: end. ret=%d f_irq=%d, f_repo=%d\n", __func__, ret, smas->f_irq, smas->f_repo);
|
||
|
||
return ret;
|
||
|
||
}
|
||
#endif
|
||
|
||
/* 写数据
|
||
* @return 成功:count, -1count太大,-2拷贝失败
|
||
*/
|
||
static ssize_t mas_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) {
|
||
int val = 0;
|
||
//MALOGD("start");
|
||
//printk("cdw--->mas_write\n");
|
||
if(count==6) { //cmd ioctl, old version used the write interface to do ioctl, this is only for the old version
|
||
int cmd, arg;
|
||
u8 tmp[6];
|
||
ret = copy_from_user(tmp, buf, count);
|
||
cmd = tmp[0];
|
||
cmd <<= 8;
|
||
cmd += tmp[1];
|
||
arg = tmp[2];
|
||
arg <<= 8;
|
||
arg += tmp[3];
|
||
arg <<= 8;
|
||
arg += tmp[4];
|
||
arg <<= 8;
|
||
arg += tmp[5];
|
||
#ifdef COMPATIBLE_VERSION3
|
||
val = (int)version3_ioctl(NULL, (unsigned int)cmd, (unsigned long)arg);
|
||
#endif
|
||
} else {
|
||
//memset(stxb, 0, FBUF);
|
||
memset(stxb, 0xff, IMAGE_DMA_SIZE);
|
||
ret = copy_from_user(stxb, buf, count);
|
||
if(ret) {
|
||
MALOGW("copy form user failed");
|
||
val = -2;
|
||
} else {
|
||
val = count;
|
||
}
|
||
}
|
||
return val;
|
||
}
|
||
void * kernel_memaddr = NULL;
|
||
unsigned long kernel_memesize = 0;
|
||
|
||
int mas_mmap(struct file *filp, struct vm_area_struct *vma){
|
||
unsigned long page;
|
||
if ( !kernel_memaddr ) {
|
||
kernel_memaddr = kmalloc(128*1024, GFP_KERNEL);
|
||
if( !kernel_memaddr ) {
|
||
return -1;
|
||
}
|
||
}
|
||
page = virt_to_phys((void *)kernel_memaddr) >> PAGE_SHIFT;
|
||
vma->vm_page_prot=pgprot_noncached(vma->vm_page_prot);
|
||
if( remap_pfn_range(vma, vma->vm_start, page, (vma->vm_end - vma->vm_start),
|
||
vma->vm_page_prot) )
|
||
return -1;
|
||
//vma->vm_flags |= VM_RESERVED;
|
||
vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP;
|
||
printk("remap_pfn_rang page:[%lu] ok.\n", page);
|
||
return 0;
|
||
}
|
||
|
||
#ifdef COMPATIBLE_VERSION3
|
||
static unsigned int mas_poll(struct file *filp, struct poll_table_struct *wait) {
|
||
unsigned int mask = 0;
|
||
|
||
printd("%s: start. f_irq=%d f_repo=%d f_wake=%d\n",
|
||
__func__, smas->f_irq, smas->f_repo, smas->f_wake);
|
||
|
||
poll_wait(filp, &drv_waitq, wait);
|
||
if(smas->f_irq && smas->f_repo) {
|
||
smas->f_repo = FALSE;
|
||
mask |= POLLIN | POLLRDNORM;
|
||
} else if( smas->f_wake ) {
|
||
smas->f_wake = FALSE;
|
||
mask |= POLLPRI;
|
||
}
|
||
|
||
printd("%s: end. mask=%d\n", __func__, mask);
|
||
|
||
return mask;
|
||
}
|
||
#endif
|
||
|
||
/*---------------------------------- fops ------------------------------------*/
|
||
static const struct file_operations sfops = {
|
||
.owner = THIS_MODULE,
|
||
.write = mas_write,
|
||
.read = mas_read,
|
||
.unlocked_ioctl = mas_ioctl,
|
||
.mmap = mas_mmap,
|
||
//.ioctl = mas_ioctl,
|
||
//using the previous line replacing the unlock_ioctl while the linux kernel under version2.6.36
|
||
#ifdef CONFIG_COMPAT
|
||
.compat_ioctl = mas_compat_ioctl,
|
||
#endif
|
||
#ifdef COMPATIBLE_VERSION3
|
||
.poll = mas_poll,
|
||
#endif
|
||
};
|
||
/*---------------------------------- fops end ---------------------------------*/
|
||
|
||
static int init_file_node(void)
|
||
{
|
||
int ret;
|
||
//MALOGF("start");
|
||
ret = alloc_chrdev_region(&sdev->idd, 0, 1, MA_CHR_DEV_NAME);
|
||
if(ret < 0)
|
||
{
|
||
MALOGW("alloc_chrdev_region error!");
|
||
return -1;
|
||
}
|
||
sdev->chd = cdev_alloc();
|
||
if (!sdev->chd)
|
||
{
|
||
MALOGW("cdev_alloc error!");
|
||
return -1;
|
||
}
|
||
sdev->chd->owner = THIS_MODULE;
|
||
sdev->chd->ops = &sfops;
|
||
cdev_add(sdev->chd, sdev->idd, 1);
|
||
sdev->cls = class_create(THIS_MODULE, MA_CHR_DEV_NAME);
|
||
if (IS_ERR(sdev->cls)) {
|
||
MALOGE("class_create");
|
||
return -1;
|
||
}
|
||
sdev->dev = device_create(sdev->cls, NULL, sdev->idd, NULL, MA_CHR_FILE_NAME);
|
||
ret = IS_ERR(sdev->dev) ? PTR_ERR(sdev->dev) : 0;
|
||
if(ret){
|
||
MALOGE("device_create");
|
||
}
|
||
//MALOGF("end");
|
||
return 0;
|
||
}
|
||
|
||
static int deinit_file_node(void)
|
||
{
|
||
cdev_del(sdev->chd);
|
||
device_destroy(sdev->cls, sdev->idd);
|
||
unregister_chrdev_region(sdev->idd, 1);
|
||
return 0;
|
||
}
|
||
|
||
static int init_interrupt(void)
|
||
{
|
||
const char*tname = MA_EINT_NAME;
|
||
irq = mas_get_irq(&smas->spi->dev);
|
||
if(irq<=0){
|
||
ret = irq;
|
||
MALOGE("mas_get_irq");
|
||
}
|
||
#ifdef DOUBLE_EDGE_IRQ
|
||
ret = request_irq(irq, mas_interrupt, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, tname, NULL);
|
||
#else
|
||
ret = request_irq(irq, mas_interrupt, IRQF_TRIGGER_RISING | IRQF_ONESHOT, tname, NULL);
|
||
#endif
|
||
if(ret<0){
|
||
MALOGE("request_irq");
|
||
}
|
||
enable_irq_wake(irq);
|
||
return ret;
|
||
}
|
||
static int deinit_interrupt(void)
|
||
{
|
||
disable_irq(irq);
|
||
free_irq(irq, NULL);
|
||
return 0;
|
||
}
|
||
|
||
|
||
static int init_vars(void)
|
||
{
|
||
sdev = kmalloc(sizeof(struct fprint_dev), GFP_KERNEL);
|
||
smas = kmalloc(sizeof(struct fprint_spi), GFP_KERNEL);
|
||
stxb = (u8*)__get_free_pages(GFP_KERNEL, get_order(IMAGE_DMA_SIZE));
|
||
srxb = (u8*)__get_free_pages(GFP_KERNEL, get_order(IMAGE_DMA_SIZE));
|
||
|
||
if (sdev==NULL || smas==NULL || stxb==NULL || srxb==NULL) {
|
||
MALOGW("smas kmalloc failed.");
|
||
if(sdev!=NULL) kfree(sdev);
|
||
if(smas!=NULL) kfree(smas);
|
||
if(stxb!=NULL) free_pages((unsigned long)stxb, get_order(IMAGE_DMA_SIZE));
|
||
if(srxb!=NULL) free_pages((unsigned long)srxb, get_order(IMAGE_DMA_SIZE));
|
||
return -ENOMEM;
|
||
}
|
||
|
||
memset(stxb,0x00,get_order(IMAGE_DMA_SIZE));
|
||
memset(srxb,0x00,get_order(IMAGE_DMA_SIZE));
|
||
|
||
// #ifdef CONFIG_PM_WAKELOCKS
|
||
// sf_ctl_dev.wakelock = wakeup_source_register(&dev->dev,"sf_wakelock");
|
||
|
||
// wakeup_source_init(&gProcessWakeLock, "microarray_process_wakelock");
|
||
// #else
|
||
// wake_lock_init(&gProcessWakeLock, WAKE_LOCK_SUSPEND, "microarray_process_wakelock");
|
||
// #endif
|
||
|
||
INIT_WORK(&gWork, mas_work);
|
||
gWorkq = create_singlethread_workqueue("mas_workqueue");
|
||
if (!gWorkq) {
|
||
MALOGW("create_single_workqueue error!");
|
||
return -ENOMEM;
|
||
}
|
||
return 0;
|
||
}
|
||
static int deinit_vars(void)
|
||
{
|
||
destroy_workqueue(gWorkq);
|
||
wakeup_source_unregister(gProcessWakeLock);
|
||
return 0;
|
||
}
|
||
|
||
static int init_spi(struct spi_device *spi){
|
||
smas->spi = spi;
|
||
smas->spi->max_speed_hz = SPI_SPEED;
|
||
smas->spi->bits_per_word = 8;
|
||
smas->spi->mode = SPI_MODE_0;
|
||
//mas_set_spi_controller_data(smas->spi);
|
||
INIT_LIST_HEAD(&smas->dev_entry);
|
||
return 0;
|
||
}
|
||
|
||
static int deinit_spi(struct spi_device *spi){
|
||
smas->spi = NULL;
|
||
return 0;
|
||
}
|
||
int sp_mode_read_chipid(void)
|
||
{
|
||
uint8_t write_buffer[5] = {0, 0, 0, 0,0};
|
||
uint8_t read_buffer[5] = {0, 0, 0, 0,0};
|
||
uint8_t chipid = 0;
|
||
|
||
write_buffer[0] = 0x70; //read drv
|
||
write_buffer[1] = 0;
|
||
write_buffer[2] = 0;
|
||
write_buffer[3] = 0;
|
||
write_buffer[4] = 0;
|
||
#ifdef TEE_ID_COMPATIBLE_TRUSTKERNEL
|
||
mas_tee_spi_transfer(write_buffer, read_buffer, 5);
|
||
#else
|
||
mas_sync(write_buffer, read_buffer, 5);
|
||
#endif
|
||
msleep(3);
|
||
|
||
write_buffer[0] = 0x90; //read drv
|
||
write_buffer[1] = 0x08;
|
||
write_buffer[2] = 0;
|
||
write_buffer[3] = 0;
|
||
write_buffer[4] = 0;
|
||
|
||
#ifdef TEE_ID_COMPATIBLE_TRUSTKERNEL
|
||
mas_tee_spi_transfer(write_buffer, read_buffer, 5);
|
||
#else
|
||
mas_sync(write_buffer, read_buffer, 5);
|
||
#endif
|
||
chipid = read_buffer[3];
|
||
printk("sp mode read chip id %d", chipid);
|
||
|
||
return chipid;
|
||
}
|
||
|
||
int sp_mode_read_chipid_e026(void)
|
||
{
|
||
uint8_t write_buffer[5] = {0, 0, 0, 0,0};
|
||
uint8_t read_buffer[5] = {0, 0, 0, 0,0};
|
||
uint8_t chipid = 0;
|
||
|
||
write_buffer[0] = 0x90; //read drv
|
||
write_buffer[1] = 0x0b;
|
||
write_buffer[2] = 0;
|
||
write_buffer[3] = 0;
|
||
write_buffer[4] = 0;
|
||
|
||
#ifdef TEE_ID_COMPATIBLE_TRUSTKERNEL
|
||
mas_tee_spi_transfer(write_buffer, read_buffer, 5);
|
||
#else
|
||
mas_sync(write_buffer, read_buffer, 5);
|
||
#endif
|
||
chipid = read_buffer[3];
|
||
printk("mas_probe sp mode e026 read chip id %d", chipid);
|
||
|
||
return chipid;
|
||
}
|
||
/*
|
||
* init_connect function to check whether the chip is microarray's
|
||
* @return 0 not 1 yes
|
||
* param void
|
||
*/
|
||
int init_connect(void){
|
||
int i;
|
||
int chipid = 0;
|
||
MALOGD("start");
|
||
for(i=0; i<4; i++){
|
||
stxb[0] = 0x8c;
|
||
stxb[1] = 0xff;
|
||
stxb[2] = 0xff;
|
||
stxb[3] = 0xff;
|
||
#ifdef TEE_ID_COMPATIBLE_TRUSTKERNEL
|
||
mas_tee_spi_transfer(stxb, srxb, 4);
|
||
#else
|
||
mas_sync(stxb, srxb, 4);
|
||
#endif
|
||
msleep(8);
|
||
stxb[0] = 0x00;
|
||
stxb[1] = 0xff;
|
||
stxb[2] = 0xff;
|
||
stxb[3] = 0xff;
|
||
#ifdef TEE_ID_COMPATIBLE_TRUSTKERNEL
|
||
ret = mas_tee_spi_transfer(stxb, srxb, 4);
|
||
#else
|
||
ret = mas_sync(stxb, srxb, 4);
|
||
#endif
|
||
if(ret!=0) MALOGW("do init_connect failed!");
|
||
printk("mas_probe guq srxb[3] = 0x%02x srxb[2] = 0x%02x\n", srxb[3], srxb[2]);
|
||
if(srxb[3] == 0x41 || srxb[3] == 0x45) return 1;
|
||
chipid = sp_mode_read_chipid();
|
||
if(chipid == 32 || chipid == 26) return 1;
|
||
chipid = sp_mode_read_chipid_e026();
|
||
if(chipid == 74){
|
||
#if IS_ENABLED(CONFIG_PRIZE_HARDWARE_INFO)
|
||
sprintf(current_fingerprint_info.chip,"E026L");
|
||
sprintf(current_fingerprint_info.id,"74");
|
||
#endif
|
||
return 1;
|
||
}
|
||
|
||
}
|
||
MALOGD("end");
|
||
return 0;
|
||
}
|
||
|
||
|
||
int deinit_connect(void){
|
||
mas_free_dts_info();
|
||
return 0;
|
||
}
|
||
|
||
static int mas_fb_notifier_callback(struct notifier_block *self, unsigned long event, void *data){
|
||
struct fb_event *evdata = data;
|
||
unsigned int blank;
|
||
if(event != FB_EVENT_BLANK) {
|
||
return 0;
|
||
}
|
||
blank = *(int *)evdata->data;
|
||
switch(blank){
|
||
case FB_BLANK_UNBLANK:
|
||
is_screen_on = 1;
|
||
break;
|
||
case FB_BLANK_POWERDOWN:
|
||
is_screen_on = 0;
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
screen_flag = 1;
|
||
wake_up(&screenwaitq);
|
||
return 0;
|
||
}
|
||
|
||
static int init_notifier_call(void);
|
||
static int deinit_notifier_call(void);
|
||
|
||
static int init_notifier_call(){
|
||
notifier.notifier_call = mas_fb_notifier_callback;
|
||
fb_register_client(¬ifier);
|
||
is_screen_on = 1;
|
||
return 0;
|
||
}
|
||
|
||
static int deinit_notifier_call(){
|
||
fb_unregister_client(¬ifier);
|
||
return 0;
|
||
}
|
||
|
||
#if defined(CONFIG_PRIZE_FP_USE_VFP)
|
||
int vfp_regulator_ctl(int enable)
|
||
{
|
||
int ret =0;
|
||
if(enable){
|
||
if (!IS_ERR_OR_NULL(vdd_ldo)){
|
||
ret = regulator_enable(vdd_ldo);
|
||
if (ret) {
|
||
MALOGE("Regulator vdd enable failed ret = %d\n", ret);
|
||
return ret;
|
||
}
|
||
};
|
||
}else{
|
||
ret = regulator_disable(vdd_ldo);
|
||
if (ret) {
|
||
MALOGE("Regulator vdd disable failed ret = %d\n", ret);
|
||
return ret;
|
||
}
|
||
|
||
}
|
||
return ret;
|
||
}
|
||
#endif
|
||
|
||
int mas_plat_probe(struct platform_device *pdev) {
|
||
MALOGD("start");
|
||
|
||
ret = mas_finger_get_gpio_info(pdev);
|
||
if(ret){
|
||
MALOGE("mas_plat_probe do mas_finger_get_gpio_info error\n");
|
||
goto free_masfinger;
|
||
}
|
||
|
||
#if defined(CONFIG_PRIZE_FP_USE_VFP)
|
||
vdd_ldo = regulator_get(&pdev->dev, "VFP");
|
||
// vdd_ldo = regulator_get(NULL, "VFP");
|
||
if (IS_ERR(vdd_ldo)) {
|
||
ret = PTR_ERR(vdd_ldo);
|
||
MALOGE("%s: Regulator get failed vdd ret = %d\n",__func__,ret);
|
||
//return ret;
|
||
goto free_masfinger;
|
||
}
|
||
ret = regulator_set_voltage(vdd_ldo, 2800000, 2800000);
|
||
if (ret) {
|
||
MALOGE("%s: Regulator set vdd val fail ret = %d\n",__func__,ret);
|
||
//return ret;
|
||
goto free_regulator;
|
||
}
|
||
#endif
|
||
|
||
gProcessWakeLock = wakeup_source_register(&pdev->dev,"microarray_wakelock");
|
||
|
||
ret = mas_finger_set_gpio_info(1);
|
||
if(ret){
|
||
MALOGE("mas_plat_probe do mas_finger_set_gpio_info");
|
||
}
|
||
//MALOGD("cdw----------->platform driver end");
|
||
return ret;
|
||
#if defined(CONFIG_PRIZE_FP_USE_VFP)
|
||
free_regulator:
|
||
regulator_put(vdd_ldo);
|
||
#endif
|
||
|
||
free_masfinger:
|
||
return -1;
|
||
|
||
}
|
||
|
||
int mas_plat_remove(struct platform_device *pdev) {
|
||
mas_finger_set_gpio_info(0);
|
||
return 0;
|
||
}
|
||
|
||
bool pri_mas_finger = false;
|
||
EXPORT_SYMBOL(pri_mas_finger);
|
||
|
||
int mas_probe(struct spi_device *spi) {
|
||
|
||
int ret = 0;
|
||
|
||
|
||
MALOGD("start");
|
||
|
||
mas_do_some_for_probe(spi);
|
||
ret = init_vars();
|
||
if(ret){
|
||
goto err1;
|
||
}
|
||
|
||
MALOGD("start3333");
|
||
ret = init_spi(spi);
|
||
if(ret){
|
||
goto err2;
|
||
}
|
||
#ifdef READ_CHIP_ID
|
||
mas_enable_spi_clock(smas->spi);
|
||
ret = init_connect();
|
||
mas_disable_spi_clock(smas->spi);
|
||
|
||
|
||
if(ret == 0){//not chip
|
||
goto err3;
|
||
} else {
|
||
#ifdef TEE_ID_COMPATIBLE_MICROTRUST
|
||
struct TEEC_UUID vendor_uuid = {0xedcf9395, 0x3518, 0x9067, { 0x61, 0x4c, 0xaf, 0xae, 0x29, 0x09, 0x77, 0x5b }};
|
||
memcpy(&uuid_fp, &vendor_uuid, sizeof(struct TEEC_UUID));
|
||
#endif
|
||
}
|
||
#else
|
||
ret = 0;
|
||
#endif
|
||
|
||
MALOGD("start11111");
|
||
|
||
|
||
ret = init_interrupt();
|
||
if(ret < 0){
|
||
goto err4;
|
||
}
|
||
|
||
MALOGD("start22222");
|
||
ret = init_file_node();
|
||
if(ret){
|
||
goto err5;
|
||
}
|
||
|
||
mas_set_input();
|
||
MALOGF("end");
|
||
ret = init_notifier_call();
|
||
if(ret != 0){
|
||
ret = -ENODEV;
|
||
goto err6;
|
||
}
|
||
//prize add by wangyongsheng 20210329 start
|
||
#if IS_ENABLED(CONFIG_PRIZE_HARDWARE_INFO)
|
||
|
||
strcpy(current_fingerprint_info.vendor,"mircoarray");
|
||
strcpy(current_fingerprint_info.more,"fingerprint");
|
||
#endif
|
||
//prize add by wangyongsheng 20210329 end
|
||
pri_mas_finger = true;
|
||
|
||
MALOGD("cdw----------->end1111");
|
||
return ret;
|
||
|
||
err6:
|
||
deinit_notifier_call();
|
||
err5:
|
||
deinit_file_node();
|
||
err4:
|
||
deinit_interrupt();
|
||
#ifdef READ_CHIP_ID
|
||
err3:
|
||
deinit_connect();
|
||
// mas_remove_platform();
|
||
#endif
|
||
err2:
|
||
deinit_spi(spi);
|
||
err1:
|
||
deinit_vars();
|
||
|
||
// mas_remove_platform();
|
||
return ret;
|
||
}
|
||
|
||
int mas_remove(struct spi_device *spi) {
|
||
deinit_file_node();
|
||
deinit_interrupt();
|
||
deinit_vars();
|
||
return 0;
|
||
}
|
||
|
||
|
||
static int __init mas_init(void)
|
||
{
|
||
int ret = 0;
|
||
MALOGF("start");
|
||
compatible = 1;
|
||
ret = mas_get_platform();
|
||
if(ret){
|
||
MALOGE("mas_get_platform");
|
||
}
|
||
|
||
return ret;
|
||
}
|
||
|
||
static void __exit mas_exit(void)
|
||
{
|
||
}
|
||
|
||
module_init(mas_init);
|
||
module_exit(mas_exit);
|
||
|
||
MODULE_AUTHOR("Microarray");
|
||
MODULE_DESCRIPTION("Driver for microarray fingerprint sensor");
|
||
MODULE_LICENSE("GPL");
|