Add samsung specific changes
This commit is contained in:
286
drivers/nfc/nfc_logger/nfc_logger.c
Normal file
286
drivers/nfc/nfc_logger/nfc_logger.c
Normal file
@@ -0,0 +1,286 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Samsung Electronics Co., Ltd.
|
||||
* http://www.samsung.com
|
||||
*
|
||||
* NFC logger
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/version.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/stat.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/ktime.h>
|
||||
#if LINUX_VERSION_CODE > KERNEL_VERSION(4, 10, 0)
|
||||
#include <linux/sched/clock.h>
|
||||
#else
|
||||
#include <linux/sched.h>
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SEC_NFC_LOGGER_ADD_ACPM_LOG
|
||||
#include <linux/io.h>
|
||||
#include <soc/samsung/acpm_ipc_ctrl.h>
|
||||
#include <soc/samsung/debug-snapshot.h>
|
||||
#endif
|
||||
|
||||
#include "nfc_logger.h"
|
||||
|
||||
#ifdef CONFIG_NFC_LOGGER_LOWMEM
|
||||
#define BUF_SIZE SZ_64K
|
||||
#else
|
||||
#define BUF_SIZE SZ_128K
|
||||
#endif
|
||||
#define BOOT_LOG_SIZE 2400
|
||||
#define MAX_STR_LEN 160
|
||||
#define PROC_FILE_NAME "nfclog"
|
||||
#define LOG_PREFIX "sec-nfc"
|
||||
#define PRINT_DATE_FREQ 20
|
||||
|
||||
static char nfc_log_buf[BUF_SIZE];
|
||||
static unsigned int g_curpos;
|
||||
static int is_nfc_logger_init;
|
||||
static int is_buf_full;
|
||||
static int g_log_max_count = -1;
|
||||
static void (*print_nfc_status)(void);
|
||||
struct proc_dir_entry *g_entry;
|
||||
|
||||
/* set max log count, if count is -1, no limit */
|
||||
void nfc_logger_set_max_count(int count)
|
||||
{
|
||||
g_log_max_count = count;
|
||||
}
|
||||
|
||||
void nfc_logger_get_date_time(char *date_time, int size)
|
||||
{
|
||||
struct timespec64 ts;
|
||||
struct tm tm;
|
||||
unsigned long sec;
|
||||
int len = 0;
|
||||
|
||||
ktime_get_real_ts64(&ts);
|
||||
sec = ts.tv_sec - (sys_tz.tz_minuteswest * 60);
|
||||
time64_to_tm(sec, 0, &tm);
|
||||
len = snprintf(date_time, size, "%02d-%02d %02d:%02d:%02d.%03lu", tm.tm_mon + 1, tm.tm_mday,
|
||||
tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec / 1000000);
|
||||
|
||||
#ifdef CONFIG_SEC_NFC_LOGGER_ADD_ACPM_LOG
|
||||
snprintf(date_time + len, size - len, ", rtc: %u", date_time,
|
||||
nfc_logger_acpm_get_rtc_time());
|
||||
#endif
|
||||
}
|
||||
|
||||
void nfc_logger_print(char *fmt, ...)
|
||||
{
|
||||
int len;
|
||||
va_list args;
|
||||
char buf[MAX_STR_LEN] = {0, };
|
||||
u64 time;
|
||||
u32 nsec;
|
||||
unsigned int curpos;
|
||||
static unsigned int log_count = PRINT_DATE_FREQ;
|
||||
char date_time[64] = {0, };
|
||||
bool date_time_print = false;
|
||||
|
||||
if (!is_nfc_logger_init)
|
||||
return;
|
||||
|
||||
if (g_log_max_count == 0)
|
||||
return;
|
||||
else if (g_log_max_count > 0)
|
||||
g_log_max_count--;
|
||||
|
||||
if (--log_count == 0) {
|
||||
nfc_logger_get_date_time(date_time, sizeof(date_time));
|
||||
log_count = PRINT_DATE_FREQ;
|
||||
date_time_print = true;
|
||||
}
|
||||
|
||||
time = local_clock();
|
||||
nsec = do_div(time, 1000000000);
|
||||
if (g_curpos < BOOT_LOG_SIZE)
|
||||
len = snprintf(buf, sizeof(buf), "[B%4llu.%06u] ", time, nsec / 1000);
|
||||
else
|
||||
len = snprintf(buf, sizeof(buf), "[%5llu.%06u] ", time, nsec / 1000);
|
||||
|
||||
if (date_time_print)
|
||||
len += snprintf(buf + len, sizeof(buf) - len, "[%s] ", date_time);
|
||||
|
||||
va_start(args, fmt);
|
||||
len += vsnprintf(buf + len, MAX_STR_LEN - len, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
if (len > MAX_STR_LEN)
|
||||
len = MAX_STR_LEN;
|
||||
|
||||
curpos = g_curpos;
|
||||
if (curpos + len >= BUF_SIZE) {
|
||||
g_curpos = curpos = BOOT_LOG_SIZE;
|
||||
is_buf_full = 1;
|
||||
}
|
||||
memcpy(nfc_log_buf + curpos, buf, len);
|
||||
g_curpos += len;
|
||||
}
|
||||
|
||||
void nfc_logger_register_nfc_stauts_func(void (*print_status_callback)(void))
|
||||
{
|
||||
print_nfc_status = print_status_callback;
|
||||
}
|
||||
|
||||
void nfc_print_hex_dump(void *buf, void *pref, size_t size)
|
||||
{
|
||||
uint8_t *ptr = buf;
|
||||
size_t i;
|
||||
char tmp[128] = {0x0, };
|
||||
char *ptmp = tmp;
|
||||
int len;
|
||||
|
||||
if (!is_nfc_logger_init)
|
||||
return;
|
||||
|
||||
if (g_log_max_count == 0)
|
||||
return;
|
||||
else if (g_log_max_count > 0)
|
||||
g_log_max_count--;
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
len = snprintf(ptmp, 4, "%02x ", *ptr++);
|
||||
ptmp = ptmp + len;
|
||||
if (((i+1)%16) == 0) {
|
||||
nfc_logger_print("%s%s\n", pref, tmp);
|
||||
ptmp = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
if (i % 16) {
|
||||
len = ptmp - tmp;
|
||||
tmp[len] = 0x0;
|
||||
nfc_logger_print("%s%s\n", pref, tmp);
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t nfc_logger_read(struct file *file, char __user *buf, size_t len, loff_t *offset)
|
||||
{
|
||||
loff_t pos = *offset;
|
||||
ssize_t count;
|
||||
size_t size;
|
||||
unsigned int curpos = g_curpos;
|
||||
|
||||
if (is_buf_full || BUF_SIZE <= curpos)
|
||||
size = BUF_SIZE;
|
||||
else
|
||||
size = (size_t)curpos;
|
||||
|
||||
if (pos >= size)
|
||||
return 0;
|
||||
|
||||
if (print_nfc_status && !pos)
|
||||
print_nfc_status();
|
||||
|
||||
count = min(len, size);
|
||||
|
||||
if ((pos + count) > size)
|
||||
count = size - pos;
|
||||
|
||||
if (copy_to_user(buf, nfc_log_buf + pos, count))
|
||||
return -EFAULT;
|
||||
|
||||
*offset += count;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
#if KERNEL_VERSION(5, 6, 0) <= LINUX_VERSION_CODE
|
||||
static const struct proc_ops nfc_logger_ops = {
|
||||
.proc_read = nfc_logger_read,
|
||||
.proc_lseek = default_llseek,
|
||||
};
|
||||
#else
|
||||
static const struct file_operations nfc_logger_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.read = nfc_logger_read,
|
||||
.llseek = default_llseek,
|
||||
};
|
||||
#endif
|
||||
|
||||
int nfc_logger_init(void)
|
||||
{
|
||||
struct proc_dir_entry *entry;
|
||||
|
||||
if (is_nfc_logger_init)
|
||||
return 0;
|
||||
|
||||
entry = proc_create(PROC_FILE_NAME, 0444, NULL, &nfc_logger_ops);
|
||||
if (!entry) {
|
||||
pr_err("%s: failed to create proc entry\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
proc_set_size(entry, BUF_SIZE);
|
||||
is_nfc_logger_init = 1;
|
||||
nfc_logger_print("nfc logger init ok\n");
|
||||
|
||||
g_entry = entry;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void nfc_logger_deinit(void)
|
||||
{
|
||||
if (!g_entry)
|
||||
return;
|
||||
|
||||
proc_remove(g_entry);
|
||||
g_entry = NULL;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SEC_NFC_LOGGER_ADD_ACPM_LOG
|
||||
static __iomem *g_rtc_reg;
|
||||
|
||||
u32 nfc_logger_acpm_get_rtc_time(void)
|
||||
{
|
||||
u32 rtc = 0;
|
||||
|
||||
if (g_rtc_reg)
|
||||
rtc = readl(g_rtc_reg);
|
||||
|
||||
return rtc;
|
||||
}
|
||||
|
||||
void nfc_logger_acpm_log_print(void)
|
||||
{
|
||||
struct nfc_clk_req_log *acpm_log;
|
||||
int last_ptr, len, i;
|
||||
u32 rtc;
|
||||
|
||||
if (!acpm_get_nfc_log_buf(&acpm_log, &last_ptr, &len)) {
|
||||
for (i = 0; i < len; i++) {
|
||||
rtc = nfc_logger_acpm_get_rtc_time();
|
||||
NFC_LOG_INFO("rtc[%u] - acpm[%2d][%d] %d\n",
|
||||
rtc, i, acpm_log[i].timestamp, acpm_log[i].is_on);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void nfc_logger_acpm_log_init(u32 rtc_addr)
|
||||
{
|
||||
u32 rtc_reg_addr = CONFIG_SEC_NFC_LOGGER_RTC_REG_ADDR;
|
||||
|
||||
if (rtc_addr)
|
||||
rtc_reg_addr = rtc_addr;
|
||||
|
||||
if (rtc_reg_addr) {
|
||||
NFC_LOG_INFO("rtc: 0x%X\n", rtc_reg_addr);
|
||||
g_rtc_reg = ioremap(rtc_reg_addr, 0x4);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
47
drivers/nfc/nfc_logger/nfc_logger.h
Normal file
47
drivers/nfc/nfc_logger/nfc_logger.h
Normal file
@@ -0,0 +1,47 @@
|
||||
#ifndef _NFC_LOGGER_H_
|
||||
#define _NFC_LOGGER_H_
|
||||
|
||||
#ifdef CONFIG_SEC_NFC_LOGGER
|
||||
|
||||
#define NFC_LOG_ERR(fmt, ...) \
|
||||
do { \
|
||||
pr_err("sec_nfc: "fmt, ##__VA_ARGS__); \
|
||||
nfc_logger_print(fmt, ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
#define NFC_LOG_INFO(fmt, ...) \
|
||||
do { \
|
||||
pr_info("sec_nfc: "fmt, ##__VA_ARGS__); \
|
||||
nfc_logger_print(fmt, ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
#define NFC_LOG_INFO_WITH_DATE(fmt, ...) \
|
||||
do { \
|
||||
char new_fmt[128] = {0, }; \
|
||||
char date_time[64] = {0, }; \
|
||||
pr_info("sec_nfc: "fmt, ##__VA_ARGS__); \
|
||||
nfc_logger_get_date_time(date_time, sizeof(date_time)); \
|
||||
snprintf(new_fmt, sizeof(new_fmt), "[%s] %s", date_time, fmt); \
|
||||
nfc_logger_print(new_fmt, ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
#define NFC_LOG_DBG(fmt, ...) \
|
||||
do { \
|
||||
pr_debug("sec_nfc: "fmt, ##__VA_ARGS__); \
|
||||
nfc_logger_print(fmt, ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
#define NFC_LOG_REC(fmt, ...) nfc_logger_print(fmt, ##__VA_ARGS__)
|
||||
|
||||
void nfc_logger_set_max_count(int count);
|
||||
void nfc_logger_get_date_time(char *date_time, int size);
|
||||
void nfc_logger_print(char *fmt, ...);
|
||||
void nfc_print_hex_dump(void *buf, void *pref, size_t len);
|
||||
int nfc_logger_init(void);
|
||||
void nfc_logger_deinit(void);
|
||||
void nfc_logger_register_nfc_stauts_func(void (*nfc_status_func)(void));
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SEC_NFC_LOGGER_ADD_ACPM_LOG
|
||||
u32 nfc_logger_acpm_get_rtc_time(void);
|
||||
void nfc_logger_acpm_log_print(void);
|
||||
void nfc_logger_acpm_log_init(u32 rtc_addr);
|
||||
#endif
|
||||
|
||||
#endif
|
146
drivers/nfc/nxp_combined/Kconfig
Normal file
146
drivers/nfc/nxp_combined/Kconfig
Normal file
@@ -0,0 +1,146 @@
|
||||
#
|
||||
# near field communication configuration
|
||||
#
|
||||
config SAMSUNG_NFC
|
||||
tristate "Samsung NFC driver"
|
||||
default n
|
||||
help
|
||||
Say Y here if you want to build support for NFC (Near field
|
||||
communication) devices.
|
||||
To compile this support as a module, choose M here: the module will
|
||||
be called nfc.
|
||||
|
||||
config NFC_NXP_COMBINED
|
||||
bool "NXP COMBINED driver Feature"
|
||||
default n
|
||||
help
|
||||
NXP Near Field Communication controller support.
|
||||
If this feature is enabled, sn2xx driver can support various ICs
|
||||
such as PN557, sn1xx and sn2xx.
|
||||
For one binary, this driver should be used.
|
||||
|
||||
config NFC_SN2XX
|
||||
bool "NXP SN2XX Feature"
|
||||
default n
|
||||
help
|
||||
NXP SN2XX Near Field Communication controller support
|
||||
This option enables device driver support for the NFC.
|
||||
It is used by many services. NFC is fully controlled using I2C
|
||||
to communicate the AP chip.
|
||||
|
||||
config NFC_SN2XX_ESE_SUPPORT
|
||||
tristate "Nxp secure element protocol driver (SPI) devices"
|
||||
depends on SPI
|
||||
help
|
||||
This enables the Secure Element driver for SNxxx based devices.
|
||||
If unsure, say N.
|
||||
This selects Secure Element support.
|
||||
If you want NFC support, you should say Y here and
|
||||
also to your specific host controller driver.
|
||||
|
||||
config ESE_USE_TZ_API
|
||||
bool "use tz api"
|
||||
depends on NFC_SN2XX_ESE_SUPPORT
|
||||
default n
|
||||
help
|
||||
Enable when using TZ API.
|
||||
You should select this feature if your NFC product
|
||||
uses S.LSI AP and TZ API.
|
||||
Say Y here to compile support for TZ API.
|
||||
|
||||
config SEC_NFC_LOGGER
|
||||
bool "NFC logger"
|
||||
default n
|
||||
help
|
||||
Enable NFC log.
|
||||
NFC log will be recorded at proc folder.
|
||||
but will not included at dumpstate log.
|
||||
so need to copy this file to log folder.
|
||||
|
||||
config NFC_LOGGER_LOWMEM
|
||||
bool "NFC logger low memory"
|
||||
default n
|
||||
help
|
||||
Reduces NFC log buffer size.
|
||||
This option reduces the size of the nfc log buffer in order to save memory on low-memory devices.
|
||||
By reducing its size, we can free up memory for other purposes,
|
||||
but it may also result in some log messages being lost if the buffer fills up quickly.
|
||||
|
||||
config SEC_NFC_WAKELOCK_METHOD
|
||||
int "nfc wakelock method"
|
||||
default 0
|
||||
help
|
||||
Different functions must be used depending on the kernel version
|
||||
for wakelock initialization.
|
||||
0 - auto selection
|
||||
1 - wakeup_source_init
|
||||
2 - wakeup_source_register
|
||||
|
||||
config SEC_NFC_LOGGER_ADD_ACPM_LOG
|
||||
bool "NFC logger: add acpm log"
|
||||
default n
|
||||
depends on SEC_NFC_LOGGER
|
||||
help
|
||||
add acpm log.
|
||||
this feature is for particualr AP.
|
||||
rtc reg addr is needed to compare time
|
||||
|
||||
config SEC_NFC_LOGGER_RTC_REG_ADDR
|
||||
hex "NFC logger: add acpm log"
|
||||
default 0x0
|
||||
depends on SEC_NFC_LOGGER_ADD_ACPM_LOG
|
||||
help
|
||||
RTC time is to compare between acpm and kernel.
|
||||
address value is hex.
|
||||
|
||||
config MAKE_NODE_USING_PLATFORM_DEVICE
|
||||
bool "eSE platform driver"
|
||||
default n
|
||||
help
|
||||
Using eSE platform driver.
|
||||
Sometimes eSE node is created after permission setting
|
||||
cause of SPI driver dependency.
|
||||
So, use platform driver to make node first.
|
||||
|
||||
config SEC_STAR
|
||||
tristate "LSI star platform"
|
||||
default n
|
||||
help
|
||||
LSI star platform
|
||||
This driver provides support for LSI star platform.
|
||||
|
||||
config STAR_MEMORY_LEAK
|
||||
bool "memory leak test in sec-star"
|
||||
help
|
||||
memory leak test in sec-star.
|
||||
All allocated memory are listed in array.
|
||||
|
||||
config STAR_K250A
|
||||
bool "S.LSI k250a driver"
|
||||
help
|
||||
S.LSI k250a driver except ISO7816 protocol layer
|
||||
This driver provides support for S.LSI k250a product.
|
||||
|
||||
config NFC_QTI_I2C
|
||||
tristate "QTI NCI based NFC I2C Driver for SNxxx"
|
||||
depends on I2C
|
||||
help
|
||||
This enables the NFC driver for SNxxx based devices.
|
||||
This is for I2C connected version. NCI protocol logic
|
||||
resides in the usermode and it has no other NFC dependencies.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config CLK_ACPM_INIT
|
||||
bool "ACPM INIT"
|
||||
default n
|
||||
help
|
||||
ACPM INIT must be called to use CLK3 in S5910.
|
||||
|
||||
config SEC_NFC_EINT_EXYNOS
|
||||
bool "enable eint mode for nfc clk"
|
||||
help
|
||||
This feature enable eint mode for nfc clk
|
||||
Need to trigger clk by clk req
|
||||
It is necessary for slsi ap if clk_req is assigned to no dedicated gpio.
|
||||
|
63
drivers/nfc/nxp_combined/Makefile
Normal file
63
drivers/nfc/nxp_combined/Makefile
Normal file
@@ -0,0 +1,63 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# Makefile for nfc devices
|
||||
#
|
||||
ifneq ($(SEC_BUILD_CONF_USE_ESE_TZ), false)
|
||||
ccflags-y += -DENABLE_ESE_SPI_SECURED
|
||||
endif
|
||||
|
||||
# for combined driver
|
||||
ifeq ($(CONFIG_NFC_NXP_COMBINED),y)
|
||||
ifeq ($(CONFIG_SAMSUNG_NFC),m)
|
||||
obj-$(CONFIG_SAMSUNG_NFC) += nfc_nxp_sec.o
|
||||
nfc_nxp_sec-$(CONFIG_NFC_SN2XX_ESE_SUPPORT) += p73.o ese_reset.o
|
||||
nfc_nxp_sec-$(CONFIG_SEC_NFC_LOGGER) += ../nfc_logger/nfc_logger.o
|
||||
nfc_nxp_sec-$(CONFIG_NFC_SN2XX) += common.o common_ese.o i2c_drv.o
|
||||
else
|
||||
obj-$(CONFIG_NFC_SN2XX) += nfc.o
|
||||
nfc-objs += common.o common_ese.o i2c_drv.o
|
||||
obj-$(CONFIG_NFC_SN2XX_ESE_SUPPORT) += p73.o ese_reset.o
|
||||
obj-$(CONFIG_SEC_NFC_LOGGER) += ../nfc_logger/nfc_logger.o
|
||||
endif # CONFIG_SAMSUNG_NFC
|
||||
|
||||
else # !CONFIG_NFC_NXP_COMBINED
|
||||
# for sn2xx only
|
||||
ifeq ($(CONFIG_SAMSUNG_NFC),m)
|
||||
obj-$(CONFIG_SAMSUNG_NFC) += nfc_sec.o
|
||||
nfc_sec-$(CONFIG_NFC_SN2XX_ESE_SUPPORT) += p73.o ese_reset.o
|
||||
nfc_sec-$(CONFIG_SEC_NFC_LOGGER) += nfc_logger/nfc_logger.o
|
||||
nfc_sec-$(CONFIG_NFC_SN2XX) += common.o common_ese.o i2c_drv.o
|
||||
else
|
||||
obj-$(CONFIG_NFC_SN2XX) += nfc.o
|
||||
nfc-objs += common.o common_ese.o i2c_drv.o
|
||||
obj-$(CONFIG_NFC_SN2XX_ESE_SUPPORT) += p73.o ese_reset.o
|
||||
obj-$(CONFIG_SEC_NFC_LOGGER) += nfc_logger/nfc_logger.o
|
||||
endif # CONFIG_SAMSUNG_NFC
|
||||
|
||||
|
||||
ifeq ($(CONFIG_SEC_STAR), m)
|
||||
obj-$(CONFIG_SEC_STAR) = sec-star/sec_star.o
|
||||
|
||||
sec_star-y += \
|
||||
sec-star/sec_star.o \
|
||||
sec-star/protocol/ese_data.o \
|
||||
sec-star/protocol/ese_iso7816_t1.o \
|
||||
sec-star/hal/ese_i2c.o \
|
||||
sec-star/hal/ese_spi.o \
|
||||
sec-star/hal/ese_hal.o
|
||||
|
||||
sec_star-$(CONFIG_STAR_K250A) += sec-star/sec_k250a.o
|
||||
else
|
||||
obj-$(CONFIG_SEC_STAR) += sec-star/sec_star.o \
|
||||
sec-star/protocol/ese_data.o \
|
||||
sec-star/protocol/ese_memory.o \
|
||||
sec-star/protocol/ese_iso7816_t1.o \
|
||||
sec-star/hal/ese_i2c.o \
|
||||
sec-star/hal/ese_spi.o \
|
||||
sec-star/hal/ese_hal.o
|
||||
|
||||
obj-$(CONFIG_STAR_K250A) += sec-star/sec_k250a.o
|
||||
endif # CONFIG_SEC_STAR
|
||||
endif # CONFIG_NFC_NXP_COMBINED
|
||||
|
||||
ccflags-y += -DRECOVERY_ENABLE -UDEBUG
|
1018
drivers/nfc/nxp_combined/common.c
Normal file
1018
drivers/nfc/nxp_combined/common.c
Normal file
File diff suppressed because it is too large
Load Diff
329
drivers/nfc/nxp_combined/common.h
Normal file
329
drivers/nfc/nxp_combined/common.h
Normal file
@@ -0,0 +1,329 @@
|
||||
/******************************************************************************
|
||||
* Copyright (C) 2015, The Linux Foundation. All rights reserved.
|
||||
* Copyright (C) 2019-2022 NXP
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
******************************************************************************/
|
||||
#ifndef _COMMON_H_
|
||||
#define _COMMON_H_
|
||||
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/fs.h>
|
||||
|
||||
#if IS_ENABLED(CONFIG_SAMSUNG_NFC)
|
||||
#include <linux/clk.h>
|
||||
#include <linux/reboot.h>
|
||||
#include "nfc_wakelock.h"
|
||||
#ifdef CONFIG_SEC_NFC_LOGGER
|
||||
#ifdef CONFIG_NFC_NXP_COMBINED
|
||||
#include "../nfc_logger/nfc_logger.h"
|
||||
#else
|
||||
#include "nfc_logger/nfc_logger.h"
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "i2c_drv.h"
|
||||
|
||||
/* Max device count for this driver */
|
||||
#define DEV_COUNT 1
|
||||
/* i2c device class */
|
||||
#if IS_ENABLED(CONFIG_SAMSUNG_NFC)
|
||||
#define CLASS_NAME "nfc_sec"
|
||||
#else
|
||||
#define CLASS_NAME "nfc"
|
||||
#endif
|
||||
|
||||
/* NFC character device name, this will be in /dev/ */
|
||||
#define NFC_CHAR_DEV_NAME "pn547"
|
||||
|
||||
/* NCI packet details */
|
||||
#define NCI_CMD (0x20)
|
||||
#define NCI_RSP (0x40)
|
||||
#define NCI_HDR_LEN (3)
|
||||
#define NCI_HDR_IDX (0)
|
||||
#define NCI_HDR_OID_IDX (1)
|
||||
#define NCI_PAYLOAD_IDX (3)
|
||||
#define NCI_PAYLOAD_LEN_IDX (2)
|
||||
|
||||
/* FW DNLD packet details */
|
||||
#define DL_HDR_LEN (2)
|
||||
#define DL_CRC_LEN (2)
|
||||
|
||||
#define MAX_NCI_PAYLOAD_LEN (255)
|
||||
#define MAX_NCI_BUFFER_SIZE (NCI_HDR_LEN + MAX_NCI_PAYLOAD_LEN)
|
||||
/*
|
||||
* Compile time option to select maximum writer buffer of either 4K or 550 bytes.
|
||||
* Default value is set as 4K. This value shall be chosen based on Hal flag "HDLL_4K_WRITE_SUPPORTED".
|
||||
* undef or comment HDLL_4K_WRITE_SUPPORTED to fallback to 550 bytes write frame buffer.
|
||||
*/
|
||||
#define HDLL_4K_WRITE_SUPPORTED
|
||||
#ifdef HDLL_4K_WRITE_SUPPORTED
|
||||
#define MAX_DL_PAYLOAD_LEN (4096)
|
||||
#else
|
||||
#define MAX_DL_PAYLOAD_LEN (550)
|
||||
#endif
|
||||
#define MAX_DL_BUFFER_SIZE (DL_HDR_LEN + DL_CRC_LEN + \
|
||||
MAX_DL_PAYLOAD_LEN)
|
||||
|
||||
|
||||
/* Retry count for normal write */
|
||||
#define NO_RETRY (1)
|
||||
/* Maximum retry count for standby writes */
|
||||
#define MAX_RETRY_COUNT (3)
|
||||
#define MAX_WRITE_IRQ_COUNT (5)
|
||||
#define MAX_IRQ_WAIT_TIME (90)
|
||||
#define WAKEUP_SRC_TIMEOUT (2000)
|
||||
|
||||
/* command response timeout */
|
||||
#define NCI_CMD_RSP_TIMEOUT_MS (2000)
|
||||
/* Time to wait for NFCC to be ready again after any change in the GPIO */
|
||||
#define NFC_GPIO_SET_WAIT_TIME_US (15000)
|
||||
/* Time to wait before retrying writes */
|
||||
#define WRITE_RETRY_WAIT_TIME_US (3000)
|
||||
/* Time to wait before retrying read for some specific usecases */
|
||||
#define READ_RETRY_WAIT_TIME_US (3500)
|
||||
#define NFC_MAGIC (0xE9)
|
||||
|
||||
/* Ioctls */
|
||||
/* The type should be aligned with MW HAL definitions */
|
||||
#define NFC_SET_PWR _IOW(NFC_MAGIC, 0x01, uint64_t)
|
||||
#define ESE_SET_PWR _IOW(NFC_MAGIC, 0x02, uint64_t)
|
||||
#define ESE_GET_PWR _IOR(NFC_MAGIC, 0x03, uint64_t)
|
||||
#define NFC_GET_GPIO_STATUS _IOR(NFC_MAGIC, 0x05, uint64_t)
|
||||
|
||||
#if IS_ENABLED(CONFIG_SAMSUNG_NFC)
|
||||
#define CONFIG_SAMSUNG_NFC_DEBUG
|
||||
#define FEATURE_CORE_RESET_NTF_CHECK
|
||||
|
||||
#define DTS_IRQ_GPIO_STR "pn547,irq-gpio"
|
||||
#define DTS_VEN_GPIO_STR "pn547,ven-gpio"
|
||||
#define DTS_FWDN_GPIO_STR "pn547,firm-gpio"
|
||||
|
||||
enum ap_vendors {
|
||||
AP_VENDOR_NONE,
|
||||
AP_VENDOR_SLSI,
|
||||
AP_VENDOR_QCT,
|
||||
AP_VENDOR_MTK,
|
||||
AP_VENDOR_ERR
|
||||
};
|
||||
|
||||
enum lpm_status {
|
||||
LPM_NO_SUPPORT = -1,
|
||||
LPM_FALSE,
|
||||
LPM_TRUE
|
||||
};
|
||||
#else
|
||||
#define DTS_IRQ_GPIO_STR "nxp,sn-irq"
|
||||
#define DTS_VEN_GPIO_STR "nxp,sn-ven-rstn"
|
||||
#define DTS_FWDN_GPIO_STR "nxp,sn-dwl-req"
|
||||
#endif
|
||||
|
||||
/* Each GPIO occupies consecutive two bits */
|
||||
#define GPIO_POS_SHIFT_VAL 2
|
||||
/* Two bits to indicate GPIO status (Invalid(-2), Set(1) or Reset(0)) */
|
||||
#define GPIO_STATUS_MASK_BITS 3
|
||||
|
||||
#ifndef CONFIG_SEC_NFC_LOGGER
|
||||
#define NFC_LOG_ERR(fmt, ...) pr_err("sec_nfc: "fmt, ##__VA_ARGS__)
|
||||
#define NFC_LOG_INFO(fmt, ...) pr_info("sec_nfc: "fmt, ##__VA_ARGS__)
|
||||
#define NFC_LOG_INFO_WITH_DATE(fmt, ...) pr_info("sec_nfc: "fmt, ##__VA_ARGS__)
|
||||
#define NFC_LOG_DBG(fmt, ...) pr_debug("sec_nfc: "fmt, ##__VA_ARGS__)
|
||||
#define NFC_LOG_REC(fmt, ...) do { } while (0)
|
||||
|
||||
#define nfc_print_hex_dump(a, b, c) do { } while (0)
|
||||
#define nfc_logger_init() do { } while (0)
|
||||
#define nfc_logger_deinit() do { } while (0)
|
||||
#define nfc_logger_set_max_count(a) do { } while (0)
|
||||
#define nfc_logger_register_nfc_stauts_func(a) do { } while (0)
|
||||
#endif /* CONFIG_SEC_NFC_LOGGER */
|
||||
|
||||
enum nfcc_ioctl_request {
|
||||
/* NFC disable request with VEN LOW */
|
||||
NFC_POWER_OFF = 0,
|
||||
/* NFC enable request with VEN Toggle */
|
||||
NFC_POWER_ON,
|
||||
/* firmware download request with VEN Toggle */
|
||||
NFC_FW_DWL_VEN_TOGGLE,
|
||||
/* ISO reset request */
|
||||
NFC_ISO_RESET,
|
||||
/* request for firmware download gpio HIGH */
|
||||
NFC_FW_DWL_HIGH,
|
||||
/* VEN hard reset request */
|
||||
NFC_VEN_FORCED_HARD_RESET,
|
||||
/* request for firmware download gpio LOW */
|
||||
NFC_FW_DWL_LOW,
|
||||
};
|
||||
|
||||
/* nfc platform interface type */
|
||||
enum interface_flags {
|
||||
/* I2C physical IF for NFCC */
|
||||
PLATFORM_IF_I2C = 0,
|
||||
};
|
||||
|
||||
/* nfc state flags */
|
||||
enum nfc_state_flags {
|
||||
/* nfc in unknown state */
|
||||
NFC_STATE_UNKNOWN = 0,
|
||||
/* nfc in download mode */
|
||||
NFC_STATE_FW_DWL = 0x1,
|
||||
/* nfc booted in NCI mode */
|
||||
NFC_STATE_NCI = 0x2,
|
||||
/* nfc booted in Fw teared mode */
|
||||
NFC_STATE_FW_TEARED = 0x4,
|
||||
};
|
||||
/*
|
||||
* Power state for IBI handing, mainly needed to defer the IBI handling
|
||||
* for the IBI received in suspend state to do it later in resume call
|
||||
*/
|
||||
enum pm_state_flags {
|
||||
PM_STATE_NORMAL = 0,
|
||||
PM_STATE_SUSPEND,
|
||||
PM_STATE_IBI_BEFORE_RESUME,
|
||||
};
|
||||
|
||||
/* Enum for GPIO values */
|
||||
enum gpio_values {
|
||||
GPIO_INPUT = 0x0,
|
||||
GPIO_OUTPUT = 0x1,
|
||||
GPIO_HIGH = 0x2,
|
||||
GPIO_OUTPUT_HIGH = 0x3,
|
||||
GPIO_IRQ = 0x4,
|
||||
};
|
||||
|
||||
#if IS_ENABLED(CONFIG_SAMSUNG_NFC)
|
||||
#define PLATFORM_DEFAULT_GPIO_CNT 3
|
||||
#endif
|
||||
/* NFC GPIO variables */
|
||||
struct platform_gpio {
|
||||
int irq;
|
||||
int ven;
|
||||
int dwl_req;
|
||||
#if IS_ENABLED(CONFIG_SAMSUNG_NFC)
|
||||
int clk_req;
|
||||
int clk_req_irq;
|
||||
bool clk_req_irq_enabled;
|
||||
#endif
|
||||
};
|
||||
|
||||
/* NFC Struct to get all the required configs from DTS */
|
||||
struct platform_configs {
|
||||
struct platform_gpio gpio;
|
||||
#if IS_ENABLED(CONFIG_SAMSUNG_NFC)
|
||||
bool clk_req_wake;
|
||||
bool clk_req_all_trigger;
|
||||
bool change_clkreq_for_acpm;
|
||||
int ap_vendor;
|
||||
struct regulator *nfc_pvdd;
|
||||
struct clk *nfc_clock;
|
||||
bool late_pvdd_en;
|
||||
bool disable_clk_irq_during_wakeup;
|
||||
struct notifier_block ldo_ocp_nb;
|
||||
#if IS_ENABLED(CONFIG_SEC_NFC_EINT_EXYNOS)
|
||||
bool clk_req_eint_mode;
|
||||
#endif
|
||||
#endif
|
||||
};
|
||||
|
||||
/* cold reset Features specific Parameters */
|
||||
struct cold_reset {
|
||||
bool rsp_pending; /* cmd rsp pending status */
|
||||
bool in_progress; /* for cold reset when gurad timer in progress */
|
||||
bool reset_protection; /* reset protection enabled/disabled */
|
||||
uint8_t status; /* status from response buffer */
|
||||
uint8_t rst_prot_src; /* reset protection source (SPI, NFC) */
|
||||
struct timer_list timer;
|
||||
wait_queue_head_t read_wq;
|
||||
};
|
||||
|
||||
/* Device specific structure */
|
||||
struct nfc_dev {
|
||||
wait_queue_head_t read_wq;
|
||||
struct mutex read_mutex;
|
||||
struct mutex write_mutex;
|
||||
uint8_t *read_kbuf;
|
||||
uint8_t *write_kbuf;
|
||||
struct mutex dev_ref_mutex;
|
||||
unsigned int dev_ref_count;
|
||||
struct class *nfc_class;
|
||||
struct device *nfc_device;
|
||||
struct cdev c_dev;
|
||||
dev_t devno;
|
||||
/* Interface flag */
|
||||
uint8_t interface;
|
||||
/* nfc state flags */
|
||||
uint8_t nfc_state;
|
||||
/* NFC VEN pin state */
|
||||
bool nfc_ven_enabled;
|
||||
bool release_read;
|
||||
union {
|
||||
struct i2c_dev i2c_dev;
|
||||
};
|
||||
struct platform_configs configs;
|
||||
struct cold_reset cold_reset;
|
||||
|
||||
#if IS_ENABLED(CONFIG_SAMSUNG_NFC)
|
||||
struct nfc_wake_lock nfc_wake_lock;
|
||||
struct nfc_wake_lock nfc_clk_wake_lock;
|
||||
bool clk_req_wakelock;
|
||||
bool screen_cfg;
|
||||
bool screen_on_cmd;
|
||||
bool screen_off_cmd;
|
||||
int screen_off_rsp_count;
|
||||
struct notifier_block reboot_nb;
|
||||
#endif
|
||||
|
||||
/* function pointers for the common i2c functionality */
|
||||
int (*nfc_read)(struct nfc_dev *dev, char *buf, size_t count,
|
||||
int timeout);
|
||||
int (*nfc_write)(struct nfc_dev *dev, const char *buf,
|
||||
const size_t count, int max_retry_cnt);
|
||||
int (*nfc_enable_intr)(struct nfc_dev *dev);
|
||||
int (*nfc_disable_intr)(struct nfc_dev *dev);
|
||||
#if IS_ENABLED(CONFIG_SAMSUNG_NFC)
|
||||
void (*nfc_enable_clk_intr)(struct nfc_dev *dev);
|
||||
void (*nfc_disable_clk_intr)(struct nfc_dev *dev);
|
||||
#endif
|
||||
};
|
||||
|
||||
int nfc_dev_open(struct inode *inode, struct file *filp);
|
||||
int nfc_dev_flush(struct file *pfile, fl_owner_t id);
|
||||
int nfc_dev_close(struct inode *inode, struct file *filp);
|
||||
long nfc_dev_compat_ioctl(struct file *pfile, unsigned int cmd,
|
||||
unsigned long arg);
|
||||
long nfc_dev_ioctl(struct file *pfile, unsigned int cmd, unsigned long arg);
|
||||
int nfc_parse_dt(struct device *dev, struct platform_configs *nfc_configs,
|
||||
uint8_t interface);
|
||||
int nfc_misc_register(struct nfc_dev *nfc_dev,
|
||||
const struct file_operations *nfc_fops, int count,
|
||||
char *devname, char *classname);
|
||||
void nfc_misc_unregister(struct nfc_dev *nfc_dev, int count);
|
||||
int configure_gpio(unsigned int gpio, int flag);
|
||||
void gpio_set_ven(struct nfc_dev *nfc_dev, int value);
|
||||
void gpio_free_all(struct nfc_dev *nfc_dev);
|
||||
int validate_nfc_state_nci(struct nfc_dev *nfc_dev);
|
||||
#if IS_ENABLED(CONFIG_SAMSUNG_NFC)
|
||||
int nfc_regulator_onoff(struct nfc_dev *nfc_dev, int onoff);
|
||||
void nfc_power_control(struct nfc_dev *nfc_dev);
|
||||
void nfc_print_status(void);
|
||||
void nfc_probe_done(struct nfc_dev *nfc_dev);
|
||||
bool nfc_check_pvdd_status(void);
|
||||
enum lpm_status nfc_get_lpcharge(void);
|
||||
#ifdef CONFIG_MAKE_NODE_USING_PLATFORM_DEVICE
|
||||
void nfc_parse_dt_for_platform_device(struct device *dev);
|
||||
#endif
|
||||
#endif
|
||||
#endif /* _COMMON_H_ */
|
360
drivers/nfc/nxp_combined/common_ese.c
Normal file
360
drivers/nfc/nxp_combined/common_ese.c
Normal file
@@ -0,0 +1,360 @@
|
||||
/******************************************************************************
|
||||
* Copyright (C) 2020-2021 NXP
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
******************************************************************************/
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/gpio.h>
|
||||
|
||||
#include "common_ese.h"
|
||||
|
||||
static void cold_reset_gaurd_timer_callback(struct timer_list *t)
|
||||
{
|
||||
struct cold_reset *cold_reset = from_timer(cold_reset, t, timer);
|
||||
|
||||
NFC_LOG_REC("%s: entry\n", __func__);
|
||||
cold_reset->in_progress = false;
|
||||
}
|
||||
|
||||
static long start_cold_reset_guard_timer(struct cold_reset *cold_reset)
|
||||
{
|
||||
long ret = -EINVAL;
|
||||
|
||||
if (timer_pending(&cold_reset->timer) == 1) {
|
||||
NFC_LOG_REC("%s: delete pending timer\n", __func__);
|
||||
/* delete timer if already pending */
|
||||
del_timer(&cold_reset->timer);
|
||||
}
|
||||
cold_reset->in_progress = true;
|
||||
timer_setup(&cold_reset->timer, cold_reset_gaurd_timer_callback, 0);
|
||||
ret = mod_timer(&cold_reset->timer,
|
||||
jiffies + msecs_to_jiffies(ESE_CLD_RST_GUARD_TIME_MS));
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int send_cold_reset_protection_cmd(struct nfc_dev *nfc_dev,
|
||||
bool requestType)
|
||||
{
|
||||
int ret = 0;
|
||||
int cmd_length = 0;
|
||||
uint8_t *cmd = nfc_dev->write_kbuf;
|
||||
struct cold_reset *cold_reset = &nfc_dev->cold_reset;
|
||||
|
||||
*cmd++ = NCI_PROP_MSG_CMD;
|
||||
|
||||
if (requestType) { /* reset protection */
|
||||
*cmd++ = RST_PROT_OID;
|
||||
*cmd++ = RST_PROT_PAYLOAD_SIZE;
|
||||
*cmd++ = (!cold_reset->reset_protection) ? 1 : 0;
|
||||
} else { /* cold reset */
|
||||
*cmd++ = CLD_RST_OID;
|
||||
*cmd++ = CLD_RST_PAYLOAD_SIZE;
|
||||
}
|
||||
cmd_length = cmd - nfc_dev->write_kbuf;
|
||||
|
||||
ret = nfc_dev->nfc_write(nfc_dev, nfc_dev->write_kbuf, cmd_length,
|
||||
MAX_RETRY_COUNT);
|
||||
if (ret != cmd_length) {
|
||||
ret = -EIO;
|
||||
NFC_LOG_ERR("%s: nfc_write returned %d\n", __func__, ret);
|
||||
goto exit;
|
||||
}
|
||||
cmd = nfc_dev->write_kbuf;
|
||||
if (requestType)
|
||||
NFC_LOG_DBG(" %s: NxpNciX: %d > 0x%02x%02x%02x%02x\n", __func__,
|
||||
ret, cmd[NCI_HDR_IDX], cmd[NCI_HDR_OID_IDX],
|
||||
cmd[NCI_PAYLOAD_LEN_IDX], cmd[NCI_PAYLOAD_IDX]);
|
||||
else
|
||||
NFC_LOG_DBG(" %s: NxpNciX: %d > 0x%02x%02x%02x\n", __func__, ret,
|
||||
cmd[NCI_HDR_IDX], cmd[NCI_HDR_OID_IDX],
|
||||
cmd[NCI_PAYLOAD_LEN_IDX]);
|
||||
exit:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void wakeup_on_prop_rsp(struct nfc_dev *nfc_dev, uint8_t *buf)
|
||||
{
|
||||
struct cold_reset *cold_reset = &nfc_dev->cold_reset;
|
||||
|
||||
cold_reset->status = -EIO;
|
||||
if ((NCI_HDR_LEN + buf[NCI_PAYLOAD_LEN_IDX]) != NCI_PROP_MSG_RSP_LEN)
|
||||
NFC_LOG_ERR("%s: invalid response for cold_reset/protection\n",
|
||||
__func__);
|
||||
else
|
||||
cold_reset->status = buf[NCI_PAYLOAD_IDX];
|
||||
|
||||
NFC_LOG_DBG(" %s: NxpNciR 0x%02x%02x%02x%02x\n", __func__,
|
||||
buf[NCI_HDR_IDX], buf[NCI_HDR_OID_IDX],
|
||||
buf[NCI_PAYLOAD_LEN_IDX], buf[NCI_PAYLOAD_IDX]);
|
||||
|
||||
cold_reset->rsp_pending = false;
|
||||
wake_up_interruptible(&cold_reset->read_wq);
|
||||
}
|
||||
|
||||
static int validate_cold_reset_protection_request(struct cold_reset *cold_reset,
|
||||
unsigned long arg)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (!cold_reset->reset_protection) {
|
||||
if (IS_RST_PROT_EN_REQ(arg) && IS_SRC_VALID_PROT(arg)) {
|
||||
NFC_LOG_REC("%s: reset protection enable\n", __func__);
|
||||
} else if (IS_CLD_RST_REQ(arg) && IS_SRC_VALID(arg)) {
|
||||
NFC_LOG_REC("%s: cold reset\n", __func__);
|
||||
} else if (IS_RST_PROT_DIS_REQ(arg) && IS_SRC_VALID_PROT(arg)) {
|
||||
NFC_LOG_REC("%s: reset protection already disable\n",
|
||||
__func__);
|
||||
ret = -EINVAL;
|
||||
} else {
|
||||
NFC_LOG_ERR("%s: operation not permitted\n", __func__);
|
||||
ret = -EPERM;
|
||||
}
|
||||
} else {
|
||||
if (IS_RST_PROT_DIS_REQ(arg) &&
|
||||
IS_SRC(arg, cold_reset->rst_prot_src)) {
|
||||
NFC_LOG_REC("%s: disable reset protection from same src\n",
|
||||
__func__);
|
||||
} else if (IS_CLD_RST_REQ(arg) &&
|
||||
IS_SRC(arg, cold_reset->rst_prot_src)) {
|
||||
NFC_LOG_REC("%s: cold reset from same source\n", __func__);
|
||||
} else if (IS_RST_PROT_EN_REQ(arg) &&
|
||||
IS_SRC(arg, cold_reset->rst_prot_src)) {
|
||||
NFC_LOG_REC("%s: enable reset protection from same src\n",
|
||||
__func__);
|
||||
} else {
|
||||
NFC_LOG_ERR("%s: operation not permitted\n", __func__);
|
||||
ret = -EPERM;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int perform_cold_reset_protection(struct nfc_dev *nfc_dev,
|
||||
unsigned long arg)
|
||||
{
|
||||
int ret = 0;
|
||||
int timeout = 0;
|
||||
char *rsp = nfc_dev->read_kbuf;
|
||||
struct cold_reset *cold_reset = &nfc_dev->cold_reset;
|
||||
|
||||
/* check if NFCC not in the FW download or hard reset state */
|
||||
ret = validate_nfc_state_nci(nfc_dev);
|
||||
if (ret < 0) {
|
||||
NFC_LOG_ERR("%s: invalid state\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* check if NFCC not in the FW download or hard reset state */
|
||||
ret = validate_cold_reset_protection_request(cold_reset, arg);
|
||||
if (ret < 0) {
|
||||
NFC_LOG_ERR("%s: invalid cmd\n", __func__);
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* check if cold reset already in progress */
|
||||
if (IS_CLD_RST_REQ(arg) && cold_reset->in_progress) {
|
||||
NFC_LOG_ERR("%s: cold reset already in progress\n", __func__);
|
||||
ret = -EBUSY;
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* enable interrupt if not enabled incase when devnode not opened by HAL */
|
||||
nfc_dev->nfc_enable_intr(nfc_dev);
|
||||
|
||||
mutex_lock(&nfc_dev->write_mutex);
|
||||
/* write api has 15ms maximum wait to clear any pending read before */
|
||||
cold_reset->status = -EIO;
|
||||
cold_reset->rsp_pending = true;
|
||||
|
||||
ret = send_cold_reset_protection_cmd(nfc_dev, IS_RST_PROT_REQ(arg));
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&nfc_dev->write_mutex);
|
||||
cold_reset->rsp_pending = false;
|
||||
NFC_LOG_ERR("%s: failed to send cold reset/protection cmd\n",
|
||||
__func__);
|
||||
goto err;
|
||||
}
|
||||
ret = 0;
|
||||
/* start the cold reset guard timer */
|
||||
if (IS_CLD_RST_REQ(arg)) {
|
||||
/* Guard timer not needed when OSU over NFC */
|
||||
if (!(cold_reset->reset_protection && IS_SRC_NFC(arg))) {
|
||||
ret = start_cold_reset_guard_timer(cold_reset);
|
||||
if (ret) {
|
||||
mutex_unlock(&nfc_dev->write_mutex);
|
||||
NFC_LOG_ERR("%s: error in mod_timer\n", __func__);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
timeout = NCI_CMD_RSP_TIMEOUT_MS;
|
||||
do {
|
||||
/* call read api directly if reader thread is not blocked */
|
||||
if (mutex_trylock(&nfc_dev->read_mutex)) {
|
||||
pr_debug("%s: reader thread not pending\n", __func__);
|
||||
ret = nfc_dev->nfc_read(nfc_dev, rsp, 3,
|
||||
timeout);
|
||||
mutex_unlock(&nfc_dev->read_mutex);
|
||||
if (!ret)
|
||||
break;
|
||||
usleep_range(READ_RETRY_WAIT_TIME_US,
|
||||
READ_RETRY_WAIT_TIME_US + 500);
|
||||
/* Read pending response form the HAL service */
|
||||
} else if (!wait_event_interruptible_timeout(
|
||||
cold_reset->read_wq,
|
||||
cold_reset->rsp_pending == false,
|
||||
msecs_to_jiffies(timeout))) {
|
||||
pr_err("%s: cold reset/prot response timeout\n", __func__);
|
||||
ret = -EAGAIN;
|
||||
}
|
||||
} while (ret == -ERESTARTSYS || ret == -EFAULT);
|
||||
mutex_unlock(&nfc_dev->write_mutex);
|
||||
|
||||
timeout = ESE_CLD_RST_REBOOT_GUARD_TIME_MS;
|
||||
if (ret == 0) { /* success case */
|
||||
ret = cold_reset->status;
|
||||
if (IS_RST_PROT_REQ(arg)) {
|
||||
cold_reset->reset_protection = IS_RST_PROT_EN_REQ(arg);
|
||||
cold_reset->rst_prot_src = IS_RST_PROT_EN_REQ(arg) ?
|
||||
GET_SRC(arg) :
|
||||
SRC_NONE;
|
||||
/* wait for reboot guard timer */
|
||||
} else if (wait_event_interruptible_timeout(
|
||||
cold_reset->read_wq, true,
|
||||
msecs_to_jiffies(timeout)) == 0) {
|
||||
NFC_LOG_INFO("%s: reboot guard timer timeout\n", __func__);
|
||||
}
|
||||
}
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* nfc_ese_pwr() - power control for ese
|
||||
* @nfc_dev: nfc device data structure
|
||||
* @arg: mode that we want to move to
|
||||
*
|
||||
* Device power control. Depending on the arg value, device moves to
|
||||
* different states, refer common_ese.h for args
|
||||
*
|
||||
* Return: -ENOIOCTLCMD if arg is not supported
|
||||
* 0 if Success(or no issue)
|
||||
* 0 or 1 in case of arg is ESE_POWER_STATE
|
||||
* and error ret code otherwise
|
||||
*/
|
||||
int nfc_ese_pwr(struct nfc_dev *nfc_dev, unsigned long arg)
|
||||
{
|
||||
int ret = 0;
|
||||
struct platform_gpio *nfc_gpio = &nfc_dev->configs.gpio;
|
||||
|
||||
if (arg == ESE_POWER_ON) {
|
||||
/*
|
||||
* Let's store the NFC VEN pin state
|
||||
* will check stored value in case of eSE power off request,
|
||||
* to find out if NFC MW also sent request to set VEN HIGH
|
||||
* VEN state will remain HIGH if NFC is enabled otherwise
|
||||
* it will be set as LOW
|
||||
*/
|
||||
nfc_dev->nfc_ven_enabled = gpio_get_value(nfc_gpio->ven);
|
||||
if (!nfc_dev->nfc_ven_enabled) {
|
||||
NFC_LOG_REC("%s: ese hal service setting ven high\n",
|
||||
__func__);
|
||||
gpio_set_ven(nfc_dev, 1);
|
||||
} else {
|
||||
NFC_LOG_REC("%s: ven already high\n", __func__);
|
||||
}
|
||||
} else if (arg == ESE_POWER_OFF) {
|
||||
if (!nfc_dev->nfc_ven_enabled) {
|
||||
NFC_LOG_REC("%s: nfc not enabled, disabling ven\n",
|
||||
__func__);
|
||||
gpio_set_ven(nfc_dev, 0);
|
||||
} else {
|
||||
NFC_LOG_REC("%s: keep ven high as nfc is enabled\n",
|
||||
__func__);
|
||||
}
|
||||
} else if (arg == ESE_POWER_STATE) {
|
||||
/* eSE get power state */
|
||||
ret = gpio_get_value(nfc_gpio->ven);
|
||||
} else if (IS_CLD_RST_REQ(arg) || IS_RST_PROT_REQ(arg)) {
|
||||
ret = perform_cold_reset_protection(nfc_dev, arg);
|
||||
} else {
|
||||
NFC_LOG_ERR("%s: bad arg %lu\n", __func__, arg);
|
||||
ret = -ENOIOCTLCMD;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(nfc_ese_pwr);
|
||||
|
||||
#define ESE_LEGACY_INTERFACE
|
||||
#ifdef ESE_LEGACY_INTERFACE
|
||||
static struct nfc_dev *nfc_dev_legacy;
|
||||
|
||||
/******************************************************************************
|
||||
* perform_ese_cold_reset() - It shall be called by others driver(not nfc/ese)
|
||||
* to perform cold reset only
|
||||
* @arg: request of cold reset from other drivers should be ESE_CLD_RST_OTHER
|
||||
*
|
||||
* Returns:- 0 in case of success and negative values in case of failure
|
||||
*****************************************************************************/
|
||||
int perform_ese_cold_reset(unsigned long arg)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (nfc_dev_legacy) {
|
||||
if (IS_CLD_RST_REQ(arg) && IS_SRC_OTHER(arg)) {
|
||||
ret = nfc_ese_pwr(nfc_dev_legacy, arg);
|
||||
} else {
|
||||
NFC_LOG_ERR("%s: operation not permitted\n", __func__);
|
||||
return -EPERM;
|
||||
}
|
||||
}
|
||||
NFC_LOG_DBG("%s: arg = %lu ret = %d\n", __func__, arg, ret);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(perform_ese_cold_reset);
|
||||
#endif /* ESE_LEGACY_INTERFACE */
|
||||
|
||||
void ese_cold_reset_release(struct nfc_dev *nfc_dev)
|
||||
{
|
||||
struct cold_reset *cold_reset = &nfc_dev->cold_reset;
|
||||
|
||||
cold_reset->rsp_pending = false;
|
||||
cold_reset->in_progress = false;
|
||||
if (timer_pending(&cold_reset->timer) == 1)
|
||||
del_timer(&cold_reset->timer);
|
||||
}
|
||||
|
||||
void common_ese_init(struct nfc_dev *nfc_dev)
|
||||
{
|
||||
struct cold_reset *cold_reset = &nfc_dev->cold_reset;
|
||||
|
||||
cold_reset->reset_protection = false;
|
||||
cold_reset->rst_prot_src = SRC_NONE;
|
||||
init_waitqueue_head(&cold_reset->read_wq);
|
||||
ese_cold_reset_release(nfc_dev);
|
||||
#ifdef ESE_LEGACY_INTERFACE
|
||||
nfc_dev_legacy = nfc_dev;
|
||||
#endif /* ESE_LEGACY_INTERFACE */
|
||||
}
|
||||
|
||||
void common_ese_exit(struct nfc_dev *nfc_dev)
|
||||
{
|
||||
#ifdef ESE_LEGACY_INTERFACE
|
||||
nfc_dev_legacy = NULL;
|
||||
#endif /* ESE_LEGACY_INTERFACE */
|
||||
}
|
101
drivers/nfc/nxp_combined/common_ese.h
Normal file
101
drivers/nfc/nxp_combined/common_ese.h
Normal file
@@ -0,0 +1,101 @@
|
||||
/******************************************************************************
|
||||
* Copyright (C) 2020-2021 NXP
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
******************************************************************************/
|
||||
#ifndef _COMMON_ESE_H_
|
||||
#define _COMMON_ESE_H_
|
||||
|
||||
#include "common.h"
|
||||
|
||||
/* nci prop msg 1st byte */
|
||||
#define NCI_PROP_MSG_GID 0x0F
|
||||
#define NCI_PROP_MSG_CMD (NCI_CMD | NCI_PROP_MSG_GID)
|
||||
#define NCI_PROP_MSG_RSP (NCI_RSP | NCI_PROP_MSG_GID)
|
||||
|
||||
/* nci prop msg 2nd byte */
|
||||
#define CLD_RST_OID 0x1E
|
||||
#define RST_PROT_OID 0x1F
|
||||
|
||||
/* nci prop msg 3rd byte */
|
||||
#define CLD_RST_PAYLOAD_SIZE 0x00
|
||||
#define RST_PROT_PAYLOAD_SIZE 0x01
|
||||
|
||||
/* nci prop msg response length */
|
||||
#define NCI_PROP_MSG_RSP_LEN 0x04
|
||||
|
||||
/* cold reset guard time to allow back to back cold reset after some time */
|
||||
#define ESE_CLD_RST_GUARD_TIME_MS (3000)
|
||||
/* guard time to reboot after reset */
|
||||
#define ESE_CLD_RST_REBOOT_GUARD_TIME_MS (50)
|
||||
/* sources of reset protection and cold reset */
|
||||
enum reset_source {
|
||||
SRC_SPI = 0,
|
||||
SRC_NFC = 0x10,
|
||||
SRC_OTHER = 0x20,
|
||||
SRC_NONE = 0x80,
|
||||
};
|
||||
|
||||
enum ese_ioctl_request {
|
||||
ESE_POWER_ON = 0, /* eSE POWER ON */
|
||||
ESE_POWER_OFF, /* eSE POWER OFF */
|
||||
ESE_POWER_STATE, /* eSE GET POWER STATE */
|
||||
|
||||
/* ese reset requests from eSE service/hal/driver */
|
||||
ESE_CLD_RST, /* eSE COLD RESET */
|
||||
ESE_RST_PROT_EN, /* eSE RESET PROTECTION ENABLE */
|
||||
ESE_RST_PROT_DIS, /* eSE RESET PROTECTION DISABLE */
|
||||
|
||||
/* similar ese reset requests from nfc service/hal/driver */
|
||||
ESE_CLD_RST_NFC = ESE_CLD_RST | SRC_NFC,
|
||||
ESE_RST_PROT_EN_NFC = ESE_RST_PROT_EN | SRC_NFC,
|
||||
ESE_RST_PROT_DIS_NFC = ESE_RST_PROT_DIS | SRC_NFC,
|
||||
|
||||
/* similar ese reset requests from other service/hal/driver */
|
||||
ESE_CLD_RST_OTHER = ESE_CLD_RST | SRC_OTHER,
|
||||
};
|
||||
|
||||
#define GET_SRC(arg) (arg & 0xF0)
|
||||
#define IS_SRC(arg, src) (GET_SRC(arg) == src)
|
||||
#define IS_SRC_SPI(arg) IS_SRC(arg, SRC_SPI)
|
||||
#define IS_SRC_NFC(arg) IS_SRC(arg, SRC_NFC)
|
||||
#define IS_SRC_OTHER(arg) IS_SRC(arg, SRC_OTHER)
|
||||
#define IS_SRC_VALID(arg) (IS_SRC_SPI(arg) || \
|
||||
IS_SRC_NFC(arg) || \
|
||||
IS_SRC_OTHER(arg))
|
||||
#define IS_SRC_VALID_PROT(arg) (IS_SRC_SPI(arg) || \
|
||||
IS_SRC_NFC(arg))
|
||||
|
||||
#define IS_RST(arg, type) ((arg & 0xF) == type)
|
||||
#define IS_CLD_RST_REQ(arg) IS_RST(arg, ESE_CLD_RST)
|
||||
#define IS_RST_PROT_EN_REQ(arg) IS_RST(arg, ESE_RST_PROT_EN)
|
||||
#define IS_RST_PROT_DIS_REQ(arg) IS_RST(arg, ESE_RST_PROT_DIS)
|
||||
#define IS_RST_PROT_REQ(arg) (IS_RST_PROT_EN_REQ(arg) || \
|
||||
IS_RST_PROT_DIS_REQ(arg))
|
||||
/* This macro evaluates to 1 if prop cmd response is received */
|
||||
#define IS_PROP_CMD_RSP(buf) ((buf[0] == NCI_PROP_MSG_RSP) && \
|
||||
((buf[1] == CLD_RST_OID) || \
|
||||
(buf[1] == RST_PROT_OID)))
|
||||
|
||||
void wakeup_on_prop_rsp(struct nfc_dev *nfc_dev, uint8_t *buf);
|
||||
int nfc_ese_pwr(struct nfc_dev *nfc_dev, unsigned long arg);
|
||||
void ese_cold_reset_release(struct nfc_dev *nfc_dev);
|
||||
void common_ese_init(struct nfc_dev *nfc_dev);
|
||||
void common_ese_exit(struct nfc_dev *nfc_dev);
|
||||
#if IS_ENABLED(CONFIG_SAMSUNG_NFC)
|
||||
void ese_set_spi_pinctrl_for_ese_off(void *p61);
|
||||
#endif
|
||||
#endif /* _COMMON_ESE_H_ */
|
160
drivers/nfc/nxp_combined/ese_reset.c
Normal file
160
drivers/nfc/nxp_combined/ese_reset.c
Normal file
@@ -0,0 +1,160 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright 2023 NXP
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
******************************************************************************/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include "common_ese.h"
|
||||
#include "ese_reset.h"
|
||||
|
||||
static reset_timer_t sResetTimer;
|
||||
|
||||
/*!
|
||||
* \ingroup spi_driver
|
||||
* \brief Function getting invoked after eSE reset guard time
|
||||
* \n expiry
|
||||
* \param[in] struct timer_list *
|
||||
*/
|
||||
static void gpio_reset_guard_timer_callback(struct timer_list *t)
|
||||
{
|
||||
struct reset_timer *sResetTimer = from_timer(sResetTimer, t, timer);
|
||||
|
||||
NFC_LOG_INFO("%s: entry\n", __func__);
|
||||
sResetTimer->in_progress = false;
|
||||
NFC_LOG_INFO("%s: exit with in_progress set to false\n", __func__);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \ingroup spi_driver
|
||||
* \brief Initialising mutex
|
||||
*/
|
||||
void ese_reset_init(void)
|
||||
{
|
||||
mutex_init(&sResetTimer.reset_mutex);
|
||||
timer_setup(&sResetTimer.timer, gpio_reset_guard_timer_callback, 0);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \ingroup spi_driver
|
||||
* \brief Deinitialising mutex
|
||||
*/
|
||||
void ese_reset_deinit(void)
|
||||
{
|
||||
mutex_destroy(&sResetTimer.reset_mutex);
|
||||
del_timer(&sResetTimer.timer);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \ingroup spi_driver
|
||||
* \brief Setting the timer on
|
||||
* \return long 0 for inactive timer 1 for active timer
|
||||
*/
|
||||
static long start_gpio_reset_guard_timer(void)
|
||||
{
|
||||
long ret;
|
||||
|
||||
NFC_LOG_INFO("%s: entry\n", __func__);
|
||||
ret = mod_timer(&sResetTimer.timer,
|
||||
jiffies + msecs_to_jiffies(ESE_GPIO_RST_GUARD_TIME_MS));
|
||||
if (!ret)
|
||||
sResetTimer.in_progress = true;
|
||||
else
|
||||
NFC_LOG_ERR("%s: Error in mod_timer, returned:'%ld'\n", __func__, ret);
|
||||
NFC_LOG_INFO("%s: exit\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \ingroup spi_driver
|
||||
* \brief Reset the gpio by toggling low and high state
|
||||
* \param struct p61_dev
|
||||
*/
|
||||
int perform_ese_gpio_reset(int rst_gpio)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (gpio_is_valid(rst_gpio)) {
|
||||
NFC_LOG_INFO("%s: entry\n", __func__);
|
||||
mutex_lock(&sResetTimer.reset_mutex);
|
||||
if (sResetTimer.in_progress) {
|
||||
NFC_LOG_ERR("%s: gpio reset already in progress\n", __func__);
|
||||
ret = -EBUSY;
|
||||
mutex_unlock(&sResetTimer.reset_mutex);
|
||||
return ret;
|
||||
}
|
||||
NFC_LOG_INFO("%s: entering gpio ese reset case\n", __func__);
|
||||
ret = start_gpio_reset_guard_timer();
|
||||
if (ret) {
|
||||
mutex_unlock(&sResetTimer.reset_mutex);
|
||||
NFC_LOG_ERR("%s: error in mod_timer\n", __func__);
|
||||
ret = -EINVAL;
|
||||
return ret;
|
||||
}
|
||||
NFC_LOG_INFO(" eSE Domain Reset");
|
||||
|
||||
gpio_set_value(rst_gpio, 0);
|
||||
usleep_range(ESE_GPIO_RESET_WAIT_TIME_USEC,
|
||||
ESE_GPIO_RESET_WAIT_TIME_USEC + 100);
|
||||
gpio_set_value(rst_gpio, 1);
|
||||
NFC_LOG_INFO("%s: exit\n", __func__);
|
||||
mutex_unlock(&sResetTimer.reset_mutex);
|
||||
} else {
|
||||
NFC_LOG_ERR("%s not entering GPIO Reset, gpio value invalid : %x\n",
|
||||
__func__, rst_gpio);
|
||||
return -EPERM;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \ingroup spi_driver
|
||||
* \brief setup ese reset gpio pin as an output pin
|
||||
* \param struct p61_spi_platform_data *
|
||||
* \return Return 0 in case of success, else an error code
|
||||
*/
|
||||
int ese_reset_gpio_setup(struct p61_spi_platform_data *platform_data)
|
||||
{
|
||||
int ret = -1;
|
||||
|
||||
if (gpio_is_valid(platform_data->rst_gpio)) {
|
||||
ret = gpio_request(platform_data->rst_gpio, "p61 reset");
|
||||
if (ret < 0) {
|
||||
NFC_LOG_ERR("reset gpio 0x%x request failed\n", platform_data->rst_gpio);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* reset gpio is set to default high */
|
||||
ret = gpio_direction_output(platform_data->rst_gpio, 1);
|
||||
if (ret < 0) {
|
||||
NFC_LOG_ERR("Failed to set the direction of reset gpio=0x%x\n",
|
||||
platform_data->rst_gpio);
|
||||
goto fail_gpio;
|
||||
}
|
||||
NFC_LOG_INFO("Exit : %s\n", __func__);
|
||||
} else {
|
||||
NFC_LOG_INFO("%s, gpio value invalid : %x\n", __func__,
|
||||
platform_data->rst_gpio);
|
||||
}
|
||||
return ret;
|
||||
fail_gpio:
|
||||
if (gpio_is_valid(platform_data->rst_gpio))
|
||||
gpio_free(platform_data->rst_gpio);
|
||||
|
||||
return ret;
|
||||
}
|
59
drivers/nfc/nxp_combined/ese_reset.h
Normal file
59
drivers/nfc/nxp_combined/ese_reset.h
Normal file
@@ -0,0 +1,59 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright 2023 NXP Semiconductors
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
******************************************************************************/
|
||||
#ifndef _ESE_RESET_H_
|
||||
#define _ESE_RESET_H_
|
||||
#include <linux/ktime.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/gpio.h>
|
||||
#include "p73.h"
|
||||
/*!
|
||||
* \brief Delay time required for ese reset by gpio
|
||||
*/
|
||||
#define ESE_GPIO_RESET_WAIT_TIME_USEC (12000)
|
||||
|
||||
/*!
|
||||
* \brief Guard time delay to prevent back to back gpio reset requests
|
||||
*/
|
||||
#define ESE_GPIO_RST_GUARD_TIME_MS (50)
|
||||
|
||||
/*!
|
||||
* \brief Argument values to be passed to ioctl call
|
||||
*/
|
||||
#define ESE_SOFT_RESET (1UL)
|
||||
#define ESE_HARD_RESET (2UL)
|
||||
#define ESE_DOMAIN_RESET (5UL)
|
||||
|
||||
/*!
|
||||
* \brief To denote gpio reset currently in progress
|
||||
*/
|
||||
typedef struct reset_timer {
|
||||
int rst_gpio;
|
||||
bool in_progress;
|
||||
struct mutex reset_mutex;
|
||||
struct timer_list timer;
|
||||
} reset_timer_t;
|
||||
|
||||
void ese_reset_init(void);
|
||||
int perform_ese_gpio_reset(int rst_gpio);
|
||||
void ese_reset_deinit(void);
|
||||
int ese_reset_gpio_setup(struct p61_spi_platform_data *data);
|
||||
#endif
|
1228
drivers/nfc/nxp_combined/i2c_drv.c
Normal file
1228
drivers/nfc/nxp_combined/i2c_drv.c
Normal file
File diff suppressed because it is too large
Load Diff
57
drivers/nfc/nxp_combined/i2c_drv.h
Normal file
57
drivers/nfc/nxp_combined/i2c_drv.h
Normal file
@@ -0,0 +1,57 @@
|
||||
/******************************************************************************
|
||||
* Copyright (C) 2015, The Linux Foundation. All rights reserved.
|
||||
* Copyright (C) 2019-2021 NXP
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
******************************************************************************/
|
||||
#ifndef _I2C_DRV_H_
|
||||
#define _I2C_DRV_H_
|
||||
|
||||
#include <linux/i2c.h>
|
||||
|
||||
/* kept same as dts */
|
||||
#define NFC_I2C_DRV_STR "pn547"
|
||||
#define NFC_I2C_DEV_ID "pn547"
|
||||
|
||||
/* Interface specific parameters */
|
||||
struct i2c_dev {
|
||||
struct i2c_client *client;
|
||||
/* IRQ parameters */
|
||||
bool irq_enabled;
|
||||
spinlock_t irq_enabled_lock;
|
||||
/* NFC_IRQ wake-up state */
|
||||
bool irq_wake_up;
|
||||
};
|
||||
|
||||
long nfc_i2c_dev_ioctl(struct file *pfile, unsigned int cmd, unsigned long arg);
|
||||
#if (KERNEL_VERSION(6, 3, 0) <= LINUX_VERSION_CODE)
|
||||
int nfc_i2c_dev_probe(struct i2c_client *client);
|
||||
#else
|
||||
int nfc_i2c_dev_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id);
|
||||
#endif
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0)
|
||||
int nfc_i2c_dev_remove(struct i2c_client *client);
|
||||
#else
|
||||
void nfc_i2c_dev_remove(struct i2c_client *client);
|
||||
#endif
|
||||
int nfc_i2c_dev_suspend(struct device *device);
|
||||
int nfc_i2c_dev_resume(struct device *device);
|
||||
#ifdef CONFIG_CLK_ACPM_INIT
|
||||
extern acpm_init_eint_nfc_clk_req(u32 eint_num);
|
||||
#endif
|
||||
|
||||
#endif /* _I2C_DRV_H_ */
|
74
drivers/nfc/nxp_combined/nfc_wakelock.h
Normal file
74
drivers/nfc/nxp_combined/nfc_wakelock.h
Normal file
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* NFC Wakelock
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _NFC_WAKELOCK_H
|
||||
#define _NFC_WAKELOCK_H
|
||||
|
||||
#include <linux/ktime.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/version.h>
|
||||
|
||||
#define wake_lock_init(a, b, c) nfc_wake_lock_init(a, c)
|
||||
#define wake_lock_destroy(a) nfc_wake_lock_destroy(a)
|
||||
#define wake_lock_timeout(a, b) nfc_wake_lock_timeout(a, b)
|
||||
#define wake_lock_active(a) nfc_wake_lock_active(a)
|
||||
#define wake_lock(a) nfc_wake_lock(a)
|
||||
#define wake_unlock(a) nfc_wake_unlock(a)
|
||||
|
||||
struct nfc_wake_lock {
|
||||
struct wakeup_source *ws;
|
||||
};
|
||||
|
||||
static inline void nfc_wake_lock_init(struct nfc_wake_lock *lock, const char *name)
|
||||
{
|
||||
#if CONFIG_SEC_NFC_WAKELOCK_METHOD == 2
|
||||
lock->ws = wakeup_source_register(NULL, name);
|
||||
#elif (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)) || CONFIG_SEC_NFC_WAKELOCK_METHOD == 1
|
||||
wakeup_source_init(lock->ws, name); /* 4.19 R */
|
||||
if (!(lock->ws)) {
|
||||
lock->ws = wakeup_source_create(name); /* 4.19 Q */
|
||||
if (lock->ws)
|
||||
wakeup_source_add(lock->ws);
|
||||
}
|
||||
#else
|
||||
lock->ws = wakeup_source_register(NULL, name); /* 5.4 R */
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void nfc_wake_lock_destroy(struct nfc_wake_lock *lock)
|
||||
{
|
||||
if (lock->ws)
|
||||
wakeup_source_unregister(lock->ws);
|
||||
}
|
||||
|
||||
static inline void nfc_wake_lock(struct nfc_wake_lock *lock)
|
||||
{
|
||||
if (lock->ws)
|
||||
__pm_stay_awake(lock->ws);
|
||||
}
|
||||
|
||||
static inline void nfc_wake_lock_timeout(struct nfc_wake_lock *lock, long timeout)
|
||||
{
|
||||
if (lock->ws)
|
||||
__pm_wakeup_event(lock->ws, jiffies_to_msecs(timeout));
|
||||
}
|
||||
|
||||
static inline void nfc_wake_unlock(struct nfc_wake_lock *lock)
|
||||
{
|
||||
if (lock->ws)
|
||||
__pm_relax(lock->ws);
|
||||
}
|
||||
|
||||
static inline int nfc_wake_lock_active(struct nfc_wake_lock *lock)
|
||||
{
|
||||
return lock->ws->active;
|
||||
}
|
||||
|
||||
#endif
|
1883
drivers/nfc/nxp_combined/p73.c
Normal file
1883
drivers/nfc/nxp_combined/p73.c
Normal file
File diff suppressed because it is too large
Load Diff
113
drivers/nfc/nxp_combined/p73.h
Normal file
113
drivers/nfc/nxp_combined/p73.h
Normal file
@@ -0,0 +1,113 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (C) 2012-2020 NXP Semiconductors
|
||||
* *
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef _P73_H_
|
||||
#define _P73_H_
|
||||
#define P61_MAGIC 0xEB
|
||||
#define P61_SET_PWR _IOW(P61_MAGIC, 0x01, long)
|
||||
#define P61_SET_DBG _IOW(P61_MAGIC, 0x02, long)
|
||||
#define P61_SET_POLL _IOW(P61_MAGIC, 0x03, long)
|
||||
/*
|
||||
* SPI Request NFCC to enable p61 power, only in param
|
||||
* Only for SPI
|
||||
* level 1 = Enable power
|
||||
* level 0 = Disable power
|
||||
*/
|
||||
#define P61_SET_SPM_PWR _IOW(P61_MAGIC, 0x04, long)
|
||||
|
||||
/* SPI or DWP can call this ioctl to get the current
|
||||
* power state of P61
|
||||
*
|
||||
*/
|
||||
#define P61_GET_SPM_STATUS _IOR(P61_MAGIC, 0x05, long)
|
||||
|
||||
/* throughput measurement is deprecated */
|
||||
/* #define P61_SET_THROUGHPUT _IOW(P61_MAGIC, 0x06, long) */
|
||||
#define P61_GET_ESE_ACCESS _IOW(P61_MAGIC, 0x07, long)
|
||||
|
||||
#define P61_SET_POWER_SCHEME _IOW(P61_MAGIC, 0x08, long)
|
||||
|
||||
#define P61_SET_DWNLD_STATUS _IOW(P61_MAGIC, 0x09, long)
|
||||
|
||||
#define P61_INHIBIT_PWR_CNTRL _IOW(P61_MAGIC, 0x0A, long)
|
||||
|
||||
/* SPI can call this IOCTL to perform the eSE COLD_RESET
|
||||
* via NFC driver.
|
||||
*/
|
||||
#define ESE_PERFORM_COLD_RESET _IOW(P61_MAGIC, 0x0C, long)
|
||||
|
||||
#define PERFORM_RESET_PROTECTION _IOW(P61_MAGIC, 0x0D, long)
|
||||
|
||||
#define ESE_SET_TRUSTED_ACCESS _IOW(P61_MAGIC, 0x0B, long)
|
||||
|
||||
#define P61_RW_SPI_DATA _IOWR(P61_MAGIC, 0x0F, unsigned long)
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
#define P61_SET_PWR_COMPAT _IOW(P61_MAGIC, 0x01, unsigned int)
|
||||
#define P61_SET_DBG_COMPAT _IOW(P61_MAGIC, 0x02, unsigned int)
|
||||
#define P61_SET_POLL_COMPAT _IOW(P61_MAGIC, 0x03, unsigned int)
|
||||
#define P61_SET_SPM_PWR_COMPAT _IOW(P61_MAGIC, 0x04, unsigned int)
|
||||
#define P61_GET_SPM_STATUS_COMPAT _IOR(P61_MAGIC, 0x05, unsigned int)
|
||||
/* throughput measurement is deprecated */
|
||||
/* #define P61_SET_THROUGHPUT_COMPAT _IOW(P61_MAGIC, 0x06, unsigned int) */
|
||||
#define P61_GET_ESE_ACCESS_COMPAT _IOW(P61_MAGIC, 0x07, unsigned int)
|
||||
#define P61_SET_POWER_SCHEME_COMPAT _IOW(P61_MAGIC, 0x08, unsigned int)
|
||||
#define P61_SET_DWNLD_STATUS_COMPAT _IOW(P61_MAGIC, 0x09, unsigned int)
|
||||
#define P61_INHIBIT_PWR_CNTRL_COMPAT _IOW(P61_MAGIC, 0x0A, unsigned int)
|
||||
#define P61_RW_SPI_DATA_COMPAT _IOWR(P61_MAGIC, 0x0F, unsigned int)
|
||||
|
||||
#define ESE_PERFORM_COLD_RESET_COMPAT _IOW(P61_MAGIC, 0x0C, unsigned int)
|
||||
#define PERFORM_RESET_PROTECTION_COMPAT _IOW(P61_MAGIC, 0x0D, unsigned int)
|
||||
#endif
|
||||
|
||||
typedef enum ese_spi_transition_state {
|
||||
ESE_SPI_IDLE = 0x00,
|
||||
ESE_SPI_BUSY
|
||||
} ese_spi_transition_state_t;
|
||||
|
||||
struct p61_spi_platform_data {
|
||||
int irq_gpio;
|
||||
int rst_gpio;
|
||||
int trusted_ese_gpio;
|
||||
bool gpio_coldreset;
|
||||
#if IS_ENABLED(CONFIG_SAMSUNG_NFC)
|
||||
int ap_vendor;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct p61_ioctl_transfer {
|
||||
unsigned char *rx_buffer;
|
||||
unsigned char *tx_buffer;
|
||||
unsigned int len;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
struct p61_ioctl_transfer32 {
|
||||
u32 rx_buffer;
|
||||
u32 tx_buffer;
|
||||
u32 len;
|
||||
};
|
||||
#endif
|
||||
|
||||
#if IS_ENABLED(CONFIG_SAMSUNG_NFC)
|
||||
void store_nfc_i2c_device(struct device *nfc_i2c_dev);
|
||||
void p61_print_status(const char *func_name);
|
||||
#endif
|
||||
#endif
|
1325
drivers/nfc/nxp_combined/recovery_fw.c
Normal file
1325
drivers/nfc/nxp_combined/recovery_fw.c
Normal file
File diff suppressed because it is too large
Load Diff
41
drivers/nfc/snvm/Kconfig
Normal file
41
drivers/nfc/snvm/Kconfig
Normal file
@@ -0,0 +1,41 @@
|
||||
#
|
||||
# SEC STAR platform devices
|
||||
#
|
||||
|
||||
config STAR_MEMORY_LEAK
|
||||
bool "memory leak test in sec-star"
|
||||
help
|
||||
memory leak test in sec-star.
|
||||
All allocated memory are listed in array.
|
||||
|
||||
config STAR_K250A_LEGO
|
||||
tristate "S.LSI k250a driver"
|
||||
default n
|
||||
help
|
||||
S.LSI k250a driver except ISO7816 protocol layer
|
||||
This driver provides support for S.LSI k250a product.
|
||||
|
||||
config SEC_SNVM_WAKELOCK_METHOD
|
||||
int "snvm wakelock method"
|
||||
default 0
|
||||
help
|
||||
Different functions must be used depending on the kernel version
|
||||
for wakelock initialization.
|
||||
0 - auto selection
|
||||
1 - wakeup_source_init
|
||||
2 - wakeup_source_register
|
||||
|
||||
config SEC_SNVM_PLATFORM_DRV
|
||||
bool "snvm platform driver"
|
||||
default n
|
||||
help
|
||||
make snvm driver to platfrom driver
|
||||
disable i2c node
|
||||
|
||||
config SEC_SNVM_I2C_CLOCK_CONTROL
|
||||
bool "snvm i2c clock control"
|
||||
default n
|
||||
help
|
||||
control the I2C clocks
|
||||
now only for MTK project
|
||||
|
24
drivers/nfc/snvm/Makefile
Normal file
24
drivers/nfc/snvm/Makefile
Normal file
@@ -0,0 +1,24 @@
|
||||
#
|
||||
# Makefile for Samsung Secure Element
|
||||
#
|
||||
ifeq ($(CONFIG_STAR_K250A_LEGO), m)
|
||||
obj-$(CONFIG_STAR_K250A_LEGO) = snvm.o
|
||||
|
||||
snvm-y += sec_star.o \
|
||||
protocol/ese_data.o \
|
||||
protocol/ese_memory.o \
|
||||
protocol/ese_iso7816_t1.o \
|
||||
hal/ese_i2c.o \
|
||||
hal/ese_spi.o \
|
||||
hal/ese_hal.o \
|
||||
sec_k250a.o
|
||||
else
|
||||
obj-$(CONFIG_STAR_K250A_LEGO) += sec_star.o \
|
||||
protocol/ese_data.o \
|
||||
protocol/ese_memory.o \
|
||||
protocol/ese_iso7816_t1.o \
|
||||
hal/ese_i2c.o \
|
||||
hal/ese_spi.o \
|
||||
hal/ese_hal.o \
|
||||
sec_k250a.o
|
||||
endif
|
105
drivers/nfc/snvm/hal/ese_hal.c
Normal file
105
drivers/nfc/snvm/hal/ese_hal.c
Normal file
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Samsung Electronics. 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program;
|
||||
*
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include "ese_i2c.h"
|
||||
#include "ese_spi.h"
|
||||
#include "ese_hal.h"
|
||||
|
||||
#undef ENABLE_HAL_LOG
|
||||
|
||||
struct ese_hal_s {
|
||||
void *client;
|
||||
int (*send)(void *ctx, unsigned char *buf, unsigned int size);
|
||||
int (*receive)(void *ctx, unsigned char *buf, unsigned int size);
|
||||
};
|
||||
|
||||
int ese_hal_send(void *ctx, unsigned char *buf, unsigned int size)
|
||||
{
|
||||
struct ese_hal_s *hal = (struct ese_hal_s *)ctx;
|
||||
|
||||
if (hal == NULL || hal->client == NULL || hal->send == NULL
|
||||
|| buf == NULL || size == 0) {
|
||||
return -1;
|
||||
}
|
||||
#ifdef ENABLE_HAL_LOG
|
||||
print_hex_dump(KERN_DEBUG, "[star-hal] send : ", DUMP_PREFIX_NONE, 16, 1, buf, size, 0);
|
||||
#endif
|
||||
if (hal->send(hal->client, buf, size) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return (int)size;
|
||||
}
|
||||
|
||||
int ese_hal_receive(void *ctx, unsigned char *buf, unsigned int size)
|
||||
{
|
||||
struct ese_hal_s *hal = (struct ese_hal_s *)ctx;
|
||||
|
||||
if (hal == NULL || hal->client == NULL || hal->receive == NULL
|
||||
|| buf == NULL || size == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (hal->receive(hal->client, buf, size) < 0) {
|
||||
return -1;
|
||||
}
|
||||
#ifdef ENABLE_HAL_LOG
|
||||
print_hex_dump(KERN_DEBUG, "[star-hal] recv : ", DUMP_PREFIX_NONE, 16, 1, buf, size, 0);
|
||||
#endif
|
||||
return (int)size;
|
||||
}
|
||||
|
||||
void *ese_hal_init(enum hal_type_e type, void *client)
|
||||
{
|
||||
struct ese_hal_s *hal = NULL;
|
||||
|
||||
if (client == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hal = kzalloc(sizeof(struct ese_hal_s), GFP_KERNEL);
|
||||
if (hal == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
switch(type) {
|
||||
case ESE_HAL_I2C:
|
||||
hal->client = client;
|
||||
hal->send = ese_i2c_send;
|
||||
hal->receive = ese_i2c_receive;
|
||||
break;
|
||||
case ESE_HAL_SPI:
|
||||
hal->client = client;
|
||||
hal->send = ese_spi_send;
|
||||
hal->receive = ese_spi_receive;
|
||||
break;
|
||||
default:
|
||||
kfree(hal);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return hal;
|
||||
}
|
||||
|
||||
void ese_hal_release(void *ctx)
|
||||
{
|
||||
if (ctx != NULL) {
|
||||
kfree(ctx);
|
||||
}
|
||||
}
|
31
drivers/nfc/snvm/hal/ese_hal.h
Normal file
31
drivers/nfc/snvm/hal/ese_hal.h
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Samsung Electronics. 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 version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef __ESE_HAL__H
|
||||
#define __ESE_HAL__H
|
||||
|
||||
enum hal_type_e {
|
||||
ESE_HAL_I2C = 0,
|
||||
ESE_HAL_SPI,
|
||||
};
|
||||
|
||||
void *ese_hal_init(enum hal_type_e type, void *client);
|
||||
void ese_hal_release(void *ctx);
|
||||
int ese_hal_send(void *ctx, unsigned char *buf, unsigned int size);
|
||||
int ese_hal_receive(void *ctx, unsigned char *buf, unsigned int size);
|
||||
|
||||
#endif
|
54
drivers/nfc/snvm/hal/ese_i2c.c
Normal file
54
drivers/nfc/snvm/hal/ese_i2c.c
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Samsung Electronics. 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program;
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/printk.h>
|
||||
#include <linux/i2c.h>
|
||||
|
||||
#include "ese_i2c.h"
|
||||
|
||||
#define ERR(msg...) pr_err("[star-i2c] : " msg)
|
||||
#define INFO(msg...) pr_info("[star-i2c] : " msg)
|
||||
|
||||
int ese_i2c_send(void *ctx, unsigned char *buf, unsigned int size)
|
||||
{
|
||||
struct i2c_client *client = ctx;
|
||||
int ret = 0;
|
||||
|
||||
ret = i2c_master_send(client, buf, size);
|
||||
if (ret < 0) {
|
||||
ERR("failed to send data %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return (int)size;
|
||||
}
|
||||
|
||||
int ese_i2c_receive(void *ctx, unsigned char *buf, unsigned int size)
|
||||
{
|
||||
struct i2c_client *client = ctx;
|
||||
int ret = 0;
|
||||
|
||||
ret = i2c_master_recv(client, (void *)buf, size);
|
||||
if (ret < 0) {
|
||||
ERR("failed to recv data %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return (int)size;
|
||||
}
|
24
drivers/nfc/snvm/hal/ese_i2c.h
Normal file
24
drivers/nfc/snvm/hal/ese_i2c.h
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Samsung Electronics. 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 version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef __ESE_I2C__H
|
||||
#define __ESE_I2C__H
|
||||
|
||||
int ese_i2c_send(void *ctx, unsigned char *buf, unsigned int size);
|
||||
int ese_i2c_receive(void *ctx, unsigned char *buf, unsigned int size);
|
||||
|
||||
#endif
|
54
drivers/nfc/snvm/hal/ese_spi.c
Normal file
54
drivers/nfc/snvm/hal/ese_spi.c
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Samsung Electronics. 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program;
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/printk.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#include "ese_spi.h"
|
||||
|
||||
#define ERR(msg...) pr_err("[star-spi] : " msg)
|
||||
#define INFO(msg...) pr_info("[star-spi] : " msg)
|
||||
|
||||
int ese_spi_send(void *ctx, unsigned char *buf, unsigned int size)
|
||||
{
|
||||
struct spi_device *spidev = ctx;
|
||||
int ret = 0;
|
||||
|
||||
ret = spi_write(spidev, buf, size);
|
||||
if (ret < 0) {
|
||||
ERR("failed to write data %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return (int)size;
|
||||
}
|
||||
|
||||
int ese_spi_receive(void *ctx, unsigned char *buf, unsigned int size)
|
||||
{
|
||||
struct spi_device *spidev = ctx;
|
||||
int ret = 0;
|
||||
|
||||
ret = spi_read(spidev, (void *)buf, size);
|
||||
if (ret < 0) {
|
||||
ERR("failed to read data %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return (int)size;
|
||||
}
|
24
drivers/nfc/snvm/hal/ese_spi.h
Normal file
24
drivers/nfc/snvm/hal/ese_spi.h
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Samsung Electronics. 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 version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef __ESE_SPI__H
|
||||
#define __ESE_SPI__H
|
||||
|
||||
int ese_spi_send(void *ctx, unsigned char *buf, unsigned int size);
|
||||
int ese_spi_receive(void *ctx, unsigned char *buf, unsigned int size);
|
||||
|
||||
#endif
|
171
drivers/nfc/snvm/protocol/ese_data.c
Normal file
171
drivers/nfc/snvm/protocol/ese_data.c
Normal file
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Samsung Electronics. 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 version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
#include "ese_log.h"
|
||||
#include "ese_memory.h"
|
||||
#include "ese_data.h"
|
||||
|
||||
static int32_t _ese_data_get_from_list(data_list_t *head, uint8_t *data, uint32_t *data_size)
|
||||
{
|
||||
data_list_t *cur_node;
|
||||
uint32_t offset = 0;
|
||||
|
||||
if (head == NULL || data == NULL) {
|
||||
LOG_E("invalid header and data");
|
||||
return -1;
|
||||
}
|
||||
|
||||
cur_node = head->next;
|
||||
while (cur_node != NULL) {
|
||||
memcpy((data + offset), cur_node->packet.data, cur_node->packet.size);
|
||||
offset += cur_node->packet.size;
|
||||
cur_node = cur_node->next;
|
||||
}
|
||||
|
||||
*data_size = offset;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int32_t _ese_data_delete_list(data_list_t *head)
|
||||
{
|
||||
data_list_t *cur, *next;
|
||||
|
||||
if (head == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
cur = head->next;
|
||||
while (cur != NULL) {
|
||||
next = cur->next;
|
||||
ESE_FREE(cur->packet.data);
|
||||
ESE_FREE(cur);
|
||||
cur = NULL;
|
||||
cur = next;
|
||||
}
|
||||
|
||||
head->next = NULL;
|
||||
head->packet.data = NULL;
|
||||
head->packet.size = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ESE_STATUS ese_data_init(data_list_t *head)
|
||||
{
|
||||
head->next = NULL;
|
||||
head->packet.data = NULL;
|
||||
head->packet.size = 0;
|
||||
return ESESTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
ESE_STATUS ese_data_store(data_list_t *head, uint8_t *data, uint32_t data_size, uint8_t copy)
|
||||
{
|
||||
data_list_t *new_node = NULL;
|
||||
data_list_t *cur_node = NULL;
|
||||
|
||||
new_node = ESE_MALLOC(sizeof(data_list_t));
|
||||
if (new_node == NULL) {
|
||||
return ESESTATUS_MEMORY_ALLOCATION_FAIL;
|
||||
}
|
||||
|
||||
new_node->next = NULL;
|
||||
new_node->packet.size = data_size;
|
||||
if (copy) {
|
||||
new_node->packet.data = ESE_MALLOC(data_size);
|
||||
if (new_node->packet.data == NULL) {
|
||||
ESE_FREE(new_node);
|
||||
return ESESTATUS_MEMORY_ALLOCATION_FAIL;
|
||||
}
|
||||
memcpy(new_node->packet.data, data, data_size);
|
||||
} else {
|
||||
new_node->packet.data = data;
|
||||
}
|
||||
|
||||
cur_node = head;
|
||||
while (cur_node->next != NULL) {
|
||||
cur_node = cur_node->next;
|
||||
}
|
||||
|
||||
cur_node->next = new_node;
|
||||
head->packet.size += data_size;
|
||||
return ESESTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
ESE_STATUS ese_data_get(data_list_t *head, uint8_t **data, uint32_t *data_size)
|
||||
{
|
||||
uint32_t total_size = 0;
|
||||
uint8_t* tmp_buf = NULL;
|
||||
|
||||
if (data != NULL && data_size != NULL) {
|
||||
if (head->packet.size == 0) {
|
||||
*data = NULL;
|
||||
*data_size = 0;
|
||||
return ESESTATUS_INVALID_BUFFER;
|
||||
}
|
||||
|
||||
tmp_buf = ESE_MALLOC(head->packet.size);
|
||||
if (tmp_buf == NULL) {
|
||||
LOG_E("failed to allocate memory");
|
||||
_ese_data_delete_list(head);
|
||||
return ESESTATUS_MEMORY_ALLOCATION_FAIL;
|
||||
}
|
||||
|
||||
if (_ese_data_get_from_list(head, tmp_buf, &total_size) != 0) {
|
||||
LOG_E("failed to get data from list");
|
||||
ESE_FREE(tmp_buf);
|
||||
_ese_data_delete_list(head);
|
||||
return ESESTATUS_INVALID_BUFFER;
|
||||
}
|
||||
|
||||
if (total_size != head->packet.size) {
|
||||
LOG_E("mismatch size [%d, %d]", total_size, head->packet.size);
|
||||
ESE_FREE(tmp_buf);
|
||||
_ese_data_delete_list(head);
|
||||
return ESESTATUS_INVALID_BUFFER;
|
||||
}
|
||||
*data = tmp_buf;
|
||||
*data_size = total_size;
|
||||
}
|
||||
|
||||
_ese_data_delete_list(head);
|
||||
return ESESTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
ESE_STATUS ese_data_delete(data_list_t *head)
|
||||
{
|
||||
data_list_t *cur, *next;
|
||||
|
||||
if (head == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
cur = head->next;
|
||||
while (cur != NULL) {
|
||||
next = cur->next;
|
||||
ESE_FREE(cur->packet.data);
|
||||
ESE_FREE(cur);
|
||||
cur = NULL;
|
||||
cur = next;
|
||||
}
|
||||
|
||||
head->next = NULL;
|
||||
head->packet.data = NULL;
|
||||
head->packet.size = 0;
|
||||
return 0;
|
||||
}
|
38
drivers/nfc/snvm/protocol/ese_data.h
Normal file
38
drivers/nfc/snvm/protocol/ese_data.h
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Samsung Electronics. 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 version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _ESE_DATA_H_
|
||||
#define _ESE_DATA_H_
|
||||
|
||||
#include "ese_error.h"
|
||||
|
||||
typedef struct data_packet_s {
|
||||
uint8_t *data;
|
||||
uint32_t size;
|
||||
} data_packet_t;
|
||||
|
||||
typedef struct data_list_s {
|
||||
data_packet_t packet;
|
||||
struct data_list_s *next;
|
||||
} data_list_t;
|
||||
|
||||
ESE_STATUS ese_data_init(data_list_t *head);
|
||||
ESE_STATUS ese_data_store(data_list_t *head, uint8_t *data, uint32_t data_size, uint8_t copy);
|
||||
ESE_STATUS ese_data_get(data_list_t *head, uint8_t **data, uint32_t *data_size);
|
||||
ESE_STATUS ese_data_delete(data_list_t *head);
|
||||
|
||||
#endif
|
49
drivers/nfc/snvm/protocol/ese_error.h
Normal file
49
drivers/nfc/snvm/protocol/ese_error.h
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Samsung Electronics. 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 version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef __ESE_ERROR__H
|
||||
#define __ESE_ERROR__H
|
||||
|
||||
enum ESESTATUS {
|
||||
ESESTATUS_SUCCESS = (0x0000),
|
||||
ESESTATUS_FAILED = (0x0001),
|
||||
ESESTATUS_NOT_INITIALISED = (0x0002),
|
||||
ESESTATUS_ALREADY_INITIALISED = (0x0003),
|
||||
ESESTATUS_INVALID_PARAMETER = (0x0004),
|
||||
ESESTATUS_INVALID_DEVICE = (0x0005),
|
||||
ESESTATUS_INVALID_STATE = (0x0006),
|
||||
ESESTATUS_FEATURE_NOT_SUPPORTED = (0x0007),
|
||||
|
||||
ESESTATUS_INVALID_BUFFER = (0x0030),
|
||||
ESESTATUS_NOT_ENOUGH_MEMORY = (0x0031),
|
||||
ESESTATUS_MEMORY_ALLOCATION_FAIL = (0x0032),
|
||||
|
||||
ESESTATUS_INVALID_CLA = (0x0050),
|
||||
ESESTATUS_INVALID_CPDU_TYPE = (0x0051),
|
||||
ESESTATUS_INVALID_LE_TYPE = (0x0052),
|
||||
ESESTATUS_INVALID_FORMAT = (0x0053),
|
||||
ESESTATUS_INVALID_FRAME = (0x0054),
|
||||
|
||||
ESESTATUS_SEND_FAILED = (0x0060),
|
||||
ESESTATUS_RECEIVE_FAILED = (0x0061),
|
||||
ESESTATUS_RESPONSE_TIMEOUT = (0x0062),
|
||||
ESESTATUS_INVALID_SEND_LENGTH = (0x0063),
|
||||
ESESTATUS_INVALID_RECEIVE_LENGTH = (0x0064),
|
||||
};
|
||||
|
||||
typedef uint32_t ESE_STATUS;
|
||||
#endif
|
749
drivers/nfc/snvm/protocol/ese_iso7816_t1.c
Normal file
749
drivers/nfc/snvm/protocol/ese_iso7816_t1.c
Normal file
@@ -0,0 +1,749 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Samsung Electronics. 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 version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/printk.h>
|
||||
|
||||
#include "../hal/ese_hal.h"
|
||||
#include "ese_log.h"
|
||||
#include "ese_memory.h"
|
||||
#include "ese_error.h"
|
||||
#include "ese_protocol.h"
|
||||
#include "ese_iso7816_t1.h"
|
||||
|
||||
static void iso7816_t1_print_buffer(const char *buf_tag, uint8_t *buffer, uint32_t buffer_size)
|
||||
{
|
||||
print_hex_dump(KERN_DEBUG, buf_tag, DUMP_PREFIX_NONE, 16, 1, buffer, buffer_size, 0);
|
||||
}
|
||||
|
||||
static uint8_t iso7816_t1_compute_lrc(uint8_t *data, uint32_t offset, uint32_t size)
|
||||
{
|
||||
uint32_t lrc = 0, i = 0;
|
||||
|
||||
for (i = offset; i < offset + size; i++) {
|
||||
lrc = lrc ^ data[i];
|
||||
}
|
||||
|
||||
return (uint8_t) lrc;
|
||||
}
|
||||
|
||||
static ESE_STATUS iso7816_t1_check_lrc(uint8_t* data, uint32_t data_size)
|
||||
{
|
||||
uint8_t calc_crc = 0;
|
||||
uint8_t recv_crc = 0;
|
||||
|
||||
recv_crc = data[data_size - 1];
|
||||
calc_crc = iso7816_t1_compute_lrc(data, 0, (data_size - 1));
|
||||
if (recv_crc != calc_crc) {
|
||||
return ESESTATUS_FAILED;
|
||||
}
|
||||
return ESESTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static void iso7816_t1_reset_params(iso7816_t1_t *protocol)
|
||||
{
|
||||
protocol->state = STATE_IDLE;
|
||||
protocol->last_rx.type = FRAME_INVALID;
|
||||
protocol->next_tx.type = FRAME_INVALID;
|
||||
protocol->next_tx.iframe.data = NULL;
|
||||
protocol->last_tx.type = FRAME_INVALID;
|
||||
protocol->last_tx.iframe.data = NULL;
|
||||
protocol->next_tx.iframe.seq_no = 0x01;
|
||||
protocol->last_rx.iframe.seq_no = 0x01;
|
||||
protocol->last_tx.iframe.seq_no = 0x01;
|
||||
protocol->next_tx.sframe.data = NULL;
|
||||
protocol->next_tx.sframe.send_size = 0;
|
||||
protocol->recovery_counter = 0;
|
||||
protocol->timeout_counter = 0;
|
||||
protocol->wtx_counter = 0;
|
||||
protocol->last_frame = FRAME_UNKNOWN;
|
||||
protocol->rnack_counter = 0;
|
||||
}
|
||||
|
||||
static void iso7816_t1_set_iframe(iso7816_t1_t *protocol)
|
||||
{
|
||||
protocol->next_tx.iframe.offset = 0;
|
||||
protocol->next_tx.type = FRAME_IFRAME;
|
||||
protocol->next_tx.iframe.seq_no = protocol->last_tx.iframe.seq_no ^ 1;
|
||||
protocol->state = STATE_SEND_IFRAME;
|
||||
|
||||
if (protocol->next_tx.iframe.total_size > PROTOCOL_SEND_SIZE) {
|
||||
protocol->next_tx.iframe.chain = 1;
|
||||
protocol->next_tx.iframe.send_size = PROTOCOL_SEND_SIZE;
|
||||
protocol->next_tx.iframe.total_size =
|
||||
protocol->next_tx.iframe.total_size - PROTOCOL_SEND_SIZE;
|
||||
} else {
|
||||
protocol->next_tx.iframe.send_size = protocol->next_tx.iframe.total_size;
|
||||
protocol->next_tx.iframe.chain = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void iso7816_t1_next_iframe(iso7816_t1_t *protocol)
|
||||
{
|
||||
protocol->next_tx.type = FRAME_IFRAME;
|
||||
protocol->state = STATE_SEND_IFRAME;
|
||||
|
||||
protocol->next_tx.iframe.seq_no = protocol->last_tx.iframe.seq_no ^ 1;
|
||||
protocol->next_tx.iframe.offset =
|
||||
protocol->last_tx.iframe.offset + PROTOCOL_SEND_SIZE;
|
||||
protocol->next_tx.iframe.data = protocol->last_tx.iframe.data;
|
||||
|
||||
if (protocol->last_tx.iframe.total_size > PROTOCOL_SEND_SIZE) {
|
||||
protocol->next_tx.iframe.chain = 1;
|
||||
protocol->next_tx.iframe.send_size = PROTOCOL_SEND_SIZE;
|
||||
protocol->next_tx.iframe.total_size =
|
||||
protocol->last_tx.iframe.total_size - PROTOCOL_SEND_SIZE;
|
||||
} else {
|
||||
protocol->next_tx.iframe.chain = 0;
|
||||
protocol->next_tx.iframe.send_size = protocol->last_tx.iframe.total_size;
|
||||
}
|
||||
}
|
||||
|
||||
static void iso7816_t1_finish_recovery(iso7816_t1_t *protocol)
|
||||
{
|
||||
/*
|
||||
* ToDo : need to reset interface
|
||||
*/
|
||||
LOG_E("finish recovery, set protocol state to IDLE");
|
||||
protocol->state = STATE_IDLE;
|
||||
protocol->recovery_counter = PROTOCOL_ZERO;
|
||||
}
|
||||
|
||||
static ESE_STATUS iso7816_t1_send_sframe(iso7816_t1_t *protocol)
|
||||
{
|
||||
uint32_t frame_size = (PROTOCOL_HEADER_SIZE + PROTOCOL_LRC_SIZE);
|
||||
uint8_t buf[PROTOCOL_HEADER_SIZE + PROTOCOL_LRC_SIZE + SFRAME_MAX_INF_SIZE];
|
||||
uint8_t pcb_byte = 0;
|
||||
|
||||
protocol->last_frame = FRAME_SFRAME;
|
||||
if ((protocol->next_tx.sframe.type & 0x20 )== 0) {
|
||||
buf[2] = 0x00;
|
||||
|
||||
pcb_byte = PROTOCOL_S_BLOCK_REQ | protocol->next_tx.sframe.type;
|
||||
|
||||
if (protocol->next_tx.sframe.data && protocol->next_tx.sframe.send_size > 0) {
|
||||
if (protocol->next_tx.sframe.send_size > 4)
|
||||
protocol->next_tx.sframe.send_size = 4;
|
||||
buf[2] = protocol->next_tx.sframe.send_size;
|
||||
memcpy(buf + PROTOCOL_HEADER_SIZE,
|
||||
protocol->next_tx.sframe.data, protocol->next_tx.sframe.send_size);
|
||||
frame_size += protocol->next_tx.sframe.send_size;
|
||||
protocol->next_tx.sframe.send_size = 0;
|
||||
}
|
||||
} else if ((protocol->next_tx.sframe.type & 0x20) == 0x20) {
|
||||
buf[2] = 0x00;
|
||||
|
||||
pcb_byte = PROTOCOL_S_BLOCK_RSP | (protocol->next_tx.sframe.type & 0x0F);
|
||||
|
||||
if (protocol->next_tx.sframe.data && protocol->next_tx.sframe.send_size > 0) {
|
||||
if (protocol->next_tx.sframe.send_size > 4)
|
||||
protocol->next_tx.sframe.send_size = 4;
|
||||
buf[2] = protocol->next_tx.sframe.send_size;
|
||||
memcpy(buf + PROTOCOL_HEADER_SIZE,
|
||||
protocol->next_tx.sframe.data, protocol->next_tx.sframe.send_size);
|
||||
frame_size += protocol->next_tx.sframe.send_size;
|
||||
protocol->next_tx.sframe.send_size = 0;
|
||||
}
|
||||
} else {
|
||||
LOG_E("Invalid SFrame");
|
||||
}
|
||||
|
||||
buf[0] = protocol->send_address;
|
||||
buf[1] = pcb_byte;
|
||||
buf[frame_size - 1] = iso7816_t1_compute_lrc(buf, 0, (frame_size - 1));
|
||||
|
||||
iso7816_t1_print_buffer("[star-protocol] S_SFRAME: ", buf, frame_size);
|
||||
if ((uint32_t)ese_hal_send(protocol->hal, buf, frame_size) != frame_size) {
|
||||
return ESESTATUS_SEND_FAILED;
|
||||
}
|
||||
return ESESTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static ESE_STATUS iso7816_t1_send_rframe(iso7816_t1_t *protocol)
|
||||
{
|
||||
uint8_t buf[RFRAME_PROTOCOL_SIZE] = {0x00, 0x80, 0x00, 0x00};
|
||||
|
||||
if (protocol->next_tx.rframe.error != RFRAME_ERROR_NO) {
|
||||
buf[1] |= protocol->next_tx.rframe.error;
|
||||
} else {
|
||||
buf[1] = 0x80;
|
||||
protocol->last_frame = FRAME_RFRAME;
|
||||
}
|
||||
|
||||
buf[0] = protocol->send_address;
|
||||
buf[1] |= ((protocol->last_rx.iframe.seq_no ^ 1) << 4);
|
||||
buf[2] = 0x00;
|
||||
buf[3] = iso7816_t1_compute_lrc(buf, 0x00, RFRAME_PROTOCOL_SIZE - 1);
|
||||
|
||||
iso7816_t1_print_buffer("[star-protocol] S_RFRAME: ", buf, RFRAME_PROTOCOL_SIZE);
|
||||
if ((uint32_t)ese_hal_send(protocol->hal, buf, RFRAME_PROTOCOL_SIZE) != RFRAME_PROTOCOL_SIZE) {
|
||||
return ESESTATUS_SEND_FAILED;
|
||||
}
|
||||
|
||||
return ESESTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static ESE_STATUS iso7816_t1_send_iframe(iso7816_t1_t *protocol)
|
||||
{
|
||||
uint32_t frame_size = 0;
|
||||
uint8_t pcb_byte = 0;
|
||||
|
||||
if (protocol->proc_buf == NULL) {
|
||||
LOG_E("Process Buffer is NULL, INVALID");
|
||||
return ESESTATUS_NOT_ENOUGH_MEMORY;
|
||||
}
|
||||
|
||||
if (protocol->next_tx.iframe.send_size == 0) {
|
||||
LOG_E("Iframe Len is 0, INVALID");
|
||||
return ESESTATUS_INVALID_SEND_LENGTH;
|
||||
}
|
||||
|
||||
if (protocol->next_tx.iframe.data == NULL) {
|
||||
LOG_E("Iframe Buffer is NULL, INVALID");
|
||||
return ESESTATUS_NOT_ENOUGH_MEMORY;
|
||||
}
|
||||
|
||||
protocol->last_frame = FRAME_IFRAME;
|
||||
frame_size = (protocol->next_tx.iframe.send_size + PROTOCOL_HEADER_SIZE + PROTOCOL_LRC_SIZE);
|
||||
|
||||
(protocol->proc_buf)[0] = protocol->send_address;
|
||||
if (protocol->next_tx.iframe.chain) {
|
||||
pcb_byte |= PROTOCOL_CHAINING;
|
||||
}
|
||||
|
||||
pcb_byte |= (protocol->next_tx.iframe.seq_no << 6);
|
||||
(protocol->proc_buf)[1] = pcb_byte;
|
||||
(protocol->proc_buf)[2] = protocol->next_tx.iframe.send_size;
|
||||
memcpy(protocol->proc_buf + 3,
|
||||
protocol->next_tx.iframe.data + protocol->next_tx.iframe.offset,
|
||||
protocol->next_tx.iframe.send_size);
|
||||
(protocol->proc_buf)[frame_size - 1] = iso7816_t1_compute_lrc(protocol->proc_buf, 0, (frame_size - 1));
|
||||
|
||||
iso7816_t1_print_buffer("[star-protocol] S_IFRAME: ", protocol->proc_buf, PROTOCOL_HEADER_SIZE);
|
||||
if ((uint32_t)ese_hal_send(protocol->hal, protocol->proc_buf, frame_size) != frame_size) {
|
||||
return ESESTATUS_SEND_FAILED;
|
||||
}
|
||||
|
||||
return ESESTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static ESE_STATUS iso7816_t1_decode_frame(iso7816_t1_t *protocol, uint8_t* data, uint32_t data_size)
|
||||
{
|
||||
ESE_STATUS status = ESESTATUS_FAILED;
|
||||
uint8_t pcb_byte;
|
||||
int32_t frame_type;
|
||||
uint32_t wait_time = 0;
|
||||
|
||||
pcb_byte = data[PROTOCOL_PCB_OFFSET];
|
||||
|
||||
if (ESE_BIT(pcb_byte, 7) == 0x00) {
|
||||
iso7816_t1_print_buffer("[star-protocol] R_IFRAME: ", data, PROTOCOL_HEADER_SIZE);
|
||||
protocol->wtx_counter = 0;
|
||||
protocol->last_rx.type = FRAME_IFRAME;
|
||||
if (protocol->last_rx.iframe.seq_no != ESE_BIT(pcb_byte, 6)) {
|
||||
protocol->recovery_counter = 0;
|
||||
protocol->last_rx.iframe.seq_no = 0x00;
|
||||
protocol->last_rx.iframe.seq_no |= ESE_BIT(pcb_byte, 6);
|
||||
|
||||
if (ESE_BIT(pcb_byte, 5)) {
|
||||
protocol->last_rx.iframe.chain = 1;
|
||||
protocol->next_tx.type = FRAME_RFRAME;
|
||||
protocol->next_tx.rframe.error = RFRAME_ERROR_NO;
|
||||
protocol->state = STATE_SEND_RFRAME;
|
||||
} else {
|
||||
protocol->last_rx.iframe.chain = 0;
|
||||
protocol->state = STATE_IDLE;
|
||||
}
|
||||
|
||||
status = ese_data_store(&(protocol->recv_data), &data[3], data_size - 4, 1);
|
||||
if (status != ESESTATUS_SUCCESS) {
|
||||
return status;
|
||||
}
|
||||
} else {
|
||||
if (protocol->recovery_counter < PROTOCOL_FRAME_RETRY_COUNT) {
|
||||
protocol->next_tx.type = FRAME_RFRAME;
|
||||
protocol->next_tx.rframe.error = RFRAME_ERROR_OTHER;
|
||||
protocol->state = STATE_SEND_RFRAME;
|
||||
protocol->recovery_counter++;
|
||||
} else {
|
||||
iso7816_t1_finish_recovery(protocol);
|
||||
}
|
||||
}
|
||||
} else if ((ESE_BIT(pcb_byte, 7) == 0x01) && (ESE_BIT(pcb_byte, 6) == 0x00)) {
|
||||
iso7816_t1_print_buffer("[star-protocol] R_RFRAME: ", data, data_size);
|
||||
protocol->wtx_counter = 0;
|
||||
protocol->last_rx.type = FRAME_RFRAME;
|
||||
protocol->last_rx.rframe.seq_no = ESE_BIT(pcb_byte, 4);
|
||||
|
||||
if ((ESE_BIT(pcb_byte, 0) == 0x00) && (ESE_BIT(pcb_byte, 1) == 0x00)) {
|
||||
protocol->last_rx.rframe.error = RFRAME_ERROR_NO;
|
||||
protocol->recovery_counter = 0;
|
||||
if (protocol->last_rx.rframe.seq_no !=
|
||||
protocol->last_tx.iframe.seq_no) {
|
||||
protocol->state = STATE_SEND_IFRAME;
|
||||
iso7816_t1_next_iframe(protocol);
|
||||
} else {
|
||||
/*
|
||||
* ToDo : error handling.
|
||||
*/
|
||||
}
|
||||
} else if (((ESE_BIT(pcb_byte, 0) == 0x01) && (ESE_BIT(pcb_byte, 1) == 0x00)) ||
|
||||
((ESE_BIT(pcb_byte, 0) == 0x00) && (ESE_BIT(pcb_byte, 1) == 0x01))) {
|
||||
if ((ESE_BIT(pcb_byte, 0) == 0x00) && (ESE_BIT(pcb_byte, 1) == 0x01))
|
||||
protocol->last_rx.rframe.error = RFRAME_ERROR_OTHER;
|
||||
else
|
||||
protocol->last_rx.rframe.error = RFRAME_ERROR_PARITY;
|
||||
if (protocol->recovery_counter < PROTOCOL_FRAME_RETRY_COUNT) {
|
||||
if (protocol->last_tx.type == FRAME_IFRAME) {
|
||||
memcpy((uint8_t *)&protocol->next_tx,
|
||||
(uint8_t *)&protocol->last_tx, sizeof(frame_info_t));
|
||||
protocol->state = STATE_SEND_IFRAME;
|
||||
protocol->next_tx.type = FRAME_IFRAME;
|
||||
} else if (protocol->last_tx.type == FRAME_RFRAME) {
|
||||
if ((protocol->last_rx.rframe.seq_no ==
|
||||
protocol->last_tx.iframe.seq_no) &&
|
||||
(protocol->last_frame == FRAME_IFRAME)) {
|
||||
/*
|
||||
* Usecase to reach the below case:
|
||||
* I-frame sent first, followed by R-NACK and we receive a R-NACK with
|
||||
* last sent I-frame sequence number
|
||||
*/
|
||||
memcpy((uint8_t *)&protocol->next_tx,
|
||||
(uint8_t *)&protocol->last_tx, sizeof(frame_info_t));
|
||||
protocol->state = STATE_SEND_IFRAME;
|
||||
protocol->next_tx.type = FRAME_IFRAME;
|
||||
} else if ((protocol->last_rx.rframe.seq_no !=
|
||||
protocol->last_tx.iframe.seq_no) &&
|
||||
(protocol->last_frame == FRAME_RFRAME)) {
|
||||
/*
|
||||
* Usecase to reach the below case:
|
||||
* R-frame sent first, followed by R-NACK and we receive a R-NACK with
|
||||
* next expected I-frame sequence number
|
||||
*/
|
||||
protocol->next_tx.type = FRAME_RFRAME;
|
||||
protocol->next_tx.rframe.error = RFRAME_ERROR_NO;
|
||||
protocol->state = STATE_SEND_RFRAME;
|
||||
} else {
|
||||
/*
|
||||
* Usecase to reach the below case:
|
||||
* I-frame sent first, followed by R-NACK and we receive a R-NACK with
|
||||
* next expected I-frame sequence number + all the other unexpected
|
||||
* scenarios
|
||||
*/
|
||||
protocol->next_tx.type = FRAME_RFRAME;
|
||||
protocol->next_tx.rframe.error = RFRAME_ERROR_OTHER;
|
||||
protocol->state = STATE_SEND_RFRAME;
|
||||
}
|
||||
} else if (protocol->last_tx.type == FRAME_SFRAME) {
|
||||
memcpy((uint8_t *)&protocol->next_tx,
|
||||
(uint8_t *)&protocol->last_tx, sizeof(frame_info_t));
|
||||
}
|
||||
protocol->recovery_counter++;
|
||||
} else {
|
||||
iso7816_t1_finish_recovery(protocol);
|
||||
}
|
||||
} else if ((ESE_BIT(pcb_byte, 0) == 0x01) && (ESE_BIT(pcb_byte, 1) == 0x01)) {
|
||||
if (protocol->recovery_counter < PROTOCOL_FRAME_RETRY_COUNT) {
|
||||
protocol->last_rx.rframe.error = RFRAME_ERROR_SOF_MISSED;
|
||||
memcpy((uint8_t *)&protocol->next_tx,
|
||||
(uint8_t *)&protocol->last_tx, sizeof(frame_info_t));
|
||||
protocol->recovery_counter++;
|
||||
} else {
|
||||
iso7816_t1_finish_recovery(protocol);
|
||||
}
|
||||
} else {
|
||||
if (protocol->recovery_counter < PROTOCOL_FRAME_RETRY_COUNT) {
|
||||
protocol->last_rx.rframe.error = RFRAME_ERROR_UNDEFINED;
|
||||
memcpy((uint8_t *)&protocol->next_tx,
|
||||
(uint8_t *)&protocol->last_tx, sizeof(frame_info_t));
|
||||
protocol->recovery_counter++;
|
||||
} else {
|
||||
iso7816_t1_finish_recovery(protocol);
|
||||
}
|
||||
}
|
||||
} else if ((ESE_BIT(pcb_byte, 7) == 0x01) && (ESE_BIT(pcb_byte, 6) == 0x01)) {
|
||||
iso7816_t1_print_buffer("[star-protocol] R_SFRAME: ", data, data_size);
|
||||
frame_type = (int32_t)(pcb_byte & 0x3F);
|
||||
protocol->last_rx.type = FRAME_SFRAME;
|
||||
if (frame_type != SFRAME_WTX_REQ) {
|
||||
protocol->wtx_counter = 0;
|
||||
}
|
||||
switch (frame_type) {
|
||||
case SFRAME_RESYNCH_REQ:
|
||||
iso7816_t1_reset_params(protocol);
|
||||
protocol->last_rx.sframe.type = SFRAME_RESYNCH_REQ;
|
||||
protocol->next_tx.type = FRAME_SFRAME;
|
||||
protocol->next_tx.sframe.type = SFRAME_RESYNCH_RSP;
|
||||
protocol->state = STATE_SEND_SFRAME;
|
||||
protocol->next_tx.sframe.data = NULL;
|
||||
protocol->next_tx.sframe.send_size = 0;
|
||||
break;
|
||||
case SFRAME_RESYNCH_RSP:
|
||||
protocol->last_rx.sframe.type = SFRAME_RESYNCH_RSP;
|
||||
protocol->next_tx.type = FRAME_UNKNOWN;
|
||||
protocol->state = STATE_IDLE;
|
||||
break;
|
||||
case SFRAME_ABORT_REQ:
|
||||
protocol->last_rx.sframe.type = SFRAME_ABORT_REQ;
|
||||
break;
|
||||
case SFRAME_ABORT_RES:
|
||||
protocol->last_rx.sframe.type = SFRAME_ABORT_RES;
|
||||
protocol->next_tx.type = FRAME_UNKNOWN;
|
||||
protocol->state = STATE_IDLE;
|
||||
break;
|
||||
case SFRAME_WTX_REQ:
|
||||
if (protocol->last_tx.type == FRAME_SFRAME &&
|
||||
protocol->last_tx.sframe.type != SFRAME_WTX_RSP) {
|
||||
if (protocol->recovery_counter < PROTOCOL_FRAME_RETRY_COUNT) {
|
||||
memcpy((uint8_t *)&protocol->next_tx,
|
||||
(uint8_t *)&protocol->last_tx, sizeof(frame_info_t));
|
||||
protocol->recovery_counter++;
|
||||
} else {
|
||||
iso7816_t1_finish_recovery(protocol);
|
||||
}
|
||||
} else {
|
||||
protocol->wtx_counter++;
|
||||
if (data_size == 8) {
|
||||
wait_time = data[3] << 24 | data[4] << 16 | data[5] << 8 | data[6];
|
||||
wait_time = wait_time * 1000;
|
||||
}
|
||||
LOG_I("wtx_counter : %u, wait time : %u us", protocol->wtx_counter, wait_time);
|
||||
PROTOCOL_UDELAY(wait_time);
|
||||
protocol->last_rx.sframe.type = SFRAME_WTX_REQ;
|
||||
protocol->next_tx.type = FRAME_SFRAME;
|
||||
protocol->next_tx.sframe.type = SFRAME_WTX_RSP;
|
||||
protocol->next_tx.sframe.data = NULL;
|
||||
protocol->next_tx.sframe.send_size = 0;
|
||||
protocol->state = STATE_SEND_SFRAME;
|
||||
}
|
||||
break;
|
||||
case SFRAME_WTX_RSP:
|
||||
protocol->last_rx.sframe.type = SFRAME_WTX_RSP;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
LOG_E("Wrong-Frame Received");
|
||||
return ESESTATUS_INVALID_FORMAT;
|
||||
}
|
||||
|
||||
return ESESTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static ESE_STATUS iso7816_t1_process(iso7816_t1_t *protocol)
|
||||
{
|
||||
uint32_t data_size = 0, rec_size = 0, ret_size = 0;
|
||||
ESE_STATUS status = ESESTATUS_FAILED;
|
||||
int32_t i = 0;
|
||||
|
||||
PROTOCOL_UDELAY(100);
|
||||
for (i = 0; i < PROTOCOL_ADDRESS_POLLING_COUNT; i++) {
|
||||
if (ese_hal_receive(protocol->hal, protocol->proc_buf, 1) != 1) {
|
||||
status = ESESTATUS_INVALID_RECEIVE_LENGTH;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if ((protocol->proc_buf)[data_size] == protocol->receive_address) {
|
||||
break;
|
||||
}
|
||||
|
||||
if ((protocol->proc_buf)[data_size] != 0x0) {
|
||||
LOG_E("invalid address, expected : %d, received : %d",
|
||||
protocol->receive_address, (protocol->proc_buf)[data_size]);
|
||||
status = ESESTATUS_INVALID_FRAME;
|
||||
goto error;
|
||||
}
|
||||
PROTOCOL_UDELAY(1500);
|
||||
}
|
||||
|
||||
if (i == PROTOCOL_ADDRESS_POLLING_COUNT) {
|
||||
LOG_E("finish polling address, limit : %d", PROTOCOL_ADDRESS_POLLING_COUNT);
|
||||
status = ESESTATUS_INVALID_FRAME;
|
||||
goto error;
|
||||
}
|
||||
|
||||
data_size ++;
|
||||
|
||||
ret_size = (uint32_t)ese_hal_receive(protocol->hal, protocol->proc_buf + data_size, 2);
|
||||
if (ret_size != 2) {
|
||||
LOG_E("mismatch receive size %u, %u", 2, ret_size);
|
||||
status = ESESTATUS_INVALID_RECEIVE_LENGTH;
|
||||
goto error;
|
||||
}
|
||||
data_size += ret_size;
|
||||
|
||||
rec_size = (protocol->proc_buf)[2] + 1;
|
||||
ret_size = (uint32_t)ese_hal_receive(protocol->hal, protocol->proc_buf + data_size, rec_size);
|
||||
if (ret_size != rec_size) {
|
||||
LOG_E("mismatch receive size %u, %u", rec_size, ret_size);
|
||||
status = ESESTATUS_INVALID_RECEIVE_LENGTH;
|
||||
goto error;
|
||||
}
|
||||
data_size += ret_size;
|
||||
|
||||
error:
|
||||
if (data_size > 0) {
|
||||
protocol->timeout_counter = PROTOCOL_ZERO;
|
||||
status = iso7816_t1_check_lrc(protocol->proc_buf, data_size);
|
||||
if (status == ESESTATUS_SUCCESS) {
|
||||
protocol->rnack_counter = PROTOCOL_ZERO;
|
||||
iso7816_t1_decode_frame(protocol, protocol->proc_buf, data_size);
|
||||
} else {
|
||||
LOG_E("LRC Check failed");
|
||||
if (protocol->rnack_counter < PROTOCOL_MAX_RNACK_RETRY_LIMIT) {
|
||||
protocol->last_rx.type = FRAME_INVALID;
|
||||
protocol->next_tx.type = FRAME_RFRAME;
|
||||
protocol->next_tx.rframe.error = RFRAME_ERROR_PARITY;
|
||||
protocol->next_tx.rframe.seq_no =
|
||||
(!protocol->last_rx.iframe.seq_no) << 4;
|
||||
protocol->state = STATE_SEND_RFRAME;
|
||||
protocol->rnack_counter++;
|
||||
} else {
|
||||
protocol->rnack_counter = PROTOCOL_ZERO;
|
||||
protocol->state = STATE_IDLE;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LOG_E("ese_hal_receive failed");
|
||||
if ((protocol->last_tx.type == FRAME_SFRAME)
|
||||
&& ((protocol->last_tx.sframe.type == SFRAME_WTX_RSP) ||
|
||||
(protocol->last_tx.sframe.type == SFRAME_RESYNCH_RSP))) {
|
||||
if (protocol->rnack_counter < PROTOCOL_MAX_RNACK_RETRY_LIMIT) {
|
||||
protocol->last_rx.type = FRAME_INVALID;
|
||||
protocol->next_tx.type = FRAME_RFRAME;
|
||||
protocol->next_tx.rframe.error = RFRAME_ERROR_OTHER;
|
||||
protocol->next_tx.rframe.seq_no =
|
||||
(!protocol->last_rx.iframe.seq_no) << 4;
|
||||
protocol->state = STATE_SEND_RFRAME;
|
||||
protocol->rnack_counter++;
|
||||
} else {
|
||||
protocol->rnack_counter = PROTOCOL_ZERO;
|
||||
protocol->state = STATE_IDLE;
|
||||
protocol->timeout_counter = PROTOCOL_ZERO;
|
||||
}
|
||||
} else {
|
||||
PROTOCOL_UDELAY(50 * 1000);
|
||||
if (status == ESESTATUS_INVALID_FRAME) {
|
||||
if (protocol->rnack_counter < PROTOCOL_MAX_RNACK_RETRY_LIMIT) {
|
||||
protocol->last_rx.type = FRAME_INVALID;
|
||||
protocol->next_tx.type = FRAME_RFRAME;
|
||||
protocol->next_tx.rframe.error = RFRAME_ERROR_OTHER;
|
||||
protocol->next_tx.rframe.seq_no =
|
||||
(!protocol->last_rx.iframe.seq_no) << 4;
|
||||
protocol->state = STATE_SEND_RFRAME;
|
||||
protocol->rnack_counter++;
|
||||
} else {
|
||||
protocol->rnack_counter = PROTOCOL_ZERO;
|
||||
protocol->state = STATE_IDLE;
|
||||
protocol->timeout_counter = PROTOCOL_ZERO;
|
||||
}
|
||||
} else {
|
||||
if (protocol->timeout_counter < PROTOCOL_TIMEOUT_RETRY_COUNT) {
|
||||
LOG_E("re-transmitting the previous frame");
|
||||
protocol->timeout_counter++;
|
||||
memcpy((uint8_t *)&protocol->next_tx,
|
||||
(uint8_t *)&protocol->last_tx, sizeof(frame_info_t));
|
||||
} else {
|
||||
LOG_E("finish timeout recovery, set protocol state to IDLE");
|
||||
protocol->state = STATE_IDLE;
|
||||
protocol->timeout_counter = PROTOCOL_ZERO;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
ESE_STATUS iso7816_t1_send(void *ctx, ese_data_t *data)
|
||||
{
|
||||
iso7816_t1_t *protocol = (iso7816_t1_t *)ctx;
|
||||
ESE_STATUS status = ESESTATUS_FAILED;
|
||||
|
||||
if (protocol->state == STATE_IDLE && data != NULL) {
|
||||
protocol->next_tx.iframe.data = data->data;
|
||||
protocol->next_tx.iframe.total_size = data->size;
|
||||
iso7816_t1_set_iframe(protocol);
|
||||
}
|
||||
|
||||
memcpy((uint8_t *)&protocol->last_tx, (uint8_t *)&protocol->next_tx, sizeof(frame_info_t));
|
||||
|
||||
switch (protocol->state) {
|
||||
case STATE_SEND_IFRAME:
|
||||
status = iso7816_t1_send_iframe(protocol);
|
||||
break;
|
||||
case STATE_SEND_RFRAME:
|
||||
status = iso7816_t1_send_rframe(protocol);
|
||||
break;
|
||||
case STATE_SEND_SFRAME:
|
||||
status = iso7816_t1_send_sframe(protocol);
|
||||
break;
|
||||
default:
|
||||
LOG_E("invalid state");
|
||||
protocol->state = STATE_IDLE;
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
ESE_STATUS iso7816_t1_receive(void *ctx, ese_data_t *data)
|
||||
{
|
||||
iso7816_t1_t *protocol = (iso7816_t1_t *)ctx;
|
||||
ESE_STATUS status = ESESTATUS_FAILED;
|
||||
ESE_STATUS wstatus = ESESTATUS_FAILED;
|
||||
|
||||
do {
|
||||
status = iso7816_t1_process(protocol);
|
||||
if (status == ESESTATUS_INVALID_DEVICE) {
|
||||
return status;
|
||||
}
|
||||
|
||||
if (protocol->state != STATE_IDLE) {
|
||||
status = iso7816_t1_send(protocol, NULL);
|
||||
if (status != ESESTATUS_SUCCESS) {
|
||||
LOG_E("send failed, going to recovery!");
|
||||
protocol->state = STATE_IDLE;
|
||||
}
|
||||
}
|
||||
} while (protocol->state != STATE_IDLE);
|
||||
wstatus = ese_data_get(&(protocol->recv_data), &data->data, &data->size);
|
||||
if (wstatus != ESESTATUS_SUCCESS) {
|
||||
status = wstatus;
|
||||
}
|
||||
|
||||
protocol->cur_buf = data->data;
|
||||
protocol->cur_size = data->size;
|
||||
return status;
|
||||
}
|
||||
|
||||
ESE_STATUS iso7816_t1_resync_request(void *ctx)
|
||||
{
|
||||
iso7816_t1_t *protocol = (iso7816_t1_t *)ctx;
|
||||
|
||||
protocol->state = STATE_SEND_SFRAME;
|
||||
protocol->next_tx.type = FRAME_SFRAME;
|
||||
protocol->next_tx.sframe.type = SFRAME_RESYNCH_REQ;
|
||||
protocol->next_tx.sframe.data = NULL;
|
||||
protocol->next_tx.sframe.send_size = 0;
|
||||
return iso7816_t1_send(protocol, NULL);
|
||||
}
|
||||
|
||||
ESE_STATUS iso7816_t1_resync_response(void *ctx)
|
||||
{
|
||||
iso7816_t1_t *protocol = (iso7816_t1_t *)ctx;
|
||||
ESE_STATUS status = ESESTATUS_FAILED;
|
||||
|
||||
status = iso7816_t1_process(protocol);
|
||||
protocol->state = STATE_IDLE;
|
||||
if (protocol->last_rx.type != FRAME_SFRAME &&
|
||||
protocol->last_rx.sframe.type != SFRAME_RESYNCH_RSP) {
|
||||
return ESESTATUS_INVALID_FRAME;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
ESE_STATUS iso7816_t1_wtx_request(void *ctx, uint32_t time)
|
||||
{
|
||||
iso7816_t1_t *protocol = (iso7816_t1_t *)ctx;
|
||||
|
||||
protocol->state = STATE_SEND_SFRAME;
|
||||
protocol->next_tx.type = FRAME_SFRAME;
|
||||
protocol->next_tx.sframe.type = SFRAME_WTX_REQ;
|
||||
protocol->next_tx.sframe.data = (uint8_t *)&time;
|
||||
protocol->next_tx.sframe.send_size = 4;
|
||||
return iso7816_t1_send(protocol, NULL);
|
||||
}
|
||||
|
||||
ESE_STATUS iso7816_t1_wtx_response(void *ctx)
|
||||
{
|
||||
iso7816_t1_t *protocol = (iso7816_t1_t *)ctx;
|
||||
ESE_STATUS status = ESESTATUS_FAILED;
|
||||
|
||||
status = iso7816_t1_process(protocol);
|
||||
protocol->state = STATE_IDLE;
|
||||
if (protocol->last_rx.type != FRAME_SFRAME &&
|
||||
protocol->last_rx.sframe.type != SFRAME_WTX_RSP) {
|
||||
return ESESTATUS_INVALID_FRAME;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
ESE_STATUS iso7816_t1_get_data(void *ctx, ese_data_t *data, uint8_t *le)
|
||||
{
|
||||
iso7816_t1_t *protocol = (iso7816_t1_t *)ctx;
|
||||
unsigned int data_size = 0;
|
||||
|
||||
if (data != NULL) {
|
||||
data_size = protocol->cur_buf[APDU_P3_OFFSET];
|
||||
data->data = protocol->cur_buf + APDU_HEADER_SIZE;
|
||||
data->size = data_size;
|
||||
}
|
||||
|
||||
if (le != NULL) {
|
||||
if (data_size == 0) {
|
||||
*le = protocol->cur_buf[APDU_P3_OFFSET];
|
||||
} else {
|
||||
*le = protocol->cur_buf[APDU_HEADER_SIZE + data_size];
|
||||
}
|
||||
}
|
||||
|
||||
return ESESTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
void *iso7816_t1_init(uint8_t send_address, uint8_t receive_address, void *hal)
|
||||
{
|
||||
iso7816_t1_t *protocol = NULL;
|
||||
|
||||
protocol = ESE_MALLOC(sizeof(iso7816_t1_t));
|
||||
if (protocol == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
iso7816_t1_reset_params(protocol);
|
||||
ese_data_init(&(protocol->recv_data));
|
||||
/*
|
||||
* T1 Header + Information Field + LRC (3 + 256 + 1)
|
||||
*/
|
||||
protocol->proc_buf = ESE_MALLOC(260);
|
||||
if (protocol->proc_buf == NULL) {
|
||||
ESE_FREE(protocol);
|
||||
return NULL;
|
||||
}
|
||||
protocol->send_address = send_address;
|
||||
protocol->receive_address = receive_address;
|
||||
protocol->hal = hal;
|
||||
|
||||
return (void *)protocol;
|
||||
}
|
||||
|
||||
void iso7816_t1_deinit(void *ctx)
|
||||
{
|
||||
iso7816_t1_t *protocol = (iso7816_t1_t *)ctx;
|
||||
|
||||
ESE_FREE(protocol->proc_buf);
|
||||
ESE_FREE(protocol);
|
||||
}
|
||||
|
||||
void iso7816_t1_reset(void *ctx)
|
||||
{
|
||||
iso7816_t1_t *protocol = (iso7816_t1_t *)ctx;
|
||||
|
||||
iso7816_t1_reset_params(protocol);
|
||||
}
|
148
drivers/nfc/snvm/protocol/ese_iso7816_t1.h
Normal file
148
drivers/nfc/snvm/protocol/ese_iso7816_t1.h
Normal file
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Samsung Electronics. 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 version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/********************* Definitions and structures *****************************/
|
||||
|
||||
#ifndef __ESE_PROTOCOL_T1__H
|
||||
#define __ESE_PROTOCOL_T1__H
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "ese_protocol.h"
|
||||
#include "ese_data.h"
|
||||
|
||||
typedef enum sframe_type_e {
|
||||
SFRAME_RESYNCH_REQ = 0x00,
|
||||
SFRAME_RESYNCH_RSP = 0x20,
|
||||
SFRAME_IFSC_REQ = 0x01,
|
||||
SFRAME_IFSC_RES = 0x21,
|
||||
SFRAME_ABORT_REQ = 0x02,
|
||||
SFRAME_ABORT_RES = 0x22,
|
||||
SFRAME_WTX_REQ = 0x03,
|
||||
SFRAME_WTX_RSP = 0x23,
|
||||
SFRAME_INVALID_REQ_RES
|
||||
} sframe_type_t;
|
||||
|
||||
typedef enum rframe_type_e {
|
||||
RFRAME_ACK = 0x01,
|
||||
RFRAME_NACK = 0x02
|
||||
} rframe_type_t;
|
||||
|
||||
typedef enum rframe_error_e {
|
||||
RFRAME_ERROR_NO,
|
||||
RFRAME_ERROR_PARITY,
|
||||
RFRAME_ERROR_OTHER,
|
||||
RFRAME_ERROR_SOF_MISSED,
|
||||
RFRAME_ERROR_UNDEFINED
|
||||
} rframe_error_t;
|
||||
|
||||
typedef enum frame_type_e {
|
||||
FRAME_IFRAME,
|
||||
FRAME_SFRAME,
|
||||
FRAME_RFRAME,
|
||||
FRAME_INVALID,
|
||||
FRAME_UNKNOWN
|
||||
} frame_type_t;
|
||||
|
||||
typedef struct iframe_info_s {
|
||||
uint8_t *data;
|
||||
uint32_t offset;
|
||||
uint32_t total_size;
|
||||
uint32_t send_size;
|
||||
uint8_t chain;
|
||||
uint8_t seq_no;
|
||||
} iframe_info_t;
|
||||
|
||||
typedef struct sframe_info_s {
|
||||
uint8_t *data;
|
||||
uint32_t send_size;
|
||||
sframe_type_t type;
|
||||
} sframe_info_t;
|
||||
|
||||
typedef struct rframe_info_s {
|
||||
uint8_t seq_no;
|
||||
rframe_error_t error;
|
||||
} rframe_info_t;
|
||||
|
||||
typedef struct frame_info_s {
|
||||
iframe_info_t iframe;
|
||||
rframe_info_t rframe;
|
||||
sframe_info_t sframe;
|
||||
frame_type_t type;
|
||||
} frame_info_t;
|
||||
|
||||
typedef enum state_e {
|
||||
STATE_IDLE,
|
||||
STATE_SEND_IFRAME,
|
||||
STATE_SEND_RFRAME,
|
||||
STATE_SEND_SFRAME,
|
||||
} state_t;
|
||||
|
||||
typedef struct iso7816_t1_s {
|
||||
frame_info_t last_tx;
|
||||
frame_info_t next_tx;
|
||||
frame_info_t last_rx;
|
||||
data_list_t recv_data;
|
||||
uint8_t *proc_buf;
|
||||
uint8_t *cur_buf;
|
||||
uint32_t cur_size;
|
||||
uint32_t wtx_counter;
|
||||
uint32_t timeout_counter;
|
||||
uint32_t rnack_counter;
|
||||
uint32_t recovery_counter;
|
||||
frame_type_t last_frame;
|
||||
state_t state;
|
||||
uint8_t send_address;
|
||||
uint8_t receive_address;
|
||||
void *hal;
|
||||
} iso7816_t1_t;
|
||||
|
||||
#define PROTOCOL_HEADER_SIZE 0x03
|
||||
#define PROTOCOL_LRC_SIZE 0x01
|
||||
#define PROTOCOL_ZERO 0x00
|
||||
#define PROTOCOL_ADDRESS_POLLING_COUNT 100
|
||||
#define PROTOCOL_TIMEOUT_RETRY_COUNT 3
|
||||
#define PROTOCOL_PCB_OFFSET 1
|
||||
#define PROTOCOL_SEND_SIZE 254
|
||||
#define PROTOCOL_CHAINING 0x20
|
||||
#define PROTOCOL_S_BLOCK_REQ 0xC0
|
||||
#define PROTOCOL_S_BLOCK_RSP 0xE0
|
||||
#define PROTOCOL_FRAME_RETRY_COUNT 3
|
||||
#define PROTOCOL_MAX_RNACK_RETRY_LIMIT 2
|
||||
#define PROTOCOL_UDELAY(x) usleep_range(x, (x + 100))
|
||||
|
||||
#define SFRAME_MAX_INF_SIZE (4)
|
||||
#define RFRAME_PROTOCOL_SIZE (PROTOCOL_HEADER_SIZE + PROTOCOL_LRC_SIZE)
|
||||
|
||||
#define APDU_HEADER_SIZE (5)
|
||||
#define APDU_P3_OFFSET (4)
|
||||
|
||||
#define ESE_BIT(val, bit) ((val >> bit) & 0x1)
|
||||
|
||||
ESE_STATUS iso7816_t1_send(void *ctx, ese_data_t *data);
|
||||
ESE_STATUS iso7816_t1_receive(void *ctx, ese_data_t *data);
|
||||
ESE_STATUS iso7816_t1_resync_request(void *ctx);
|
||||
ESE_STATUS iso7816_t1_resync_response(void *ctx);
|
||||
ESE_STATUS iso7816_t1_wtx_request(void *ctx, uint32_t time);
|
||||
ESE_STATUS iso7816_t1_wtx_response(void *ctx);
|
||||
ESE_STATUS iso7816_t1_get_data(void *ctx, ese_data_t *data, uint8_t *le);
|
||||
void *iso7816_t1_init(uint8_t send_address, uint8_t receive_address, void *hal);
|
||||
void iso7816_t1_deinit(void *ctx);
|
||||
void iso7816_t1_reset(void *ctx);
|
||||
|
||||
#endif
|
28
drivers/nfc/snvm/protocol/ese_log.h
Normal file
28
drivers/nfc/snvm/protocol/ese_log.h
Normal file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Samsung Electronics. 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 version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef __ESE_LOG__H
|
||||
#define __ESE_LOG__H
|
||||
|
||||
#include <linux/printk.h>
|
||||
|
||||
#define LOG_D(msg...) pr_debug("[star-protocol] : " msg);
|
||||
#define LOG_I(msg...) pr_info("[star-protocol] : " msg);
|
||||
#define LOG_W(msg...) pr_warn("[star-protocol] : " msg);
|
||||
#define LOG_E(msg...) pr_err("[star-protocol] : " msg);
|
||||
|
||||
#endif
|
78
drivers/nfc/snvm/protocol/ese_memory.c
Normal file
78
drivers/nfc/snvm/protocol/ese_memory.c
Normal file
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Samsung Electronics. 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 version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "ese_log.h"
|
||||
#include "ese_memory.h"
|
||||
|
||||
#ifdef CONFIG_STAR_MEMORY_LEAK
|
||||
#define MAX_MEMORY_ALLOC_NUM 100
|
||||
|
||||
void *alloc_list[MAX_MEMORY_ALLOC_NUM];
|
||||
|
||||
void *ese_malloc(size_t size)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
for (i = 0; i < MAX_MEMORY_ALLOC_NUM; i ++) {
|
||||
if (alloc_list[i] == NULL) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == MAX_MEMORY_ALLOC_NUM) {
|
||||
LOG_E("<MEMORY> exceed alloc list size");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
alloc_list[i] = kzalloc(size, GFP_KERNEL);
|
||||
LOG_I("<MEMORY> kzalloc addr : %p, size : %u", alloc_list[i], (unsigned int)size);
|
||||
return alloc_list[i];
|
||||
}
|
||||
|
||||
void ese_free(void *ptr)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
for (i = 0; i < MAX_MEMORY_ALLOC_NUM; i ++) {
|
||||
if (alloc_list[i] == ptr) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == MAX_MEMORY_ALLOC_NUM) {
|
||||
LOG_E("<MEMORY> failed to find memory in alloc list : %p", ptr);
|
||||
return;
|
||||
}
|
||||
|
||||
alloc_list[i] = NULL;
|
||||
kfree((void *)ptr);
|
||||
LOG_I("<MEMORY> free addr : %p", ptr);
|
||||
}
|
||||
|
||||
void ese_alloc_list(void)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
for (i = 0; i < MAX_MEMORY_ALLOC_NUM; i ++) {
|
||||
if (alloc_list[i] != NULL) {
|
||||
LOG_E("<MEMORY> non free memory : %p", alloc_list[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
37
drivers/nfc/snvm/protocol/ese_memory.h
Normal file
37
drivers/nfc/snvm/protocol/ese_memory.h
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Samsung Electronics. 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 version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef __ESE_MEMORY__H
|
||||
#define __ESE_MEMORY__H
|
||||
|
||||
#ifdef CONFIG_STAR_MEMORY_LEAK
|
||||
void *ese_malloc(size_t size);
|
||||
void ese_free(void *ptr);
|
||||
void ese_print_list(void);
|
||||
|
||||
#define ESE_MALLOC ese_malloc
|
||||
#define ESE_FREE ese_free
|
||||
#define ESE_ALLOC_LIST() ese_alloc_list()
|
||||
#else
|
||||
#include <linux/slab.h>
|
||||
|
||||
#define ESE_MALLOC(x) kzalloc(x, GFP_KERNEL)
|
||||
#define ESE_FREE(x) kfree(x)
|
||||
#define ESE_ALLOC_LIST()
|
||||
#endif
|
||||
|
||||
#endif
|
26
drivers/nfc/snvm/protocol/ese_protocol.h
Normal file
26
drivers/nfc/snvm/protocol/ese_protocol.h
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Samsung Electronics. 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 version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef __ESE_PROTOCOL__H
|
||||
#define __ESE_PROTOCOL__H
|
||||
|
||||
typedef struct ese_data_s {
|
||||
uint32_t size;
|
||||
uint8_t* data;
|
||||
} ese_data_t;
|
||||
|
||||
#endif
|
522
drivers/nfc/snvm/sec_k250a.c
Normal file
522
drivers/nfc/snvm/sec_k250a.c
Normal file
@@ -0,0 +1,522 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Samsung Electronics. 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program;
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/ioctl.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/clk.h>
|
||||
#if defined(CONFIG_SEC_SNVM_PLATFORM_DRV)
|
||||
#include <linux/platform_device.h>
|
||||
#endif
|
||||
|
||||
#include "sec_star.h"
|
||||
|
||||
#undef USE_INTERNAL_PULLUP //if use external pull-up, not necessary
|
||||
|
||||
#define ERR(msg...) pr_err("[star-k250a] : " msg)
|
||||
#define INFO(msg...) pr_info("[star-k250a] : " msg)
|
||||
|
||||
struct k250a_dev {
|
||||
struct i2c_client *client;
|
||||
struct regulator *vdd;
|
||||
unsigned int reset_gpio;
|
||||
struct pinctrl *pinctrl;
|
||||
struct pinctrl_state *nvm_on_pin;
|
||||
struct pinctrl_state *nvm_off_pin;
|
||||
#if defined(USE_INTERNAL_PULLUP)
|
||||
#define SCL_GPIO_NUM 335
|
||||
#define SDA_GPIO_NUM 320
|
||||
struct pinctrl *pinctrl;
|
||||
struct pinctrl_state *pin_i2c;
|
||||
struct pinctrl_state *pin_gpio;
|
||||
#endif
|
||||
sec_star_t *star;
|
||||
#ifdef CONFIG_SEC_SNVM_I2C_CLOCK_CONTROL
|
||||
struct clk *i2c_main_clk;
|
||||
struct clk *i2c_dma_clk;
|
||||
bool i2c_main_clk_enabled;
|
||||
bool i2c_dma_clk_enabled;
|
||||
#endif
|
||||
};
|
||||
|
||||
static struct k250a_dev g_k250a;
|
||||
|
||||
#ifdef CONFIG_SEC_SNVM_I2C_CLOCK_CONTROL
|
||||
static void k250a_parse_i2c_clock(struct k250a_dev *k250a, struct device_node *np)
|
||||
{
|
||||
struct device_node *i2c_np;
|
||||
|
||||
i2c_np = of_parse_phandle(np, "i2c_node", 0);
|
||||
if (!i2c_np) {
|
||||
INFO("i2c_node not found\n");
|
||||
return;
|
||||
}
|
||||
|
||||
INFO("i2c_node found\n");
|
||||
k250a->i2c_main_clk = of_clk_get_by_name(i2c_np, "main");
|
||||
|
||||
if (IS_ERR(k250a->i2c_main_clk))
|
||||
INFO("failed to get i2c main clock\n");
|
||||
else
|
||||
INFO("i2c main clock found");
|
||||
|
||||
k250a->i2c_dma_clk = of_clk_get_by_name(i2c_np, "dma");
|
||||
if (IS_ERR(k250a->i2c_dma_clk))
|
||||
INFO("failed to get i2c dma clock\n");
|
||||
else
|
||||
INFO("i2c dma clock found");
|
||||
}
|
||||
|
||||
static void k250a_i2c_clock_enable(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (!g_k250a.i2c_main_clk_enabled && !IS_ERR_OR_NULL(g_k250a.i2c_main_clk)) {
|
||||
ret = clk_prepare_enable(g_k250a.i2c_main_clk);
|
||||
if (ret)
|
||||
ERR("failed to enable i2c main clock\n");
|
||||
else
|
||||
g_k250a.i2c_main_clk_enabled = true;
|
||||
}
|
||||
|
||||
if (!g_k250a.i2c_dma_clk_enabled && !IS_ERR_OR_NULL(g_k250a.i2c_dma_clk)) {
|
||||
ret = clk_prepare_enable(g_k250a.i2c_dma_clk);
|
||||
if (ret)
|
||||
ERR("failed to enable i2c dma clock\n");
|
||||
else
|
||||
g_k250a.i2c_dma_clk_enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void k250a_i2c_clock_disable(void)
|
||||
{
|
||||
if (g_k250a.i2c_main_clk_enabled && !IS_ERR_OR_NULL(g_k250a.i2c_main_clk)) {
|
||||
clk_disable_unprepare(g_k250a.i2c_main_clk);
|
||||
g_k250a.i2c_main_clk_enabled = false;
|
||||
}
|
||||
|
||||
if (g_k250a.i2c_dma_clk_enabled && !IS_ERR_OR_NULL(g_k250a.i2c_dma_clk)) {
|
||||
clk_disable_unprepare(g_k250a.i2c_dma_clk);
|
||||
g_k250a.i2c_dma_clk_enabled = false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static int k250a_poweron(void)
|
||||
{
|
||||
int ret = 0;
|
||||
#ifdef CONFIG_SEC_SNVM_I2C_CLOCK_CONTROL
|
||||
bool i2c_main_clk = !IS_ERR_OR_NULL(g_k250a.i2c_main_clk);
|
||||
bool i2c_dma_clk = !IS_ERR_OR_NULL(g_k250a.i2c_dma_clk);
|
||||
|
||||
INFO("k250a_poweron (i2c clk:%s%s)\n",
|
||||
i2c_main_clk ? " main" : "", i2c_dma_clk ? " dma" : "");
|
||||
#else
|
||||
INFO("k250a_poweron\n");
|
||||
#endif
|
||||
if (g_k250a.vdd == NULL) {
|
||||
if (g_k250a.reset_gpio == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
INFO("rest pin control instead of vdd\n");
|
||||
|
||||
gpio_set_value(g_k250a.reset_gpio, 0);
|
||||
usleep_range(1000, 2000);
|
||||
gpio_set_value(g_k250a.reset_gpio, 1);
|
||||
|
||||
msleep(20);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = regulator_enable(g_k250a.vdd);
|
||||
if (ret) {
|
||||
ERR("%s - enable vdd failed, ret=%d\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
usleep_range(1000, 2000);
|
||||
if (g_k250a.nvm_on_pin) {
|
||||
if (pinctrl_select_state(g_k250a.pinctrl, g_k250a.nvm_on_pin))
|
||||
ERR("nvm on pinctrl set error\n");
|
||||
else
|
||||
INFO("nvm on pinctrl set\n");
|
||||
}
|
||||
|
||||
#if defined(USE_INTERNAL_PULLUP)
|
||||
if (pinctrl_select_state(g_k250a.pinctrl, g_k250a.pin_i2c) < 0) {
|
||||
ERR("failed to pinctrl_select_state for gpio");
|
||||
}
|
||||
#endif
|
||||
|
||||
msleep(20);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int k250a_poweroff(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
INFO("k250a_poweroff\n");
|
||||
|
||||
if (g_k250a.vdd == NULL) {
|
||||
return 0;
|
||||
}
|
||||
if (g_k250a.nvm_off_pin) {
|
||||
if (pinctrl_select_state(g_k250a.pinctrl, g_k250a.nvm_off_pin))
|
||||
ERR("nvm off pinctrl set error\n");
|
||||
else
|
||||
INFO("nvm off pinctrl set\n");
|
||||
}
|
||||
|
||||
#if defined(USE_INTERNAL_PULLUP)
|
||||
if (pinctrl_select_state(g_k250a.pinctrl, g_k250a.pin_gpio) < 0) {
|
||||
ERR("failed to pinctrl_select_state for gpio");
|
||||
}
|
||||
#endif
|
||||
|
||||
ret = regulator_disable(g_k250a.vdd);
|
||||
if (ret) {
|
||||
ERR("%s - disable vdd failed, ret=%d\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
usleep_range(15000, 20000);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int k250a_reset(void)
|
||||
{
|
||||
if (g_k250a.reset_gpio == 0) {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
gpio_set_value(g_k250a.reset_gpio, 0);
|
||||
usleep_range(1000, 2000);
|
||||
gpio_set_value(g_k250a.reset_gpio, 1);
|
||||
usleep_range(15000, 20000);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static star_dev_t star_dev = {
|
||||
.name = "k250a",
|
||||
.hal_type = SEC_HAL_I2C,
|
||||
.client = NULL,
|
||||
.power_on = k250a_poweron,
|
||||
.power_off = k250a_poweroff,
|
||||
.reset = k250a_reset
|
||||
};
|
||||
|
||||
#if (KERNEL_VERSION(6, 3, 0) <= LINUX_VERSION_CODE)
|
||||
static int k250a_probe(struct i2c_client *client)
|
||||
#else
|
||||
static int k250a_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
||||
#endif
|
||||
{
|
||||
struct device_node *np = client->dev.of_node;
|
||||
|
||||
INFO("Entry : %s\n", __func__);
|
||||
|
||||
if (np) {
|
||||
g_k250a.vdd = devm_regulator_get_optional(&(client->dev), "1p8_pvdd");
|
||||
if (IS_ERR(g_k250a.vdd)) {
|
||||
ERR("%s - 1p8_pvdd can not be used\n", __func__);
|
||||
g_k250a.vdd = NULL;
|
||||
}
|
||||
|
||||
if (of_property_read_u32(np, "reset_gpio", &(g_k250a.reset_gpio)) < 0) {
|
||||
ERR("%s - Reset Control can not be used\n", __func__);
|
||||
g_k250a.reset_gpio = 0;
|
||||
} else {
|
||||
if (gpio_request(g_k250a.reset_gpio, "sec-reset") < 0) {
|
||||
ERR("%s - Failed to request sec-reset gpio\n", __func__);
|
||||
g_k250a.reset_gpio = 0;
|
||||
} else {
|
||||
INFO("%s - Reset GPIO Num : %u\n", __func__, g_k250a.reset_gpio);
|
||||
if (gpio_direction_output(g_k250a.reset_gpio, 1) < 0) {
|
||||
ERR("Failed to set reset gpio");
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
g_k250a.pinctrl = devm_pinctrl_get(client->adapter->dev.parent);
|
||||
if (IS_ERR(g_k250a.pinctrl)) {
|
||||
ERR("devm_pinctrl_get failed\n");
|
||||
return -1;
|
||||
}
|
||||
g_k250a.nvm_on_pin = pinctrl_lookup_state(g_k250a.pinctrl, "nvm_on");
|
||||
if (IS_ERR(g_k250a.nvm_on_pin)) {
|
||||
ERR("pinctrl_lookup_state failed-default\n");
|
||||
g_k250a.nvm_on_pin = NULL;
|
||||
}
|
||||
g_k250a.nvm_off_pin = pinctrl_lookup_state(g_k250a.pinctrl, "nvm_off");
|
||||
if (IS_ERR(g_k250a.nvm_off_pin)) {
|
||||
ERR("pinctrl_lookup_state failed-nvm_off\n");
|
||||
g_k250a.nvm_off_pin = NULL;
|
||||
} else if (pinctrl_select_state(g_k250a.pinctrl, g_k250a.nvm_off_pin)) {
|
||||
ERR("nvm off pinctrl set error\n");
|
||||
} else {
|
||||
INFO("nvm off pinctrl set\n");
|
||||
}
|
||||
|
||||
#if defined(USE_INTERNAL_PULLUP)
|
||||
g_k250a.pinctrl = devm_pinctrl_get(client->adapter->dev.parent);
|
||||
if (IS_ERR(g_k250a.pinctrl)) {
|
||||
ERR("failed to devm_pinctrl_get");
|
||||
return -1;
|
||||
}
|
||||
|
||||
g_k250a.pin_i2c = pinctrl_lookup_state(g_k250a.pinctrl, "default");
|
||||
if (IS_ERR(g_k250a.pin_i2c)) {
|
||||
ERR("failed to pinctrl_lookup_state for i2c");
|
||||
devm_pinctrl_put(g_k250a.pinctrl);
|
||||
return -1;
|
||||
}
|
||||
|
||||
g_k250a.pin_gpio = pinctrl_lookup_state(g_k250a.pinctrl, "gpio");
|
||||
if (IS_ERR(g_k250a.pin_gpio)) {
|
||||
ERR("failed to pinctrl_lookup_state for gpio");
|
||||
devm_pinctrl_put(g_k250a.pinctrl);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (pinctrl_select_state(g_k250a.pinctrl, g_k250a.pin_gpio) < 0) {
|
||||
ERR("failed to pinctrl_select_state for gpio");
|
||||
devm_pinctrl_put(g_k250a.pinctrl);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (gpio_request(SCL_GPIO_NUM, "sec-scl") < 0) {
|
||||
ERR("failed to get scl gpio");
|
||||
devm_pinctrl_put(g_k250a.pinctrl);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (gpio_request(SDA_GPIO_NUM, "sec-sda") < 0) {
|
||||
ERR("failed to get sda gpio");
|
||||
devm_pinctrl_put(g_k250a.pinctrl);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (gpio_direction_output(SCL_GPIO_NUM, 0) < 0) {
|
||||
ERR("failed to set scl gpio");
|
||||
devm_pinctrl_put(g_k250a.pinctrl);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (gpio_direction_output(SDA_GPIO_NUM, 0) < 0) {
|
||||
ERR("failed to set sda gpio");
|
||||
devm_pinctrl_put(g_k250a.pinctrl);
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
g_k250a.client = client;
|
||||
star_dev.client = client;
|
||||
g_k250a.star = star_open(&star_dev);
|
||||
if (g_k250a.star == NULL) {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
INFO("Exit : %s\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0)
|
||||
static int k250a_remove(struct i2c_client *client)
|
||||
#else
|
||||
static void k250a_remove(struct i2c_client *client)
|
||||
#endif
|
||||
{
|
||||
INFO("Entry : %s\n", __func__);
|
||||
#if defined(USE_INTERNAL_PULLUP)
|
||||
devm_pinctrl_put(g_k250a.pinctrl);
|
||||
gpio_free(SCL_GPIO_NUM);
|
||||
gpio_free(SDA_GPIO_NUM);
|
||||
#endif
|
||||
if (g_k250a.reset_gpio != 0) {
|
||||
gpio_free(g_k250a.reset_gpio);
|
||||
}
|
||||
star_close(g_k250a.star);
|
||||
INFO("Exit : %s\n", __func__);
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0)
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(CONFIG_SEC_SNVM_PLATFORM_DRV)
|
||||
static int k250a_dev_open(struct inode *inode, struct file *filp)
|
||||
{
|
||||
k250a_poweron();
|
||||
#ifdef CONFIG_SEC_SNVM_I2C_CLOCK_CONTROL
|
||||
k250a_i2c_clock_enable();
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ese_dev_release(struct inode *inode, struct file *filp)
|
||||
{
|
||||
#ifdef CONFIG_SEC_SNVM_I2C_CLOCK_CONTROL
|
||||
k250a_i2c_clock_disable();
|
||||
#endif
|
||||
k250a_poweroff();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct file_operations k250a_dev_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = k250a_dev_open,
|
||||
.release = ese_dev_release,
|
||||
};
|
||||
|
||||
static struct miscdevice k250a_misc_device = {
|
||||
.minor = MISC_DYNAMIC_MINOR,
|
||||
.name = "k250a",
|
||||
.fops = &k250a_dev_fops,
|
||||
};
|
||||
|
||||
void k250a_parse_dt_for_platform_device(struct device *dev)
|
||||
{
|
||||
g_k250a.vdd = devm_regulator_get_optional(dev, "1p8_pvdd");
|
||||
if (IS_ERR(g_k250a.vdd)) {
|
||||
ERR("%s - 1p8_pvdd can not be used\n", __func__);
|
||||
g_k250a.vdd = NULL;
|
||||
} else {
|
||||
INFO("%s: regulator_get success\n", __func__);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SEC_SNVM_I2C_CLOCK_CONTROL
|
||||
k250a_parse_i2c_clock(&g_k250a, dev->of_node);
|
||||
#endif
|
||||
}
|
||||
|
||||
static int k250a_platform_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret = -1;
|
||||
|
||||
k250a_parse_dt_for_platform_device(&pdev->dev);
|
||||
ret = misc_register(&k250a_misc_device);
|
||||
if (ret < 0)
|
||||
ERR("misc_register failed! %d\n", ret);
|
||||
|
||||
INFO("%s: finished...\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int k250a_platform_remove(struct platform_device *pdev)
|
||||
{
|
||||
INFO("Entry : %s\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id k250a_secure_match_table[] = {
|
||||
{ .compatible = "sec_k250a_platform",},
|
||||
{},
|
||||
};
|
||||
|
||||
static struct platform_driver k250a_platform_driver = {
|
||||
.driver = {
|
||||
.name = "k250a_platform",
|
||||
.owner = THIS_MODULE,
|
||||
#ifdef CONFIG_OF
|
||||
.of_match_table = k250a_secure_match_table,
|
||||
#endif
|
||||
},
|
||||
.probe = k250a_platform_probe,
|
||||
.remove = k250a_platform_remove,
|
||||
};
|
||||
#endif
|
||||
|
||||
static const struct i2c_device_id k250a_id[] = {
|
||||
{"k250a", 0},
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct of_device_id k250a_match_table[] = {
|
||||
{ .compatible = "sec_k250a",},
|
||||
{},
|
||||
};
|
||||
|
||||
static struct i2c_driver k250a_driver = {
|
||||
.id_table = k250a_id,
|
||||
.probe = k250a_probe,
|
||||
.remove = k250a_remove,
|
||||
.driver = {
|
||||
.name = "k250a",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = k250a_match_table,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init k250a_init(void)
|
||||
{
|
||||
#if defined(CONFIG_SEC_SNVM_PLATFORM_DRV)
|
||||
int ret;
|
||||
|
||||
ret = platform_driver_register(&k250a_platform_driver);
|
||||
if (!ret) {
|
||||
INFO("platform_driver_register success : %s\n", __func__);
|
||||
return ret;
|
||||
} else {
|
||||
ERR("platform_driver_register fail : %s\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
INFO("Entry : %s\n", __func__);
|
||||
|
||||
return i2c_add_driver(&k250a_driver);
|
||||
}
|
||||
module_init(k250a_init);
|
||||
|
||||
static void __exit k250a_exit(void)
|
||||
{
|
||||
INFO("Entry : %s\n", __func__);
|
||||
#if defined(CONFIG_SEC_SNVM_PLATFORM_DRV)
|
||||
platform_driver_unregister(&k250a_platform_driver);
|
||||
return;
|
||||
#endif
|
||||
i2c_del_driver(&k250a_driver);
|
||||
}
|
||||
|
||||
module_exit(k250a_exit);
|
||||
|
||||
MODULE_AUTHOR("Sec");
|
||||
MODULE_DESCRIPTION("K250A driver");
|
||||
MODULE_LICENSE("GPL");
|
532
drivers/nfc/snvm/sec_star.c
Normal file
532
drivers/nfc/snvm/sec_star.c
Normal file
@@ -0,0 +1,532 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Samsung Electronics. 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program;
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/ioctl.h>
|
||||
|
||||
#include "hal/ese_hal.h"
|
||||
#include "protocol/ese_memory.h"
|
||||
#include "protocol/ese_iso7816_t1.h"
|
||||
#include "sec_star.h"
|
||||
|
||||
#define STAR_VERSION "STAR00010000"
|
||||
|
||||
#define ERR(msg...) pr_err("[star] : " msg)
|
||||
#define INFO(msg...) pr_info("[star] : " msg)
|
||||
|
||||
#define BIGENDIAN_TO_UINT32(x, y) \
|
||||
{ \
|
||||
y = ((x)[0] << 24) | ((x)[1] << 16) | ((x)[2] << 8) | (x)[3]; \
|
||||
}
|
||||
|
||||
#define SEND_ADDRESS 0x12
|
||||
#define RECEIVE_ADDRESS 0x21
|
||||
|
||||
#define APDU_CHAIN_NUM_SIZE 4
|
||||
#define APDU_CHAIN_SEQ_SIZE 4
|
||||
#define APDU_CHAIN_CMD_SIZE 4
|
||||
#define APDU_CHAIN_EXP_SIZE 4
|
||||
#define APDU_CHAIN_HEADER_SIZE (APDU_CHAIN_SEQ_SIZE + APDU_CHAIN_CMD_SIZE + APDU_CHAIN_EXP_SIZE)
|
||||
|
||||
#define APDU_CHAIN_EXP_FLAG 4
|
||||
|
||||
#define EXPECTED_RESPONSE_NONE 0x0
|
||||
#define EXPECTED_RESPONSE_NEXT 0x1
|
||||
#define EXPECTED_RESPONSE_AGAIN 0x2
|
||||
|
||||
#define STAR_MAGIC_CODE 'S'
|
||||
#define STAR_READ_SIZE _IOR(STAR_MAGIC_CODE, 0, unsigned int)
|
||||
#define STAR_SET_DIRECT _IOW(STAR_MAGIC_CODE, 1, int)
|
||||
#define STAR_RESET_PROTOCOL _IO(STAR_MAGIC_CODE, 2)
|
||||
#define STAR_RESET_INTERFACE _IO(STAR_MAGIC_CODE, 3)
|
||||
|
||||
#define APDU_CHAIN_MAX_SIZE ((65536/256) + 1)
|
||||
|
||||
static int32_t star_transceive(void *ctx, uint8_t *cmd, uint32_t cmd_size, uint8_t **rsp, uint32_t *rsp_size)
|
||||
{
|
||||
ese_data_t cmd_data = {0, NULL};
|
||||
ese_data_t rsp_data = {0, NULL};
|
||||
uint8_t *p_cmd = NULL;
|
||||
uint32_t chain_num = 0;
|
||||
uint32_t seq = 0;
|
||||
uint32_t data_size = 0;
|
||||
uint32_t expected_flag = 0;
|
||||
uint32_t expected_size = 0;
|
||||
uint32_t i = 0;
|
||||
data_list_t total_rsp;
|
||||
|
||||
if (ctx == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((cmd == NULL) || (cmd_size == 0) || (rsp == NULL) || (rsp_size == NULL)) {
|
||||
ERR("invalid parameter or no data");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (cmd_size < APDU_CHAIN_NUM_SIZE) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
p_cmd = cmd;
|
||||
BIGENDIAN_TO_UINT32(p_cmd, chain_num);
|
||||
p_cmd += APDU_CHAIN_NUM_SIZE;
|
||||
cmd_size -= APDU_CHAIN_NUM_SIZE;
|
||||
|
||||
if (chain_num == 0 || chain_num > APDU_CHAIN_MAX_SIZE) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ese_data_init(&total_rsp);
|
||||
for (i = 0; i < chain_num; i++) {
|
||||
if (cmd_size <= APDU_CHAIN_HEADER_SIZE) {
|
||||
ERR("invalid command chain header size");
|
||||
goto error;
|
||||
}
|
||||
|
||||
BIGENDIAN_TO_UINT32(p_cmd, seq);
|
||||
p_cmd += APDU_CHAIN_SEQ_SIZE;
|
||||
if (seq != i) {
|
||||
ERR("invalid sequence number");
|
||||
goto error;
|
||||
}
|
||||
|
||||
BIGENDIAN_TO_UINT32(p_cmd, data_size);
|
||||
p_cmd += APDU_CHAIN_CMD_SIZE;
|
||||
BIGENDIAN_TO_UINT32(p_cmd, expected_size);
|
||||
p_cmd += APDU_CHAIN_EXP_SIZE;
|
||||
cmd_size -= APDU_CHAIN_HEADER_SIZE;
|
||||
if (cmd_size < (data_size + expected_size) || data_size > (data_size + expected_size)) {
|
||||
ERR("invalid send data or expected data size");
|
||||
goto error;
|
||||
}
|
||||
|
||||
cmd_data.data = p_cmd;
|
||||
cmd_data.size = data_size;
|
||||
rsp_data.data = NULL;
|
||||
rsp_data.size = 0;
|
||||
p_cmd += data_size;
|
||||
cmd_size -= data_size;
|
||||
|
||||
again:
|
||||
if (iso7816_t1_send(ctx, &cmd_data) != ESESTATUS_SUCCESS) {
|
||||
ERR("failed to send apdu");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (iso7816_t1_receive(ctx, &rsp_data) != ESESTATUS_SUCCESS) {
|
||||
ERR("failed to receive response");
|
||||
if(rsp_data.data) {
|
||||
ESE_FREE(rsp_data.data);
|
||||
}
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (expected_size > 0) {
|
||||
BIGENDIAN_TO_UINT32(p_cmd, expected_flag);
|
||||
if (rsp_data.size < (expected_size - APDU_CHAIN_EXP_FLAG) ||
|
||||
memcmp(p_cmd + APDU_CHAIN_EXP_FLAG,
|
||||
rsp_data.data + (rsp_data.size - (expected_size - APDU_CHAIN_EXP_FLAG)),
|
||||
(expected_size - APDU_CHAIN_EXP_FLAG)) != 0) {
|
||||
if (ese_data_store(&total_rsp, rsp_data.data, rsp_data.size, 0) != ESESTATUS_SUCCESS) {
|
||||
goto error;
|
||||
}
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (expected_flag & EXPECTED_RESPONSE_AGAIN) {
|
||||
if (rsp_data.size > 2) {
|
||||
if (ese_data_store(&total_rsp, rsp_data.data, rsp_data.size - 2, 0) != ESESTATUS_SUCCESS) {
|
||||
goto error;
|
||||
}
|
||||
} else {
|
||||
ESE_FREE(rsp_data.data);
|
||||
}
|
||||
goto again;
|
||||
}
|
||||
|
||||
p_cmd += expected_size;
|
||||
cmd_size -= expected_size;
|
||||
}
|
||||
|
||||
if (i < (chain_num - 1)) {
|
||||
if (rsp_data.size > 2) {
|
||||
if (ese_data_store(&total_rsp, rsp_data.data, rsp_data.size - 2, 0) != ESESTATUS_SUCCESS) {
|
||||
goto error;
|
||||
}
|
||||
} else {
|
||||
ESE_FREE(rsp_data.data);
|
||||
}
|
||||
} else {
|
||||
if (ese_data_store(&total_rsp, rsp_data.data, rsp_data.size, 0) != ESESTATUS_SUCCESS) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
ese_data_get(&total_rsp, rsp, rsp_size);
|
||||
return 0;
|
||||
error:
|
||||
ese_data_delete(&total_rsp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int star_dev_open(struct inode *inode, struct file *filp)
|
||||
{
|
||||
sec_star_t *star = container_of(filp->private_data,
|
||||
sec_star_t, misc);
|
||||
int ret = 0;
|
||||
|
||||
INFO("star_open\n");
|
||||
|
||||
mutex_lock(&(star->lock));
|
||||
|
||||
filp->private_data = star;
|
||||
|
||||
if (star->access == 0) {
|
||||
#ifdef FEATURE_STAR_WAKELOCK
|
||||
if (!wake_lock_active(&star->snvm_wake_lock)) {
|
||||
wake_lock(&star->snvm_wake_lock);
|
||||
INFO("called to snvm_wake_lock\n");
|
||||
}
|
||||
#endif
|
||||
iso7816_t1_reset(star->protocol);
|
||||
|
||||
ret = star->dev->power_on();
|
||||
if (ret < 0) {
|
||||
#ifdef FEATURE_STAR_WAKELOCK
|
||||
if (wake_lock_active(&star->snvm_wake_lock)) {
|
||||
wake_unlock(&star->snvm_wake_lock);
|
||||
INFO("called to snvm_wake_unlock\n");
|
||||
}
|
||||
#endif
|
||||
ERR("%s :failed to open star", __func__);
|
||||
mutex_unlock(&(star->lock));
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
star->access++;
|
||||
|
||||
mutex_unlock(&(star->lock));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int star_dev_close(struct inode *inode, struct file *filp)
|
||||
{
|
||||
sec_star_t *star = (sec_star_t *)filp->private_data;
|
||||
int ret = 0;
|
||||
|
||||
INFO("star_close\n");
|
||||
|
||||
if (star == NULL) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mutex_lock(&(star->lock));
|
||||
|
||||
star->access--;
|
||||
|
||||
if (star->access == 0) {
|
||||
ret = star->dev->power_off();
|
||||
if (ret < 0)
|
||||
ERR("%s :failed power_off", __func__);
|
||||
|
||||
#ifdef FEATURE_STAR_WAKELOCK
|
||||
if (wake_lock_active(&star->snvm_wake_lock)) {
|
||||
wake_unlock(&star->snvm_wake_lock);
|
||||
INFO("called to snvm_wake_unlock\n");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
mutex_unlock(&(star->lock));
|
||||
return ret;
|
||||
}
|
||||
|
||||
static long star_dev_ioctl(struct file *filp, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
sec_star_t *star = (sec_star_t *)filp->private_data;
|
||||
|
||||
if (star == NULL) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (_IOC_TYPE(cmd) != STAR_MAGIC_CODE) {
|
||||
ERR("%s invalid magic. cmd=0x%X Received=0x%X Expected=0x%X\n",
|
||||
__func__, cmd, _IOC_TYPE(cmd), STAR_MAGIC_CODE);
|
||||
return -ENOTTY;
|
||||
}
|
||||
|
||||
mutex_lock(&(star->lock));
|
||||
|
||||
switch (cmd) {
|
||||
case STAR_READ_SIZE:
|
||||
INFO("%s read size : %u\n", __func__, star->rsp_size);
|
||||
put_user(star->rsp_size, (unsigned int __user *)arg);
|
||||
break;
|
||||
case STAR_SET_DIRECT:
|
||||
get_user(star->direct, (int __user *)arg);
|
||||
INFO("%s set direct : %d\n", __func__, star->direct);
|
||||
break;
|
||||
case STAR_RESET_PROTOCOL:
|
||||
INFO("%s reset protocol\n", __func__);
|
||||
iso7816_t1_reset(star->protocol);
|
||||
break;
|
||||
case STAR_RESET_INTERFACE:
|
||||
INFO("%s reset interface\n", __func__);
|
||||
star->dev->reset();
|
||||
iso7816_t1_reset(star->protocol);
|
||||
break;
|
||||
default:
|
||||
INFO("%s no matching ioctl! 0x%X\n", __func__, cmd);
|
||||
mutex_unlock(&(star->lock));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mutex_unlock(&(star->lock));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t star_dev_write(struct file *filp, const char __user *buf, size_t count, loff_t *offset)
|
||||
{
|
||||
sec_star_t *star = (sec_star_t *)filp->private_data;
|
||||
uint8_t *cmd = NULL;
|
||||
uint32_t cmd_size = 0;
|
||||
uint8_t *rsp = NULL;
|
||||
uint32_t rsp_size = 0;
|
||||
int ret = -EIO;
|
||||
|
||||
if (star == NULL || count == 0) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mutex_lock(&(star->lock));
|
||||
|
||||
if (star->rsp != NULL && star->rsp_size > 0) {
|
||||
ESE_FREE(star->rsp);
|
||||
star->rsp = NULL;
|
||||
star->rsp_size = 0;
|
||||
}
|
||||
|
||||
cmd_size = (uint32_t)count;
|
||||
cmd = ESE_MALLOC(cmd_size);
|
||||
if (cmd == NULL) {
|
||||
ERR("failed to allocate for i2c buf\n");
|
||||
mutex_unlock(&(star->lock));
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (copy_from_user(cmd, (void __user *)buf, cmd_size) > 0) {
|
||||
ERR("%s: failed to copy from user space\n", __func__);
|
||||
ret = -EFAULT;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (star->direct > 0) {
|
||||
if (ese_hal_send(star->hal, cmd, cmd_size) < 0) {
|
||||
ERR("i2c_master_send failed\n");
|
||||
ret = -EIO;
|
||||
goto error;
|
||||
}
|
||||
} else {
|
||||
if (star_transceive(star->protocol, cmd, cmd_size, &rsp, &rsp_size) != ESESTATUS_SUCCESS) {
|
||||
ERR("%s: failed to ese_transceive_chain\n", __func__);
|
||||
ret = -EIO;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
star->rsp = rsp;
|
||||
star->rsp_size = rsp_size;
|
||||
ret = (int)cmd_size;
|
||||
error:
|
||||
ESE_FREE(cmd);
|
||||
mutex_unlock(&(star->lock));
|
||||
INFO("%s: count:%zu ret:%d\n", __func__, count, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t star_dev_read(struct file *filp, char __user *buf, size_t count, loff_t *offset)
|
||||
{
|
||||
sec_star_t *star = (sec_star_t *)filp->private_data;
|
||||
uint8_t *tmp = NULL;
|
||||
|
||||
if (star == NULL || count == 0) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mutex_lock(&(star->lock));
|
||||
if (star->direct > 0) {
|
||||
tmp = ESE_MALLOC(count);
|
||||
if (tmp == NULL) {
|
||||
mutex_unlock(&(star->lock));
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (ese_hal_receive(star->hal, tmp, count) < 0) {
|
||||
ERR("i2c_master_send failed\n");
|
||||
ESE_FREE(tmp);
|
||||
mutex_unlock(&(star->lock));
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (copy_to_user((void __user *)buf, tmp, count) > 0) {
|
||||
ERR("copy_to_user failed\n");
|
||||
ESE_FREE(tmp);
|
||||
mutex_unlock(&(star->lock));
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ESE_FREE(tmp);
|
||||
} else {
|
||||
if (star->rsp == NULL || star->rsp_size == 0) {
|
||||
mutex_unlock(&(star->lock));
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
if (star->rsp_size != count) {
|
||||
ERR("mismatch response size\n");
|
||||
ESE_FREE(star->rsp);
|
||||
star->rsp = NULL;
|
||||
star->rsp_size = 0;
|
||||
mutex_unlock(&(star->lock));
|
||||
return -E2BIG;
|
||||
}
|
||||
|
||||
if (copy_to_user((void __user *)buf, star->rsp, star->rsp_size) > 0) {
|
||||
ERR("copy_to_user failed\n");
|
||||
ESE_FREE(star->rsp);
|
||||
star->rsp = NULL;
|
||||
star->rsp_size = 0;
|
||||
mutex_unlock(&(star->lock));
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ESE_FREE(star->rsp);
|
||||
star->rsp = NULL;
|
||||
star->rsp_size = 0;
|
||||
}
|
||||
INFO("%s: count:%zu\n", __func__, count);
|
||||
mutex_unlock(&(star->lock));
|
||||
return (ssize_t)count;
|
||||
}
|
||||
|
||||
static const struct file_operations star_misc_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.read = star_dev_read,
|
||||
.write = star_dev_write,
|
||||
.open = star_dev_open,
|
||||
.release = star_dev_close,
|
||||
.unlocked_ioctl = star_dev_ioctl,
|
||||
};
|
||||
|
||||
sec_star_t *star_open(star_dev_t *dev)
|
||||
{
|
||||
sec_star_t *star = NULL;
|
||||
int ret = -1;
|
||||
|
||||
INFO("Version : %s\n", STAR_VERSION);
|
||||
INFO("Entry : %s\n", __func__);
|
||||
|
||||
star = ESE_MALLOC(sizeof(sec_star_t));
|
||||
if (star == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
star->dev = dev;
|
||||
star->protocol = NULL;
|
||||
star->rsp = NULL;
|
||||
star->rsp_size = 0;
|
||||
star->access = 0;
|
||||
star->direct = 0;
|
||||
|
||||
star->hal = ese_hal_init(dev->hal_type, dev->client);
|
||||
if (star->hal == NULL) {
|
||||
ERR("%s :failed to init hal", __func__);
|
||||
ESE_FREE(star);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
star->protocol = iso7816_t1_init(SEND_ADDRESS, RECEIVE_ADDRESS, star->hal);
|
||||
if (star->protocol == NULL) {
|
||||
ERR("%s :failed to open protocol", __func__);
|
||||
ESE_FREE(star);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mutex_init(&(star->lock));
|
||||
#ifdef FEATURE_STAR_WAKELOCK
|
||||
wake_lock_init(&star->snvm_wake_lock, WAKE_LOCK_SUSPEND, "star_wake_lock");
|
||||
#endif
|
||||
|
||||
star->misc.minor = MISC_DYNAMIC_MINOR;
|
||||
star->misc.name = dev->name;
|
||||
star->misc.fops = &star_misc_fops;
|
||||
ret = misc_register(&(star->misc));
|
||||
if (ret < 0) {
|
||||
ERR("misc_register failed! %d\n", ret);
|
||||
#ifdef FEATURE_STAR_WAKELOCK
|
||||
wake_lock_destroy(&star->snvm_wake_lock);
|
||||
#endif
|
||||
mutex_destroy(&(star->lock));
|
||||
ESE_FREE(star);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
INFO("Exit : %s\n", __func__);
|
||||
return star;
|
||||
}
|
||||
|
||||
void star_close(sec_star_t *star)
|
||||
{
|
||||
INFO("Entry : %s\n", __func__);
|
||||
|
||||
if (star == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
misc_deregister(&(star->misc));
|
||||
|
||||
iso7816_t1_deinit(star->protocol);
|
||||
ese_hal_release(star->hal);
|
||||
#ifdef FEATURE_STAR_WAKELOCK
|
||||
wake_lock_destroy(&star->snvm_wake_lock);
|
||||
#endif
|
||||
mutex_destroy(&(star->lock));
|
||||
ESE_FREE(star);
|
||||
INFO("Exit : %s\n", __func__);
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("sec");
|
||||
MODULE_DESCRIPTION("sec-star driver");
|
||||
MODULE_LICENSE("GPL");
|
61
drivers/nfc/snvm/sec_star.h
Normal file
61
drivers/nfc/snvm/sec_star.h
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Samsung Electronics. 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 version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef __SEC_STAR__H
|
||||
#define __SEC_STAR__H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
/* #define FEATURE_STAR_WAKELOCK */
|
||||
|
||||
#ifdef FEATURE_STAR_WAKELOCK
|
||||
#include "snvm_wakelock.h"
|
||||
#endif
|
||||
|
||||
enum sec_hal_e {
|
||||
SEC_HAL_I2C = 0,
|
||||
SEC_HAL_SPI,
|
||||
};
|
||||
|
||||
typedef struct star_dev_s {
|
||||
const char *name;
|
||||
int hal_type;
|
||||
void *client;
|
||||
int (*power_on)(void);
|
||||
int (*power_off)(void);
|
||||
int (*reset)(void);
|
||||
} star_dev_t;
|
||||
|
||||
typedef struct sec_star_s {
|
||||
star_dev_t *dev;
|
||||
struct mutex lock;
|
||||
#ifdef FEATURE_STAR_WAKELOCK
|
||||
struct snvm_wake_lock snvm_wake_lock;
|
||||
#endif
|
||||
struct miscdevice misc;
|
||||
void *hal;
|
||||
void *protocol;
|
||||
unsigned char *rsp;
|
||||
unsigned int rsp_size;
|
||||
unsigned int access;
|
||||
int direct;
|
||||
} sec_star_t;
|
||||
|
||||
sec_star_t *star_open(star_dev_t *dev);
|
||||
void star_close(sec_star_t *star);
|
||||
|
||||
#endif
|
74
drivers/nfc/snvm/snvm_wakelock.h
Normal file
74
drivers/nfc/snvm/snvm_wakelock.h
Normal file
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* SNVM Wakelock
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _SNVM_WAKELOCK_H
|
||||
#define _SNVM_WAKELOCK_H
|
||||
|
||||
#include <linux/ktime.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/version.h>
|
||||
|
||||
#define wake_lock_init(a, b, c) snvm_wake_lock_init(a, c)
|
||||
#define wake_lock_destroy(a) snvm_wake_lock_destroy(a)
|
||||
#define wake_lock_timeout(a, b) snvm_wake_lock_timeout(a, b)
|
||||
#define wake_lock_active(a) snvm_wake_lock_active(a)
|
||||
#define wake_lock(a) snvm_wake_lock(a)
|
||||
#define wake_unlock(a) snvm_wake_unlock(a)
|
||||
|
||||
struct snvm_wake_lock {
|
||||
struct wakeup_source *ws;
|
||||
};
|
||||
|
||||
static inline void snvm_wake_lock_init(struct snvm_wake_lock *lock, const char *name)
|
||||
{
|
||||
#if CONFIG_SEC_SNVM_WAKELOCK_METHOD == 2
|
||||
lock->ws = wakeup_source_register(NULL, name);
|
||||
#elif (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)) || CONFIG_SEC_SNVM_WAKELOCK_METHOD == 1
|
||||
wakeup_source_init(lock->ws, name); /* 4.19 R */
|
||||
if (!(lock->ws)) {
|
||||
lock->ws = wakeup_source_create(name); /* 4.19 Q */
|
||||
if (lock->ws)
|
||||
wakeup_source_add(lock->ws);
|
||||
}
|
||||
#else
|
||||
lock->ws = wakeup_source_register(NULL, name); /* 5.4 R */
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void snvm_wake_lock_destroy(struct snvm_wake_lock *lock)
|
||||
{
|
||||
if (lock->ws)
|
||||
wakeup_source_unregister(lock->ws);
|
||||
}
|
||||
|
||||
static inline void snvm_wake_lock(struct snvm_wake_lock *lock)
|
||||
{
|
||||
if (lock->ws)
|
||||
__pm_stay_awake(lock->ws);
|
||||
}
|
||||
|
||||
static inline void snvm_wake_lock_timeout(struct snvm_wake_lock *lock, long timeout)
|
||||
{
|
||||
if (lock->ws)
|
||||
__pm_wakeup_event(lock->ws, jiffies_to_msecs(timeout));
|
||||
}
|
||||
|
||||
static inline void snvm_wake_unlock(struct snvm_wake_lock *lock)
|
||||
{
|
||||
if (lock->ws)
|
||||
__pm_relax(lock->ws);
|
||||
}
|
||||
|
||||
static inline int snvm_wake_lock_active(struct snvm_wake_lock *lock)
|
||||
{
|
||||
return lock->ws->active;
|
||||
}
|
||||
|
||||
#endif
|
Reference in New Issue
Block a user