335 lines
7.5 KiB
C
335 lines
7.5 KiB
C
|
/* tui/stui_inf.c
|
||
|
*
|
||
|
* Samsung TUI HW Handler driver.
|
||
|
*
|
||
|
* Copyright (c) 2015 Samsung Electronics
|
||
|
*
|
||
|
* 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/atomic.h>
|
||
|
#include <linux/delay.h>
|
||
|
#include <linux/fb.h>
|
||
|
#include <linux/module.h>
|
||
|
#include <linux/reboot.h>
|
||
|
#include <linux/spinlock.h>
|
||
|
#include <linux/types.h>
|
||
|
#ifdef USE_TEE_CLIENT_API
|
||
|
#include <tee_client_api.h>
|
||
|
#endif /* USE_TEE_CLIENT_API */
|
||
|
#include "stui_inf.h"
|
||
|
|
||
|
#define TUI_REE_EXTERNAL_EVENT 42
|
||
|
#define SESSION_CANCEL_DELAY 150
|
||
|
#define MAX_WAIT_CNT 10
|
||
|
|
||
|
static int tui_mode = STUI_MODE_OFF;
|
||
|
static int tui_blank_cnt;
|
||
|
static DEFINE_SPINLOCK(tui_lock);
|
||
|
|
||
|
#ifdef USE_TEE_CLIENT_API
|
||
|
static TEEC_UUID uuid = {
|
||
|
.timeLow = 0x0,
|
||
|
.timeMid = 0x0,
|
||
|
.timeHiAndVersion = 0x0,
|
||
|
.clockSeqAndNode = {0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x81},
|
||
|
};
|
||
|
#endif
|
||
|
|
||
|
int stui_inc_blank_ref(void)
|
||
|
{
|
||
|
unsigned long fls;
|
||
|
int ret_cnt;
|
||
|
|
||
|
spin_lock_irqsave(&tui_lock, fls);
|
||
|
ret_cnt = ++tui_blank_cnt;
|
||
|
spin_unlock_irqrestore(&tui_lock, fls);
|
||
|
return ret_cnt;
|
||
|
}
|
||
|
EXPORT_SYMBOL(stui_inc_blank_ref);
|
||
|
|
||
|
int stui_dec_blank_ref(void)
|
||
|
{
|
||
|
unsigned long fls;
|
||
|
int ret_cnt;
|
||
|
|
||
|
spin_lock_irqsave(&tui_lock, fls);
|
||
|
ret_cnt = --tui_blank_cnt;
|
||
|
spin_unlock_irqrestore(&tui_lock, fls);
|
||
|
return ret_cnt;
|
||
|
}
|
||
|
EXPORT_SYMBOL(stui_dec_blank_ref);
|
||
|
|
||
|
int stui_get_blank_ref(void)
|
||
|
{
|
||
|
unsigned long fls;
|
||
|
int ret_cnt;
|
||
|
|
||
|
spin_lock_irqsave(&tui_lock, fls);
|
||
|
ret_cnt = tui_blank_cnt;
|
||
|
spin_unlock_irqrestore(&tui_lock, fls);
|
||
|
return ret_cnt;
|
||
|
}
|
||
|
EXPORT_SYMBOL(stui_get_blank_ref);
|
||
|
|
||
|
void stui_set_blank_ref(int cnt)
|
||
|
{
|
||
|
unsigned long fls;
|
||
|
|
||
|
spin_lock_irqsave(&tui_lock, fls);
|
||
|
tui_blank_cnt = cnt;
|
||
|
spin_unlock_irqrestore(&tui_lock, fls);
|
||
|
}
|
||
|
EXPORT_SYMBOL(stui_set_blank_ref);
|
||
|
|
||
|
int stui_get_mode(void)
|
||
|
{
|
||
|
unsigned long fls;
|
||
|
int ret_mode;
|
||
|
|
||
|
spin_lock_irqsave(&tui_lock, fls);
|
||
|
ret_mode = tui_mode;
|
||
|
spin_unlock_irqrestore(&tui_lock, fls);
|
||
|
return ret_mode;
|
||
|
}
|
||
|
EXPORT_SYMBOL(stui_get_mode);
|
||
|
|
||
|
void stui_set_mode(int mode)
|
||
|
{
|
||
|
unsigned long fls;
|
||
|
|
||
|
spin_lock_irqsave(&tui_lock, fls);
|
||
|
tui_mode = mode;
|
||
|
spin_unlock_irqrestore(&tui_lock, fls);
|
||
|
}
|
||
|
EXPORT_SYMBOL(stui_set_mode);
|
||
|
|
||
|
int stui_set_mask(int mask)
|
||
|
{
|
||
|
unsigned long fls;
|
||
|
int ret_mode;
|
||
|
|
||
|
spin_lock_irqsave(&tui_lock, fls);
|
||
|
ret_mode = (tui_mode |= mask);
|
||
|
spin_unlock_irqrestore(&tui_lock, fls);
|
||
|
return ret_mode;
|
||
|
}
|
||
|
EXPORT_SYMBOL(stui_set_mask);
|
||
|
|
||
|
int stui_clear_mask(int mask)
|
||
|
{
|
||
|
unsigned long fls;
|
||
|
int ret_mode;
|
||
|
|
||
|
spin_lock_irqsave(&tui_lock, fls);
|
||
|
ret_mode = (tui_mode &= ~mask);
|
||
|
spin_unlock_irqrestore(&tui_lock, fls);
|
||
|
return ret_mode;
|
||
|
}
|
||
|
EXPORT_SYMBOL(stui_clear_mask);
|
||
|
|
||
|
#ifdef USE_TEE_CLIENT_API
|
||
|
static atomic_t canceling = ATOMIC_INIT(0);
|
||
|
int stui_cancel_session(void)
|
||
|
{
|
||
|
TEEC_Context context;
|
||
|
TEEC_Session session;
|
||
|
TEEC_Result result;
|
||
|
TEEC_Operation operation;
|
||
|
int ret = -1;
|
||
|
int count = 0;
|
||
|
|
||
|
pr_info("[STUI] %s begin\n", __func__);
|
||
|
|
||
|
if (!(STUI_MODE_ALL & stui_get_mode())) {
|
||
|
pr_info("[STUI] session cancel is not needed\n");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
if (atomic_cmpxchg(&canceling, 0, 1) != 0) {
|
||
|
pr_info("[STUI] already canceling.\n");
|
||
|
|
||
|
while ((STUI_MODE_ALL & stui_get_mode()) && (count < MAX_WAIT_CNT)) {
|
||
|
msleep(SESSION_CANCEL_DELAY);
|
||
|
count++;
|
||
|
}
|
||
|
|
||
|
if (STUI_MODE_ALL & stui_get_mode()) {
|
||
|
pr_err("[STUI] session was not cancelled yet\n");
|
||
|
} else {
|
||
|
pr_info("[STUI] session was cancelled successfully\n");
|
||
|
ret = 0;
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
result = TEEC_InitializeContext(NULL, &context);
|
||
|
if (result != TEEC_SUCCESS) {
|
||
|
pr_err("[STUI] TEEC_InitializeContext returned: 0x%x\n", result);
|
||
|
goto out;
|
||
|
}
|
||
|
|
||
|
result = TEEC_OpenSession(&context, &session, &uuid, TEEC_LOGIN_PUBLIC, NULL, NULL, NULL);
|
||
|
if (result != TEEC_SUCCESS) {
|
||
|
pr_err("[STUI] TEEC_OpenSession returned: 0x%x\n", result);
|
||
|
goto finalize_context;
|
||
|
}
|
||
|
|
||
|
operation.paramTypes = TEEC_PARAM_TYPES(TEEC_NONE, TEEC_NONE, TEEC_NONE, TEEC_NONE);
|
||
|
|
||
|
result = TEEC_InvokeCommand(&session, TUI_REE_EXTERNAL_EVENT, &operation, NULL);
|
||
|
if (result != TEEC_SUCCESS) {
|
||
|
pr_err("[STUI] TEEC_InvokeCommand returned: 0x%x\n", result);
|
||
|
goto close_session;
|
||
|
} else {
|
||
|
pr_info("[STUI] invoked cancel cmd\n");
|
||
|
}
|
||
|
|
||
|
TEEC_CloseSession(&session);
|
||
|
TEEC_FinalizeContext(&context);
|
||
|
|
||
|
while ((STUI_MODE_ALL & stui_get_mode()) && (count < MAX_WAIT_CNT)) {
|
||
|
msleep(SESSION_CANCEL_DELAY);
|
||
|
count++;
|
||
|
}
|
||
|
|
||
|
if (STUI_MODE_ALL & stui_get_mode()) {
|
||
|
pr_err("[STUI] session was not cancelled yet\n");
|
||
|
} else {
|
||
|
pr_info("[STUI] session was cancelled successfully\n");
|
||
|
ret = 0;
|
||
|
}
|
||
|
|
||
|
atomic_set(&canceling, 0);
|
||
|
return ret;
|
||
|
|
||
|
close_session:
|
||
|
TEEC_CloseSession(&session);
|
||
|
finalize_context:
|
||
|
TEEC_FinalizeContext(&context);
|
||
|
out:
|
||
|
atomic_set(&canceling, 0);
|
||
|
pr_err("[STUI] %s end, ret=%d, result=0x%x\n", __func__, ret, result);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
#else /* USE_TEE_CLIENT_API */
|
||
|
int stui_cancel_session(void)
|
||
|
{
|
||
|
pr_err("[STUI] %s not supported\n", __func__);
|
||
|
return -1;
|
||
|
}
|
||
|
#endif /* USE_TEE_CLIENT_API */
|
||
|
|
||
|
#ifdef CONFIG_SOC_EXYNOS3250
|
||
|
|
||
|
static int tui_mode_3250 = STUI_MODE_OFF;
|
||
|
static BLOCKING_NOTIFIER_HEAD(trustedui_nb_list);
|
||
|
|
||
|
int trustedui_nb_register(struct notifier_block *nb)
|
||
|
{
|
||
|
return blocking_notifier_chain_register(&trustedui_nb_list, nb);
|
||
|
}
|
||
|
|
||
|
int trustedui_nb_unregister(struct notifier_block *nb)
|
||
|
{
|
||
|
return blocking_notifier_chain_unregister(&trustedui_nb_list, nb);
|
||
|
}
|
||
|
|
||
|
int trustedui_nb_send_event(unsigned long val, void *v)
|
||
|
{
|
||
|
pr_info("%s:cmd = %ld mode = %d\n", __func__, val, (*(int *)v));
|
||
|
return blocking_notifier_call_chain(&trustedui_nb_list, val, v);
|
||
|
}
|
||
|
|
||
|
void trustedui_set_mode(int mode)
|
||
|
{
|
||
|
unsigned long flags;
|
||
|
|
||
|
spin_lock_irqsave(&tui_lock, flags);
|
||
|
if (tui_mode_3250 == mode) {
|
||
|
spin_unlock_irqrestore(&tui_lock, flags);
|
||
|
return;
|
||
|
}
|
||
|
pr_info("%s:mode[%d->%d]\n", __func__, tui_mode_3250, mode);
|
||
|
tui_mode_3250 = mode;
|
||
|
spin_unlock_irqrestore(&tui_lock, flags);
|
||
|
trustedui_nb_send_event(TRUSTEDUI_MODE_CHANGE, (void *)&mode);
|
||
|
}
|
||
|
EXPORT_SYMBOL(trustedui_set_mode);
|
||
|
|
||
|
#endif /* CONFIG_SOC_EXYNOS3250 */
|
||
|
|
||
|
static int stui_notifier_callback(struct notifier_block *self, unsigned long event, void *data);
|
||
|
|
||
|
static struct notifier_block stui_fb_notif = {
|
||
|
.notifier_call = stui_notifier_callback,
|
||
|
};
|
||
|
|
||
|
static struct notifier_block stui_reboot_nb = {
|
||
|
.notifier_call = stui_notifier_callback,
|
||
|
};
|
||
|
|
||
|
static int stui_is_decon_in_state_tui(const struct fb_info *const info)
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int stui_notifier_callback(struct notifier_block *self, unsigned long event, void *data)
|
||
|
{
|
||
|
(void) data;
|
||
|
|
||
|
pr_info("[STUI] %s (start): event=%lu\n", __func__, event);
|
||
|
|
||
|
if (!(self == &stui_fb_notif && event == FB_EARLY_EVENT_BLANK)
|
||
|
&& self != &stui_reboot_nb) {
|
||
|
return NOTIFY_OK;
|
||
|
}
|
||
|
|
||
|
if (self == &stui_fb_notif && data != NULL) {
|
||
|
const struct fb_event *event_data = data;
|
||
|
const struct fb_info *info = event_data->info;
|
||
|
|
||
|
if (stui_is_decon_in_state_tui(info) == 0) {
|
||
|
pr_err("[STUI] %s is not state TUI\n", __func__);
|
||
|
return NOTIFY_OK;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
stui_cancel_session();
|
||
|
pr_info("[STUI] %s (finish)\n", __func__);
|
||
|
return NOTIFY_OK;
|
||
|
}
|
||
|
|
||
|
int stui_register_on_events(void)
|
||
|
{
|
||
|
int ret = -1;
|
||
|
pr_info("[STUI] %s (start)\n", __func__);
|
||
|
ret = fb_register_client(&stui_fb_notif);
|
||
|
if (ret != 0) {
|
||
|
pr_err("[STUI] Unable to register fb notifier (%d)\n", ret);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
ret = register_reboot_notifier(&stui_reboot_nb);
|
||
|
if (ret != 0) {
|
||
|
pr_err("[STUI] Unable to register reboot notifier (%d)\n", ret);
|
||
|
fb_unregister_client(&stui_fb_notif);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
pr_info("[STUI] %s (finish)\n", __func__);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
void stui_unregister_from_events(void)
|
||
|
{
|
||
|
pr_info("[STUI] %s (start)\n", __func__);
|
||
|
fb_unregister_client(&stui_fb_notif);
|
||
|
unregister_reboot_notifier(&stui_reboot_nb);
|
||
|
pr_info("[STUI] %s (finish)\n", __func__);
|
||
|
}
|