365 lines
9.0 KiB
C
Executable File
365 lines
9.0 KiB
C
Executable File
/*
|
|
* Copyright (C) 2012-2018, Samsung Electronics Co., Ltd.
|
|
*
|
|
* This software is licensed under the terms of the GNU General Public
|
|
* License version 2, as published by the Free Software Foundation, and
|
|
* may be copied, distributed, and modified under those terms.
|
|
*
|
|
* 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/anon_inodes.h>
|
|
#include <linux/module.h>
|
|
#include <linux/poll.h>
|
|
|
|
#include "tzdev.h"
|
|
#include "tz_iwsock.h"
|
|
|
|
#define IS_EINTR(x) \
|
|
(((x) == -EINTR) \
|
|
|| ((x) == -ERESTARTSYS) \
|
|
|| ((x) == -ERESTARTNOHAND) \
|
|
|| ((x) == -ERESTARTNOINTR) \
|
|
|| ((x) == -ERESTART_RESTARTBLOCK))
|
|
|
|
static const struct file_operations tz_uiwsock_fops;
|
|
|
|
static int tz_uiwsock_open(struct inode *inode, struct file *filp)
|
|
{
|
|
struct sock_desc *sd;
|
|
|
|
sd = tz_iwsock_socket(0);
|
|
if (IS_ERR(sd)) {
|
|
tzdev_uiwsock_error("Failed to create new socket, ret=%ld\n", PTR_ERR(sd));
|
|
return PTR_ERR(sd);
|
|
}
|
|
|
|
filp->private_data = sd;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static long tz_uiwsock_connect(struct file *filp, unsigned long arg)
|
|
{
|
|
long ret;
|
|
struct tz_uiwsock_connection connection;
|
|
struct sock_desc *sd = filp->private_data;
|
|
struct tz_uiwsock_connection __user *argp = (struct tz_uiwsock_connection __user *)arg;
|
|
|
|
tzdev_uiwsock_debug("Enter, filp=%pK\n", filp);
|
|
|
|
if (copy_from_user(&connection, argp, sizeof(struct tz_uiwsock_connection))) {
|
|
tzdev_uiwsock_error("Invalid user space pointer %pK, filp=%pK\n", argp, filp);
|
|
return -EFAULT;
|
|
}
|
|
|
|
connection.name[TZ_UIWSOCK_MAX_NAME_LENGTH - 1] = 0;
|
|
|
|
ret = tz_iwsock_connect(sd, connection.name, 0);
|
|
if (ret < 0) {
|
|
tzdev_uiwsock_error("Failed to connect to socket %s, filp=%pK, error %ld\n", connection.name, filp, ret);
|
|
return ret;
|
|
}
|
|
|
|
tzdev_uiwsock_debug("Exit, filp=%pK\n", filp);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static long tz_uiwsock_wait_connection(struct file *filp, unsigned long arg)
|
|
{
|
|
long ret;
|
|
struct sock_desc *sd = (struct sock_desc *)filp->private_data;
|
|
|
|
ret = tz_iwsock_wait_connection(sd);
|
|
if (IS_EINTR(ret))
|
|
tzdev_uiwsock_debug("Wait interrupted, filp=%pK\n", filp);
|
|
else if (ret < 0)
|
|
tzdev_uiwsock_error("Failed to wait connection to socket, filp=%pK socket=%d ret=%ld\n",
|
|
filp, sd->id, ret);
|
|
|
|
tzdev_uiwsock_debug("Exit, filp=%pK\n", filp);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static long tz_uiwsock_listen(struct file *filp, unsigned long arg)
|
|
{
|
|
long ret;
|
|
struct tz_uiwsock_connection connection;
|
|
struct sock_desc *sd = (struct sock_desc *)filp->private_data;
|
|
struct tz_uiwsock_connection __user *argp =
|
|
(struct tz_uiwsock_connection __user *)arg;
|
|
|
|
tzdev_uiwsock_debug("Enter, filp=%pK\n", filp);
|
|
|
|
if (copy_from_user(&connection, argp, sizeof(struct tz_uiwsock_connection))) {
|
|
tzdev_uiwsock_error("Invalid user space pointer %pK, filp=%pK\n",
|
|
argp, filp);
|
|
return -EFAULT;
|
|
}
|
|
|
|
|
|
connection.name[TZ_UIWSOCK_MAX_NAME_LENGTH - 1] = 0;
|
|
|
|
ret = tz_iwsock_listen(sd, connection.name);
|
|
if (ret) {
|
|
tzdev_uiwsock_error("Failed to listen, error %ld\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
tzdev_uiwsock_debug("Exit, filp=%pK\n", filp);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static long tz_uiwsock_accept(struct file *filp, unsigned long arg)
|
|
{
|
|
struct sock_desc *sd = filp->private_data;
|
|
struct sock_desc *new_sd;
|
|
long ret;
|
|
|
|
tzdev_uiwsock_debug("Enter, filp=%pK\n", filp);
|
|
|
|
new_sd = tz_iwsock_accept(sd);
|
|
if (IS_ERR(new_sd)) {
|
|
tzdev_uiwsock_error("Failed to accept, error %ld\n", PTR_ERR(new_sd));
|
|
return PTR_ERR(new_sd);
|
|
}
|
|
|
|
ret = anon_inode_getfd("[tz-uiwsock]", &tz_uiwsock_fops, new_sd, O_CLOEXEC);
|
|
if (ret < 0) {
|
|
tzdev_uiwsock_error("Failed to get new anon inode fd, ret=%ld\n", ret);
|
|
tz_iwsock_release(new_sd);
|
|
}
|
|
|
|
tzdev_uiwsock_debug("Exit, filp=%pK\n", filp);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static long tz_uiwsock_send(struct file *filp, unsigned long arg)
|
|
{
|
|
struct tz_uiwsock_data data;
|
|
struct sock_desc *sd = (struct sock_desc *)filp->private_data;
|
|
struct tz_uiwsock_data __user *argp = (struct tz_uiwsock_data __user *)arg;
|
|
long ret;
|
|
|
|
tzdev_uiwsock_debug("Enter, filp=%pK\n", filp);
|
|
|
|
if (copy_from_user(&data, argp, sizeof(struct tz_uiwsock_data))) {
|
|
tzdev_uiwsock_error("Invalid user space pointer %pK, filp=%pK\n", argp, filp);
|
|
return -EFAULT;
|
|
}
|
|
|
|
ret = tz_iwsock_write(sd, (void __user *)(uintptr_t)data.buffer,
|
|
data.size, data.flags);
|
|
|
|
tzdev_uiwsock_debug("Exit, filp=%pK\n", filp);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static long tz_uiwsock_recv(struct file *filp, unsigned long arg)
|
|
{
|
|
struct tz_uiwsock_data data;
|
|
struct sock_desc *sd = (struct sock_desc *)filp->private_data;
|
|
struct tz_uiwsock_data __user *argp = (struct tz_uiwsock_data __user *)arg;
|
|
long ret;
|
|
|
|
tzdev_uiwsock_debug("Enter, filp=%pK\n", filp);
|
|
|
|
if (copy_from_user(&data, argp, sizeof(struct tz_uiwsock_data))) {
|
|
tzdev_uiwsock_error("Invalid user space pointer %pK, filp=%pK\n", argp, filp);
|
|
return -EFAULT;
|
|
}
|
|
|
|
ret = tz_iwsock_read(sd, (void __user *)(uintptr_t)data.buffer,
|
|
data.size, data.flags);
|
|
if (ret < 0)
|
|
tzdev_uiwsock_error("Failed to read data ret=%ld, filp=%pK\n", ret, filp);
|
|
|
|
tzdev_uiwsock_debug("Exit, filp=%pK\n", filp);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static long tz_uiwsock_getsockopt(struct file *filp, unsigned long arg)
|
|
{
|
|
struct sock_desc *sd = (struct sock_desc *)filp->private_data;
|
|
struct tz_uiwsock_sockopt __user *argp = (struct tz_uiwsock_sockopt __user *)arg;
|
|
struct tz_uiwsock_sockopt sockopt;
|
|
int ret;
|
|
|
|
tzdev_uiwsock_debug("Enter, filp=%pK\n", filp);
|
|
|
|
if (copy_from_user(&sockopt, argp, sizeof(struct tz_uiwsock_sockopt))) {
|
|
tzdev_uiwsock_error("Invalid user space pointer %pK, filp=%pK\n",
|
|
argp, filp);
|
|
return -EFAULT;
|
|
}
|
|
|
|
ret = tz_iwsock_getsockopt(sd, sockopt.level, sockopt.optname,
|
|
(void *)(uintptr_t)sockopt.optval,
|
|
(socklen_t *)(uintptr_t)sockopt.optlen);
|
|
|
|
tzdev_uiwsock_debug("Exit, filp=%pK\n", filp);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static long tz_uiwsock_setsockopt(struct file *filp, unsigned long arg)
|
|
{
|
|
struct sock_desc *sd = (struct sock_desc *)filp->private_data;
|
|
struct tz_uiwsock_sockopt sockopt;
|
|
struct tz_uiwsock_sockopt __user *argp = (struct tz_uiwsock_sockopt __user *)arg;
|
|
int ret;
|
|
|
|
tzdev_uiwsock_debug("Enter, filp=%pK\n", filp);
|
|
|
|
if (copy_from_user(&sockopt, argp, sizeof(struct tz_uiwsock_sockopt))) {
|
|
tzdev_uiwsock_error("Invalid user space pointer %pK, filp=%pK\n",
|
|
argp, filp);
|
|
return -EFAULT;
|
|
}
|
|
|
|
ret = tz_iwsock_setsockopt(sd, sockopt.level, sockopt.optname,
|
|
(void *)(uintptr_t)sockopt.optval, sockopt.optlen);
|
|
|
|
tzdev_uiwsock_debug("Exit, filp=%pK\n", filp);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static long tz_uiwsock_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
|
{
|
|
long ret;
|
|
|
|
switch (cmd) {
|
|
case TZIO_UIWSOCK_CONNECT:
|
|
ret = tz_uiwsock_connect(filp, arg);
|
|
break;
|
|
case TZIO_UIWSOCK_WAIT_CONNECTION:
|
|
ret = tz_uiwsock_wait_connection(filp, arg);
|
|
break;
|
|
case TZIO_UIWSOCK_SEND:
|
|
ret = tz_uiwsock_send(filp, arg);
|
|
break;
|
|
case TZIO_UIWSOCK_RECV:
|
|
ret = tz_uiwsock_recv(filp, arg);
|
|
break;
|
|
case TZIO_UIWSOCK_LISTEN:
|
|
ret = tz_uiwsock_listen(filp, arg);
|
|
break;
|
|
case TZIO_UIWSOCK_ACCEPT:
|
|
ret = tz_uiwsock_accept(filp, arg);
|
|
break;
|
|
case TZIO_UIWSOCK_GETSOCKOPT:
|
|
ret = tz_uiwsock_getsockopt(filp, arg);
|
|
break;
|
|
case TZIO_UIWSOCK_SETSOCKOPT:
|
|
ret = tz_uiwsock_setsockopt(filp, arg);
|
|
break;
|
|
default:
|
|
ret = -ENOTTY;
|
|
break;
|
|
}
|
|
|
|
if (IS_EINTR(ret))
|
|
ret = -ERESTARTNOINTR;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int tz_uiwsock_release(struct inode *inode, struct file *filp)
|
|
{
|
|
struct sock_desc *sd = (struct sock_desc *)filp->private_data;
|
|
(void)inode;
|
|
|
|
tzdev_uiwsock_debug("Enter, filp=%pK\n", filp);
|
|
|
|
tz_iwsock_release(sd);
|
|
|
|
tzdev_uiwsock_debug("Exit, filp=%pK\n", filp);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static unsigned int tz_uiwsock_poll(struct file *filp, poll_table *wait)
|
|
{
|
|
unsigned int mask = 0;
|
|
struct sock_desc *sd = filp->private_data;
|
|
|
|
poll_wait(filp, &sd->wq, wait);
|
|
|
|
if (sd->state != TZ_SK_CONNECTED && sd->state != TZ_SK_LISTENING)
|
|
return 0;
|
|
|
|
switch (sd->iwd_buf->nwd_state) {
|
|
case BUF_SK_NEW:
|
|
break;
|
|
case BUF_SK_CONNECTED:
|
|
if (!circ_buf_is_empty(&sd->read_buf))
|
|
mask |= POLLIN | POLLRDNORM;
|
|
|
|
switch(sd->iwd_buf->swd_state) {
|
|
case BUF_SK_NEW:
|
|
break;
|
|
case BUF_SK_CLOSED:
|
|
mask |= POLLHUP;
|
|
break;
|
|
case BUF_SK_CONNECTED:
|
|
if (!circ_buf_is_full(&sd->write_buf))
|
|
mask |= POLLOUT | POLLWRNORM;
|
|
|
|
if (!circ_buf_is_full(&sd->oob_buf))
|
|
mask |= POLLOUT | POLLWRBAND;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case BUF_SK_CLOSED:
|
|
BUG();
|
|
}
|
|
|
|
return mask;
|
|
}
|
|
|
|
static const struct file_operations tz_uiwsock_fops = {
|
|
.owner = THIS_MODULE,
|
|
.open = tz_uiwsock_open,
|
|
.poll = tz_uiwsock_poll,
|
|
.unlocked_ioctl = tz_uiwsock_ioctl,
|
|
#ifdef CONFIG_COMPAT
|
|
.compat_ioctl = tz_uiwsock_ioctl,
|
|
#endif /* CONFIG_COMPAT */
|
|
.release = tz_uiwsock_release,
|
|
};
|
|
|
|
static struct tz_cdev tz_uiwsock_cdev = {
|
|
.name = "tziwsock",
|
|
.fops = &tz_uiwsock_fops,
|
|
.owner = THIS_MODULE,
|
|
};
|
|
|
|
int tz_uiwsock_init(void)
|
|
{
|
|
int ret;
|
|
|
|
ret = tz_cdev_register(&tz_uiwsock_cdev);
|
|
if (ret) {
|
|
tzdev_print(0, "failed to register iwsock device, error=%d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void tz_uiwsock_fini(void)
|
|
{
|
|
tz_cdev_unregister(&tz_uiwsock_cdev);
|
|
}
|