// SPDX-License-Identifier: GPL-2.0+
/*
* ipmi_devintf.c
*
* Linux device interface for the IPMI message handler.
*
* Author: MontaVista Software, Inc.
* Corey Minyard <minyard@mvista.com>
* source@mvista.com
*
* Copyright 2002 MontaVista Software Inc.
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/errno.h>
#include <linux/poll.h>
#include <linux/sched.h>
#include <linux/spinlock.h>
#include <linux/slab.h>
#include <linux/ipmi.h>
#include <linux/mutex.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/compat.h>
struct ipmi_file_private
{
struct ipmi_user *user;
spinlock_t recv_msg_lock;
struct list_head recv_msgs;
struct fasync_struct *fasync_queue;
wait_queue_head_t wait;
struct mutex recv_mutex;
int default_retries;
unsigned int default_retry_time_ms;
};
static void file_receive_handler(struct ipmi_recv_msg *msg,
void *handler_data)
{
struct ipmi_file_private *priv = handler_data;
int was_empty;
unsigned long flags;
spin_lock_irqsave(&priv->recv_msg_lock, flags);
was_empty = list_empty(&priv->recv_msgs);
list_add_tail(&msg->link, &priv->recv_msgs);
spin_unlock_irqrestore(&priv->recv_msg_lock, flags);
if (was_empty) {
wake_up_interruptible(&priv->wait);
kill_fasync(&priv->fasync_queue, SIGIO, POLL_IN);
}
}
static __poll_t ipmi_poll(struct file *file, poll_table *wait)
{
struct ipmi_file_private *priv = file->private_data;
__poll_t mask = 0;
unsigned long flags;
poll_wait(file, &priv->wait, wait);
spin_lock_irqsave(&priv->recv_msg_lock, flags);
if (!list_empty(&priv->recv_msgs))
mask |= (EPOLLIN | EPOLLRDNORM);
spin_unlock_irqrestore(&priv->recv_msg_lock, flags);
return mask;
}
static int ipmi_fasync(int fd, struct file *file, int on)
{
struct ipmi_file_private *priv = file->private_data;
return fasync_helper(fd, file, on, &priv->fasync_queue);
}
static const struct ipmi_user_hndl ipmi_hndlrs =
{
.ipmi_recv_hndl = file_receive_handler,
};
static int ipmi_open(struct inode *inode, struct file *file)
{
int if_num = iminor(inode);
int rv;
struct ipmi_file_private *priv;
priv = kmalloc(sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
rv = ipmi_create_user(if_num,
&ipmi_hndlrs,
priv,
&priv->user);
if (rv) {
kfree(priv);
goto out;
}
file->private_data = priv;
spin_lock_init(&priv->recv_msg_lock);
INIT_LIST_HEAD(&priv->recv_msgs);
init_waitqueue_head(&priv->wait);
priv->fasync_queue = NULL;
mutex_init(&priv->recv_mutex);
/* Use the low-level defaults. */
priv->default_retries = -1;
priv->default_retry_time_ms = 0;
out:
return rv;
}
static int ipmi_release(struct inode *inode, struct file *file)
{
struct ipmi_file_private *priv = file->private_data;
int rv;
struct ipmi_recv_msg *msg, *next;
rv = ipmi_destroy_user(priv->user);
if (rv)
return rv;
list_for_each_entry_safe(msg, next, &priv->recv_msgs, link)
ipmi_free_recv_msg(msg);
kfree(priv);
return 0;
}
static int handle_send_req(struct ipmi_user *user,
struct ipmi_req *req,
int retries,
unsigned int retry_time_ms)
{
int rv;
struct ipmi_addr addr;
struct kernel_ipmi_msg msg;
if (req->addr_len > sizeof(struct ipmi_addr))
return -EINVAL;
if (copy_from_user(&addr, req->addr, req->addr_len))
return -EFAULT;
msg.netfn = req->msg.netfn;
msg.cmd = req->msg.cmd;
msg.data_len = req->msg.data_len;
msg.data = kmalloc(IPMI_MAX_MSG_LENGTH, GFP_KERNEL);
if (!msg.data)
return -ENOMEM;
/* From here out we cannot return, we must jump to "out" for
error exits to free msgdata. */
rv = ipmi_validate_addr(&addr, req->addr_len);
if (rv)
goto out;
if (req->msg.data != NULL) {