blob: e8ed0ff117392db061e0080c0a6a745ae0e9d4b7 [file] [log] [blame]
/*
* Copyright (c) 2013-2016 TRUSTONIC LIMITED
* 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.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/ioctl.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/completion.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/switch.h>
#include "tui_ioctl.h"
#include "tlcTui.h"
#include "mobicore_driver_api.h"
#include "dciTui.h"
#include "tui-hal.h"
#include "build_tag.h"
/*static int tui_dev_major_number = 122; */
/*module_param(tui_dev_major_number, int, 0000); */
/*MODULE_PARM_DESC(major, */
/* "The device major number used to register a unique char device driver"); */
/* Static variables */
static struct cdev tui_cdev;
struct switch_dev tui_switch;
int tui_force_close(uint32_t arg)
{
int ret = 0;
pr_info("Force TUI_IO_NOTIFY %d\n", arg);
if (tlc_notify_event(arg))
ret = 0;
else
ret = -EFAULT;
return ret;
}
EXPORT_SYMBOL(tui_force_close);
void tui_cover_mode_set (bool arg)
{
pr_info("tui cover mode set to %d\n", arg);
tui_cover_mode_on = arg;
}
EXPORT_SYMBOL(tui_cover_mode_set);
static long tui_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
{
int ret = -ENOTTY;
int __user *uarg = (int __user *)arg;
if (_IOC_TYPE(cmd) != TUI_IO_MAGIC)
return -EINVAL;
pr_info("t-base-tui module: ioctl 0x%x ", cmd);
switch (cmd) {
case TUI_IO_SET_RESOLUTION:
pr_info("TLC_TUI_CMD_SET_RESOLUTION\n");
/* NOT IMPLEMENTED */
ret = 0;
break;
case TUI_IO_NOTIFY:
pr_info("TUI_IO_NOTIFY\n");
if (tlc_notify_event(arg))
ret = 0;
else
ret = -EFAULT;
break;
case TUI_IO_WAITCMD: {
struct tlc_tui_command_t tui_cmd = {0};
pr_info("TUI_IO_WAITCMD\n");
ret = tlc_wait_cmd(&tui_cmd);
if (ret) {
pr_debug("ERROR %s:%d tlc_wait_cmd returned (0x%08X)\n",
__func__, __LINE__, ret);
return ret;
}
/* Write command id to user */
pr_debug("IOCTL: sending command %d to user.\n", tui_cmd.id);
if (copy_to_user(uarg, &tui_cmd, sizeof(
struct tlc_tui_command_t)))
ret = -EFAULT;
else
ret = 0;
break;
}
case TUI_IO_ACK: {
struct tlc_tui_response_t rsp_id;
pr_info("TUI_IO_ACK\n");
/* Read user response */
if (copy_from_user(&rsp_id, uarg, sizeof(rsp_id)))
ret = -EFAULT;
else
ret = 0;
pr_debug("IOCTL: User completed command %d.\n", rsp_id.id);
ret = tlc_ack_cmd(&rsp_id);
if (ret)
return ret;
break;
}
case TUI_IO_INIT_DRIVER: {
pr_info("TUI_IO_INIT_DRIVER\n");
ret = tlc_init_driver();
if (ret) {
pr_debug("ERROR %s:%d tlc_init_driver returned (0x%08X)\n",
__func__, __LINE__, ret);
return ret;
}
break;
}
default:
pr_info("ERROR %s:%d Unknown ioctl (%u)!\n", __func__,
__LINE__, cmd);
return -ENOTTY;
}
return ret;
}
atomic_t fileopened;
static int tui_open(struct inode *inode, struct file *file)
{
pr_info("TUI file opened\n");
atomic_inc(&fileopened);
return 0;
}
static int tui_release(struct inode *inode, struct file *file)
{
pr_info("TUI file closed\n");
if (atomic_dec_and_test(&fileopened))
tlc_notify_event(NOT_TUI_CANCEL_EVENT);
return 0;
}
static const struct file_operations tui_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = tui_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = tui_ioctl,
#endif
.open = tui_open,
.release = tui_release
};
/*--------------------------------------------------------------------------- */
static int __init tlc_tui_init(void)
{
pr_info("Loading t-base-tui module.\n");
pr_debug("\n=============== Running TUI Kernel TLC ===============\n");
pr_info("%s\n", MOBICORE_COMPONENT_BUILD_TAG);
dev_t devno;
int err;
static struct class *tui_class;
atomic_set(&fileopened, 0);
err = alloc_chrdev_region(&devno, 0, 1, TUI_DEV_NAME);
if (err) {
pr_debug("Unable to allocate Trusted UI device number\n");
return err;
}
cdev_init(&tui_cdev, &tui_fops);
tui_cdev.owner = THIS_MODULE;
/* tui_cdev.ops = &tui_fops; */
err = cdev_add(&tui_cdev, devno, 1);
if (err) {
pr_debug("Unable to add Trusted UI char device\n");
unregister_chrdev_region(devno, 1);
return err;
}
tui_class = class_create(THIS_MODULE, "tui_cls");
if (IS_ERR(tui_class)) {
pr_debug(KERN_ERR "Failed to create tui class.\n");
unregister_chrdev_region(devno, 1);
cdev_del(&tui_cdev);
return -1;
}
device_create(tui_class, NULL, devno, NULL, TUI_DEV_NAME);
if (!hal_tui_init())
return -EPERM;
/* register the switch device for tui */
tui_switch.name = "tui";
err = switch_dev_register(&tui_switch);
if (err)
pr_debug(KERN_ERR "Failed to register tui_switch.\n");
return 0;
}
static void __exit tlc_tui_exit(void)
{
pr_info("Unloading t-base-tui module.\n");
switch_dev_unregister(&tui_switch);
unregister_chrdev_region(tui_cdev.dev, 1);
cdev_del(&tui_cdev);
hal_tui_exit();
}
module_init(tlc_tui_init);
module_exit(tlc_tui_exit);
MODULE_AUTHOR("Trustonic Limited");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Kinibi TUI");