Add samsung specific changes

This commit is contained in:
2025-08-11 14:29:00 +02:00
parent c66122e619
commit 4d134a1294
2688 changed files with 1127995 additions and 11475 deletions

View 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

View 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

View 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.

View 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

File diff suppressed because it is too large Load Diff

View 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_ */

View 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 */
}

View 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_ */

View 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;
}

View 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

File diff suppressed because it is too large Load Diff

View 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_ */

View 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

File diff suppressed because it is too large Load Diff

View 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

File diff suppressed because it is too large Load Diff

41
drivers/nfc/snvm/Kconfig Normal file
View 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
View 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

View 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);
}
}

View 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

View 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;
}

View 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

View 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;
}

View 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

View 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;
}

View 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

View 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

View 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);
}

View 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

View 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

View 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

View 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

View 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

View 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
View 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");

View 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

View 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