162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * ipmi_devintf.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Linux device interface for the IPMI message handler.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Author: MontaVista Software, Inc.
862306a36Sopenharmony_ci *         Corey Minyard <minyard@mvista.com>
962306a36Sopenharmony_ci *         source@mvista.com
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci * Copyright 2002 MontaVista Software Inc.
1262306a36Sopenharmony_ci */
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include <linux/module.h>
1562306a36Sopenharmony_ci#include <linux/moduleparam.h>
1662306a36Sopenharmony_ci#include <linux/errno.h>
1762306a36Sopenharmony_ci#include <linux/poll.h>
1862306a36Sopenharmony_ci#include <linux/sched.h>
1962306a36Sopenharmony_ci#include <linux/spinlock.h>
2062306a36Sopenharmony_ci#include <linux/slab.h>
2162306a36Sopenharmony_ci#include <linux/ipmi.h>
2262306a36Sopenharmony_ci#include <linux/mutex.h>
2362306a36Sopenharmony_ci#include <linux/init.h>
2462306a36Sopenharmony_ci#include <linux/device.h>
2562306a36Sopenharmony_ci#include <linux/compat.h>
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistruct ipmi_file_private
2862306a36Sopenharmony_ci{
2962306a36Sopenharmony_ci	struct ipmi_user     *user;
3062306a36Sopenharmony_ci	spinlock_t           recv_msg_lock;
3162306a36Sopenharmony_ci	struct list_head     recv_msgs;
3262306a36Sopenharmony_ci	struct fasync_struct *fasync_queue;
3362306a36Sopenharmony_ci	wait_queue_head_t    wait;
3462306a36Sopenharmony_ci	struct mutex	     recv_mutex;
3562306a36Sopenharmony_ci	int                  default_retries;
3662306a36Sopenharmony_ci	unsigned int         default_retry_time_ms;
3762306a36Sopenharmony_ci};
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistatic void file_receive_handler(struct ipmi_recv_msg *msg,
4062306a36Sopenharmony_ci				 void                 *handler_data)
4162306a36Sopenharmony_ci{
4262306a36Sopenharmony_ci	struct ipmi_file_private *priv = handler_data;
4362306a36Sopenharmony_ci	int                      was_empty;
4462306a36Sopenharmony_ci	unsigned long            flags;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	spin_lock_irqsave(&priv->recv_msg_lock, flags);
4762306a36Sopenharmony_ci	was_empty = list_empty(&priv->recv_msgs);
4862306a36Sopenharmony_ci	list_add_tail(&msg->link, &priv->recv_msgs);
4962306a36Sopenharmony_ci	spin_unlock_irqrestore(&priv->recv_msg_lock, flags);
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	if (was_empty) {
5262306a36Sopenharmony_ci		wake_up_interruptible(&priv->wait);
5362306a36Sopenharmony_ci		kill_fasync(&priv->fasync_queue, SIGIO, POLL_IN);
5462306a36Sopenharmony_ci	}
5562306a36Sopenharmony_ci}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistatic __poll_t ipmi_poll(struct file *file, poll_table *wait)
5862306a36Sopenharmony_ci{
5962306a36Sopenharmony_ci	struct ipmi_file_private *priv = file->private_data;
6062306a36Sopenharmony_ci	__poll_t             mask = 0;
6162306a36Sopenharmony_ci	unsigned long            flags;
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	poll_wait(file, &priv->wait, wait);
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	spin_lock_irqsave(&priv->recv_msg_lock, flags);
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	if (!list_empty(&priv->recv_msgs))
6862306a36Sopenharmony_ci		mask |= (EPOLLIN | EPOLLRDNORM);
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	spin_unlock_irqrestore(&priv->recv_msg_lock, flags);
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	return mask;
7362306a36Sopenharmony_ci}
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cistatic int ipmi_fasync(int fd, struct file *file, int on)
7662306a36Sopenharmony_ci{
7762306a36Sopenharmony_ci	struct ipmi_file_private *priv = file->private_data;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	return fasync_helper(fd, file, on, &priv->fasync_queue);
8062306a36Sopenharmony_ci}
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_cistatic const struct ipmi_user_hndl ipmi_hndlrs =
8362306a36Sopenharmony_ci{
8462306a36Sopenharmony_ci	.ipmi_recv_hndl	= file_receive_handler,
8562306a36Sopenharmony_ci};
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cistatic int ipmi_open(struct inode *inode, struct file *file)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	int                      if_num = iminor(inode);
9062306a36Sopenharmony_ci	int                      rv;
9162306a36Sopenharmony_ci	struct ipmi_file_private *priv;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	priv = kmalloc(sizeof(*priv), GFP_KERNEL);
9462306a36Sopenharmony_ci	if (!priv)
9562306a36Sopenharmony_ci		return -ENOMEM;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	rv = ipmi_create_user(if_num,
9862306a36Sopenharmony_ci			      &ipmi_hndlrs,
9962306a36Sopenharmony_ci			      priv,
10062306a36Sopenharmony_ci			      &priv->user);
10162306a36Sopenharmony_ci	if (rv) {
10262306a36Sopenharmony_ci		kfree(priv);
10362306a36Sopenharmony_ci		goto out;
10462306a36Sopenharmony_ci	}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	file->private_data = priv;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	spin_lock_init(&priv->recv_msg_lock);
10962306a36Sopenharmony_ci	INIT_LIST_HEAD(&priv->recv_msgs);
11062306a36Sopenharmony_ci	init_waitqueue_head(&priv->wait);
11162306a36Sopenharmony_ci	priv->fasync_queue = NULL;
11262306a36Sopenharmony_ci	mutex_init(&priv->recv_mutex);
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	/* Use the low-level defaults. */
11562306a36Sopenharmony_ci	priv->default_retries = -1;
11662306a36Sopenharmony_ci	priv->default_retry_time_ms = 0;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ciout:
11962306a36Sopenharmony_ci	return rv;
12062306a36Sopenharmony_ci}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_cistatic int ipmi_release(struct inode *inode, struct file *file)
12362306a36Sopenharmony_ci{
12462306a36Sopenharmony_ci	struct ipmi_file_private *priv = file->private_data;
12562306a36Sopenharmony_ci	int                      rv;
12662306a36Sopenharmony_ci	struct ipmi_recv_msg *msg, *next;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	rv = ipmi_destroy_user(priv->user);
12962306a36Sopenharmony_ci	if (rv)
13062306a36Sopenharmony_ci		return rv;
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	list_for_each_entry_safe(msg, next, &priv->recv_msgs, link)
13362306a36Sopenharmony_ci		ipmi_free_recv_msg(msg);
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	kfree(priv);
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	return 0;
13862306a36Sopenharmony_ci}
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_cistatic int handle_send_req(struct ipmi_user *user,
14162306a36Sopenharmony_ci			   struct ipmi_req *req,
14262306a36Sopenharmony_ci			   int             retries,
14362306a36Sopenharmony_ci			   unsigned int    retry_time_ms)
14462306a36Sopenharmony_ci{
14562306a36Sopenharmony_ci	int              rv;
14662306a36Sopenharmony_ci	struct ipmi_addr addr;
14762306a36Sopenharmony_ci	struct kernel_ipmi_msg msg;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	if (req->addr_len > sizeof(struct ipmi_addr))
15062306a36Sopenharmony_ci		return -EINVAL;
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	if (copy_from_user(&addr, req->addr, req->addr_len))
15362306a36Sopenharmony_ci		return -EFAULT;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	msg.netfn = req->msg.netfn;
15662306a36Sopenharmony_ci	msg.cmd = req->msg.cmd;
15762306a36Sopenharmony_ci	msg.data_len = req->msg.data_len;
15862306a36Sopenharmony_ci	msg.data = kmalloc(IPMI_MAX_MSG_LENGTH, GFP_KERNEL);
15962306a36Sopenharmony_ci	if (!msg.data)
16062306a36Sopenharmony_ci		return -ENOMEM;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	/* From here out we cannot return, we must jump to "out" for
16362306a36Sopenharmony_ci	   error exits to free msgdata. */
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	rv = ipmi_validate_addr(&addr, req->addr_len);
16662306a36Sopenharmony_ci	if (rv)
16762306a36Sopenharmony_ci		goto out;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	if (req->msg.data != NULL) {
17062306a36Sopenharmony_ci		if (req->msg.data_len > IPMI_MAX_MSG_LENGTH) {
17162306a36Sopenharmony_ci			rv = -EMSGSIZE;
17262306a36Sopenharmony_ci			goto out;
17362306a36Sopenharmony_ci		}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci		if (copy_from_user(msg.data,
17662306a36Sopenharmony_ci				   req->msg.data,
17762306a36Sopenharmony_ci				   req->msg.data_len)) {
17862306a36Sopenharmony_ci			rv = -EFAULT;
17962306a36Sopenharmony_ci			goto out;
18062306a36Sopenharmony_ci		}
18162306a36Sopenharmony_ci	} else {
18262306a36Sopenharmony_ci		msg.data_len = 0;
18362306a36Sopenharmony_ci	}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	rv = ipmi_request_settime(user,
18662306a36Sopenharmony_ci				  &addr,
18762306a36Sopenharmony_ci				  req->msgid,
18862306a36Sopenharmony_ci				  &msg,
18962306a36Sopenharmony_ci				  NULL,
19062306a36Sopenharmony_ci				  0,
19162306a36Sopenharmony_ci				  retries,
19262306a36Sopenharmony_ci				  retry_time_ms);
19362306a36Sopenharmony_ci out:
19462306a36Sopenharmony_ci	kfree(msg.data);
19562306a36Sopenharmony_ci	return rv;
19662306a36Sopenharmony_ci}
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_cistatic int handle_recv(struct ipmi_file_private *priv,
19962306a36Sopenharmony_ci			bool trunc, struct ipmi_recv *rsp,
20062306a36Sopenharmony_ci			int (*copyout)(struct ipmi_recv *, void __user *),
20162306a36Sopenharmony_ci			void __user *to)
20262306a36Sopenharmony_ci{
20362306a36Sopenharmony_ci	int              addr_len;
20462306a36Sopenharmony_ci	struct list_head *entry;
20562306a36Sopenharmony_ci	struct ipmi_recv_msg  *msg;
20662306a36Sopenharmony_ci	unsigned long    flags;
20762306a36Sopenharmony_ci	int rv = 0, rv2 = 0;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	/* We claim a mutex because we don't want two
21062306a36Sopenharmony_ci	   users getting something from the queue at a time.
21162306a36Sopenharmony_ci	   Since we have to release the spinlock before we can
21262306a36Sopenharmony_ci	   copy the data to the user, it's possible another
21362306a36Sopenharmony_ci	   user will grab something from the queue, too.  Then
21462306a36Sopenharmony_ci	   the messages might get out of order if something
21562306a36Sopenharmony_ci	   fails and the message gets put back onto the
21662306a36Sopenharmony_ci	   queue.  This mutex prevents that problem. */
21762306a36Sopenharmony_ci	mutex_lock(&priv->recv_mutex);
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	/* Grab the message off the list. */
22062306a36Sopenharmony_ci	spin_lock_irqsave(&priv->recv_msg_lock, flags);
22162306a36Sopenharmony_ci	if (list_empty(&(priv->recv_msgs))) {
22262306a36Sopenharmony_ci		spin_unlock_irqrestore(&priv->recv_msg_lock, flags);
22362306a36Sopenharmony_ci		rv = -EAGAIN;
22462306a36Sopenharmony_ci		goto recv_err;
22562306a36Sopenharmony_ci	}
22662306a36Sopenharmony_ci	entry = priv->recv_msgs.next;
22762306a36Sopenharmony_ci	msg = list_entry(entry, struct ipmi_recv_msg, link);
22862306a36Sopenharmony_ci	list_del(entry);
22962306a36Sopenharmony_ci	spin_unlock_irqrestore(&priv->recv_msg_lock, flags);
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	addr_len = ipmi_addr_length(msg->addr.addr_type);
23262306a36Sopenharmony_ci	if (rsp->addr_len < addr_len) {
23362306a36Sopenharmony_ci		rv = -EINVAL;
23462306a36Sopenharmony_ci		goto recv_putback_on_err;
23562306a36Sopenharmony_ci	}
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	if (copy_to_user(rsp->addr, &msg->addr, addr_len)) {
23862306a36Sopenharmony_ci		rv = -EFAULT;
23962306a36Sopenharmony_ci		goto recv_putback_on_err;
24062306a36Sopenharmony_ci	}
24162306a36Sopenharmony_ci	rsp->addr_len = addr_len;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	rsp->recv_type = msg->recv_type;
24462306a36Sopenharmony_ci	rsp->msgid = msg->msgid;
24562306a36Sopenharmony_ci	rsp->msg.netfn = msg->msg.netfn;
24662306a36Sopenharmony_ci	rsp->msg.cmd = msg->msg.cmd;
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	if (msg->msg.data_len > 0) {
24962306a36Sopenharmony_ci		if (rsp->msg.data_len < msg->msg.data_len) {
25062306a36Sopenharmony_ci			if (trunc) {
25162306a36Sopenharmony_ci				rv2 = -EMSGSIZE;
25262306a36Sopenharmony_ci				msg->msg.data_len = rsp->msg.data_len;
25362306a36Sopenharmony_ci			} else {
25462306a36Sopenharmony_ci				rv = -EMSGSIZE;
25562306a36Sopenharmony_ci				goto recv_putback_on_err;
25662306a36Sopenharmony_ci			}
25762306a36Sopenharmony_ci		}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci		if (copy_to_user(rsp->msg.data,
26062306a36Sopenharmony_ci				 msg->msg.data,
26162306a36Sopenharmony_ci				 msg->msg.data_len)) {
26262306a36Sopenharmony_ci			rv = -EFAULT;
26362306a36Sopenharmony_ci			goto recv_putback_on_err;
26462306a36Sopenharmony_ci		}
26562306a36Sopenharmony_ci		rsp->msg.data_len = msg->msg.data_len;
26662306a36Sopenharmony_ci	} else {
26762306a36Sopenharmony_ci		rsp->msg.data_len = 0;
26862306a36Sopenharmony_ci	}
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	rv = copyout(rsp, to);
27162306a36Sopenharmony_ci	if (rv)
27262306a36Sopenharmony_ci		goto recv_putback_on_err;
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	mutex_unlock(&priv->recv_mutex);
27562306a36Sopenharmony_ci	ipmi_free_recv_msg(msg);
27662306a36Sopenharmony_ci	return rv2;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_cirecv_putback_on_err:
27962306a36Sopenharmony_ci	/* If we got an error, put the message back onto
28062306a36Sopenharmony_ci	   the head of the queue. */
28162306a36Sopenharmony_ci	spin_lock_irqsave(&priv->recv_msg_lock, flags);
28262306a36Sopenharmony_ci	list_add(entry, &priv->recv_msgs);
28362306a36Sopenharmony_ci	spin_unlock_irqrestore(&priv->recv_msg_lock, flags);
28462306a36Sopenharmony_cirecv_err:
28562306a36Sopenharmony_ci	mutex_unlock(&priv->recv_mutex);
28662306a36Sopenharmony_ci	return rv;
28762306a36Sopenharmony_ci}
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_cistatic int copyout_recv(struct ipmi_recv *rsp, void __user *to)
29062306a36Sopenharmony_ci{
29162306a36Sopenharmony_ci	return copy_to_user(to, rsp, sizeof(struct ipmi_recv)) ? -EFAULT : 0;
29262306a36Sopenharmony_ci}
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_cistatic long ipmi_ioctl(struct file   *file,
29562306a36Sopenharmony_ci		       unsigned int  cmd,
29662306a36Sopenharmony_ci		       unsigned long data)
29762306a36Sopenharmony_ci{
29862306a36Sopenharmony_ci	int                      rv = -EINVAL;
29962306a36Sopenharmony_ci	struct ipmi_file_private *priv = file->private_data;
30062306a36Sopenharmony_ci	void __user *arg = (void __user *)data;
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	switch (cmd)
30362306a36Sopenharmony_ci	{
30462306a36Sopenharmony_ci	case IPMICTL_SEND_COMMAND:
30562306a36Sopenharmony_ci	{
30662306a36Sopenharmony_ci		struct ipmi_req req;
30762306a36Sopenharmony_ci		int retries;
30862306a36Sopenharmony_ci		unsigned int retry_time_ms;
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci		if (copy_from_user(&req, arg, sizeof(req))) {
31162306a36Sopenharmony_ci			rv = -EFAULT;
31262306a36Sopenharmony_ci			break;
31362306a36Sopenharmony_ci		}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci		mutex_lock(&priv->recv_mutex);
31662306a36Sopenharmony_ci		retries = priv->default_retries;
31762306a36Sopenharmony_ci		retry_time_ms = priv->default_retry_time_ms;
31862306a36Sopenharmony_ci		mutex_unlock(&priv->recv_mutex);
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci		rv = handle_send_req(priv->user, &req, retries, retry_time_ms);
32162306a36Sopenharmony_ci		break;
32262306a36Sopenharmony_ci	}
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	case IPMICTL_SEND_COMMAND_SETTIME:
32562306a36Sopenharmony_ci	{
32662306a36Sopenharmony_ci		struct ipmi_req_settime req;
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci		if (copy_from_user(&req, arg, sizeof(req))) {
32962306a36Sopenharmony_ci			rv = -EFAULT;
33062306a36Sopenharmony_ci			break;
33162306a36Sopenharmony_ci		}
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci		rv = handle_send_req(priv->user,
33462306a36Sopenharmony_ci				     &req.req,
33562306a36Sopenharmony_ci				     req.retries,
33662306a36Sopenharmony_ci				     req.retry_time_ms);
33762306a36Sopenharmony_ci		break;
33862306a36Sopenharmony_ci	}
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	case IPMICTL_RECEIVE_MSG:
34162306a36Sopenharmony_ci	case IPMICTL_RECEIVE_MSG_TRUNC:
34262306a36Sopenharmony_ci	{
34362306a36Sopenharmony_ci		struct ipmi_recv      rsp;
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci		if (copy_from_user(&rsp, arg, sizeof(rsp)))
34662306a36Sopenharmony_ci			rv = -EFAULT;
34762306a36Sopenharmony_ci		else
34862306a36Sopenharmony_ci			rv = handle_recv(priv, cmd == IPMICTL_RECEIVE_MSG_TRUNC,
34962306a36Sopenharmony_ci					 &rsp, copyout_recv, arg);
35062306a36Sopenharmony_ci		break;
35162306a36Sopenharmony_ci	}
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	case IPMICTL_REGISTER_FOR_CMD:
35462306a36Sopenharmony_ci	{
35562306a36Sopenharmony_ci		struct ipmi_cmdspec val;
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci		if (copy_from_user(&val, arg, sizeof(val))) {
35862306a36Sopenharmony_ci			rv = -EFAULT;
35962306a36Sopenharmony_ci			break;
36062306a36Sopenharmony_ci		}
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci		rv = ipmi_register_for_cmd(priv->user, val.netfn, val.cmd,
36362306a36Sopenharmony_ci					   IPMI_CHAN_ALL);
36462306a36Sopenharmony_ci		break;
36562306a36Sopenharmony_ci	}
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	case IPMICTL_UNREGISTER_FOR_CMD:
36862306a36Sopenharmony_ci	{
36962306a36Sopenharmony_ci		struct ipmi_cmdspec   val;
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci		if (copy_from_user(&val, arg, sizeof(val))) {
37262306a36Sopenharmony_ci			rv = -EFAULT;
37362306a36Sopenharmony_ci			break;
37462306a36Sopenharmony_ci		}
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci		rv = ipmi_unregister_for_cmd(priv->user, val.netfn, val.cmd,
37762306a36Sopenharmony_ci					     IPMI_CHAN_ALL);
37862306a36Sopenharmony_ci		break;
37962306a36Sopenharmony_ci	}
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	case IPMICTL_REGISTER_FOR_CMD_CHANS:
38262306a36Sopenharmony_ci	{
38362306a36Sopenharmony_ci		struct ipmi_cmdspec_chans val;
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci		if (copy_from_user(&val, arg, sizeof(val))) {
38662306a36Sopenharmony_ci			rv = -EFAULT;
38762306a36Sopenharmony_ci			break;
38862306a36Sopenharmony_ci		}
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci		rv = ipmi_register_for_cmd(priv->user, val.netfn, val.cmd,
39162306a36Sopenharmony_ci					   val.chans);
39262306a36Sopenharmony_ci		break;
39362306a36Sopenharmony_ci	}
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	case IPMICTL_UNREGISTER_FOR_CMD_CHANS:
39662306a36Sopenharmony_ci	{
39762306a36Sopenharmony_ci		struct ipmi_cmdspec_chans val;
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci		if (copy_from_user(&val, arg, sizeof(val))) {
40062306a36Sopenharmony_ci			rv = -EFAULT;
40162306a36Sopenharmony_ci			break;
40262306a36Sopenharmony_ci		}
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci		rv = ipmi_unregister_for_cmd(priv->user, val.netfn, val.cmd,
40562306a36Sopenharmony_ci					     val.chans);
40662306a36Sopenharmony_ci		break;
40762306a36Sopenharmony_ci	}
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	case IPMICTL_SET_GETS_EVENTS_CMD:
41062306a36Sopenharmony_ci	{
41162306a36Sopenharmony_ci		int val;
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci		if (copy_from_user(&val, arg, sizeof(val))) {
41462306a36Sopenharmony_ci			rv = -EFAULT;
41562306a36Sopenharmony_ci			break;
41662306a36Sopenharmony_ci		}
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci		rv = ipmi_set_gets_events(priv->user, val);
41962306a36Sopenharmony_ci		break;
42062306a36Sopenharmony_ci	}
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	/* The next four are legacy, not per-channel. */
42362306a36Sopenharmony_ci	case IPMICTL_SET_MY_ADDRESS_CMD:
42462306a36Sopenharmony_ci	{
42562306a36Sopenharmony_ci		unsigned int val;
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci		if (copy_from_user(&val, arg, sizeof(val))) {
42862306a36Sopenharmony_ci			rv = -EFAULT;
42962306a36Sopenharmony_ci			break;
43062306a36Sopenharmony_ci		}
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci		rv = ipmi_set_my_address(priv->user, 0, val);
43362306a36Sopenharmony_ci		break;
43462306a36Sopenharmony_ci	}
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	case IPMICTL_GET_MY_ADDRESS_CMD:
43762306a36Sopenharmony_ci	{
43862306a36Sopenharmony_ci		unsigned int  val;
43962306a36Sopenharmony_ci		unsigned char rval;
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci		rv = ipmi_get_my_address(priv->user, 0, &rval);
44262306a36Sopenharmony_ci		if (rv)
44362306a36Sopenharmony_ci			break;
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci		val = rval;
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci		if (copy_to_user(arg, &val, sizeof(val))) {
44862306a36Sopenharmony_ci			rv = -EFAULT;
44962306a36Sopenharmony_ci			break;
45062306a36Sopenharmony_ci		}
45162306a36Sopenharmony_ci		break;
45262306a36Sopenharmony_ci	}
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	case IPMICTL_SET_MY_LUN_CMD:
45562306a36Sopenharmony_ci	{
45662306a36Sopenharmony_ci		unsigned int val;
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci		if (copy_from_user(&val, arg, sizeof(val))) {
45962306a36Sopenharmony_ci			rv = -EFAULT;
46062306a36Sopenharmony_ci			break;
46162306a36Sopenharmony_ci		}
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci		rv = ipmi_set_my_LUN(priv->user, 0, val);
46462306a36Sopenharmony_ci		break;
46562306a36Sopenharmony_ci	}
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	case IPMICTL_GET_MY_LUN_CMD:
46862306a36Sopenharmony_ci	{
46962306a36Sopenharmony_ci		unsigned int  val;
47062306a36Sopenharmony_ci		unsigned char rval;
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci		rv = ipmi_get_my_LUN(priv->user, 0, &rval);
47362306a36Sopenharmony_ci		if (rv)
47462306a36Sopenharmony_ci			break;
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci		val = rval;
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci		if (copy_to_user(arg, &val, sizeof(val))) {
47962306a36Sopenharmony_ci			rv = -EFAULT;
48062306a36Sopenharmony_ci			break;
48162306a36Sopenharmony_ci		}
48262306a36Sopenharmony_ci		break;
48362306a36Sopenharmony_ci	}
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	case IPMICTL_SET_MY_CHANNEL_ADDRESS_CMD:
48662306a36Sopenharmony_ci	{
48762306a36Sopenharmony_ci		struct ipmi_channel_lun_address_set val;
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci		if (copy_from_user(&val, arg, sizeof(val))) {
49062306a36Sopenharmony_ci			rv = -EFAULT;
49162306a36Sopenharmony_ci			break;
49262306a36Sopenharmony_ci		}
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci		return ipmi_set_my_address(priv->user, val.channel, val.value);
49562306a36Sopenharmony_ci	}
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	case IPMICTL_GET_MY_CHANNEL_ADDRESS_CMD:
49862306a36Sopenharmony_ci	{
49962306a36Sopenharmony_ci		struct ipmi_channel_lun_address_set val;
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci		if (copy_from_user(&val, arg, sizeof(val))) {
50262306a36Sopenharmony_ci			rv = -EFAULT;
50362306a36Sopenharmony_ci			break;
50462306a36Sopenharmony_ci		}
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci		rv = ipmi_get_my_address(priv->user, val.channel, &val.value);
50762306a36Sopenharmony_ci		if (rv)
50862306a36Sopenharmony_ci			break;
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci		if (copy_to_user(arg, &val, sizeof(val))) {
51162306a36Sopenharmony_ci			rv = -EFAULT;
51262306a36Sopenharmony_ci			break;
51362306a36Sopenharmony_ci		}
51462306a36Sopenharmony_ci		break;
51562306a36Sopenharmony_ci	}
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	case IPMICTL_SET_MY_CHANNEL_LUN_CMD:
51862306a36Sopenharmony_ci	{
51962306a36Sopenharmony_ci		struct ipmi_channel_lun_address_set val;
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci		if (copy_from_user(&val, arg, sizeof(val))) {
52262306a36Sopenharmony_ci			rv = -EFAULT;
52362306a36Sopenharmony_ci			break;
52462306a36Sopenharmony_ci		}
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci		rv = ipmi_set_my_LUN(priv->user, val.channel, val.value);
52762306a36Sopenharmony_ci		break;
52862306a36Sopenharmony_ci	}
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	case IPMICTL_GET_MY_CHANNEL_LUN_CMD:
53162306a36Sopenharmony_ci	{
53262306a36Sopenharmony_ci		struct ipmi_channel_lun_address_set val;
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci		if (copy_from_user(&val, arg, sizeof(val))) {
53562306a36Sopenharmony_ci			rv = -EFAULT;
53662306a36Sopenharmony_ci			break;
53762306a36Sopenharmony_ci		}
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci		rv = ipmi_get_my_LUN(priv->user, val.channel, &val.value);
54062306a36Sopenharmony_ci		if (rv)
54162306a36Sopenharmony_ci			break;
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci		if (copy_to_user(arg, &val, sizeof(val))) {
54462306a36Sopenharmony_ci			rv = -EFAULT;
54562306a36Sopenharmony_ci			break;
54662306a36Sopenharmony_ci		}
54762306a36Sopenharmony_ci		break;
54862306a36Sopenharmony_ci	}
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	case IPMICTL_SET_TIMING_PARMS_CMD:
55162306a36Sopenharmony_ci	{
55262306a36Sopenharmony_ci		struct ipmi_timing_parms parms;
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci		if (copy_from_user(&parms, arg, sizeof(parms))) {
55562306a36Sopenharmony_ci			rv = -EFAULT;
55662306a36Sopenharmony_ci			break;
55762306a36Sopenharmony_ci		}
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci		mutex_lock(&priv->recv_mutex);
56062306a36Sopenharmony_ci		priv->default_retries = parms.retries;
56162306a36Sopenharmony_ci		priv->default_retry_time_ms = parms.retry_time_ms;
56262306a36Sopenharmony_ci		mutex_unlock(&priv->recv_mutex);
56362306a36Sopenharmony_ci		rv = 0;
56462306a36Sopenharmony_ci		break;
56562306a36Sopenharmony_ci	}
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	case IPMICTL_GET_TIMING_PARMS_CMD:
56862306a36Sopenharmony_ci	{
56962306a36Sopenharmony_ci		struct ipmi_timing_parms parms;
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci		mutex_lock(&priv->recv_mutex);
57262306a36Sopenharmony_ci		parms.retries = priv->default_retries;
57362306a36Sopenharmony_ci		parms.retry_time_ms = priv->default_retry_time_ms;
57462306a36Sopenharmony_ci		mutex_unlock(&priv->recv_mutex);
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci		if (copy_to_user(arg, &parms, sizeof(parms))) {
57762306a36Sopenharmony_ci			rv = -EFAULT;
57862306a36Sopenharmony_ci			break;
57962306a36Sopenharmony_ci		}
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci		rv = 0;
58262306a36Sopenharmony_ci		break;
58362306a36Sopenharmony_ci	}
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	case IPMICTL_GET_MAINTENANCE_MODE_CMD:
58662306a36Sopenharmony_ci	{
58762306a36Sopenharmony_ci		int mode;
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci		mode = ipmi_get_maintenance_mode(priv->user);
59062306a36Sopenharmony_ci		if (copy_to_user(arg, &mode, sizeof(mode))) {
59162306a36Sopenharmony_ci			rv = -EFAULT;
59262306a36Sopenharmony_ci			break;
59362306a36Sopenharmony_ci		}
59462306a36Sopenharmony_ci		rv = 0;
59562306a36Sopenharmony_ci		break;
59662306a36Sopenharmony_ci	}
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci	case IPMICTL_SET_MAINTENANCE_MODE_CMD:
59962306a36Sopenharmony_ci	{
60062306a36Sopenharmony_ci		int mode;
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci		if (copy_from_user(&mode, arg, sizeof(mode))) {
60362306a36Sopenharmony_ci			rv = -EFAULT;
60462306a36Sopenharmony_ci			break;
60562306a36Sopenharmony_ci		}
60662306a36Sopenharmony_ci		rv = ipmi_set_maintenance_mode(priv->user, mode);
60762306a36Sopenharmony_ci		break;
60862306a36Sopenharmony_ci	}
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	default:
61162306a36Sopenharmony_ci		rv = -ENOTTY;
61262306a36Sopenharmony_ci		break;
61362306a36Sopenharmony_ci	}
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	return rv;
61662306a36Sopenharmony_ci}
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci#ifdef CONFIG_COMPAT
61962306a36Sopenharmony_ci/*
62062306a36Sopenharmony_ci * The following code contains code for supporting 32-bit compatible
62162306a36Sopenharmony_ci * ioctls on 64-bit kernels.  This allows running 32-bit apps on the
62262306a36Sopenharmony_ci * 64-bit kernel
62362306a36Sopenharmony_ci */
62462306a36Sopenharmony_ci#define COMPAT_IPMICTL_SEND_COMMAND	\
62562306a36Sopenharmony_ci	_IOR(IPMI_IOC_MAGIC, 13, struct compat_ipmi_req)
62662306a36Sopenharmony_ci#define COMPAT_IPMICTL_SEND_COMMAND_SETTIME	\
62762306a36Sopenharmony_ci	_IOR(IPMI_IOC_MAGIC, 21, struct compat_ipmi_req_settime)
62862306a36Sopenharmony_ci#define COMPAT_IPMICTL_RECEIVE_MSG	\
62962306a36Sopenharmony_ci	_IOWR(IPMI_IOC_MAGIC, 12, struct compat_ipmi_recv)
63062306a36Sopenharmony_ci#define COMPAT_IPMICTL_RECEIVE_MSG_TRUNC	\
63162306a36Sopenharmony_ci	_IOWR(IPMI_IOC_MAGIC, 11, struct compat_ipmi_recv)
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_cistruct compat_ipmi_msg {
63462306a36Sopenharmony_ci	u8		netfn;
63562306a36Sopenharmony_ci	u8		cmd;
63662306a36Sopenharmony_ci	u16		data_len;
63762306a36Sopenharmony_ci	compat_uptr_t	data;
63862306a36Sopenharmony_ci};
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_cistruct compat_ipmi_req {
64162306a36Sopenharmony_ci	compat_uptr_t		addr;
64262306a36Sopenharmony_ci	compat_uint_t		addr_len;
64362306a36Sopenharmony_ci	compat_long_t		msgid;
64462306a36Sopenharmony_ci	struct compat_ipmi_msg	msg;
64562306a36Sopenharmony_ci};
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_cistruct compat_ipmi_recv {
64862306a36Sopenharmony_ci	compat_int_t		recv_type;
64962306a36Sopenharmony_ci	compat_uptr_t		addr;
65062306a36Sopenharmony_ci	compat_uint_t		addr_len;
65162306a36Sopenharmony_ci	compat_long_t		msgid;
65262306a36Sopenharmony_ci	struct compat_ipmi_msg	msg;
65362306a36Sopenharmony_ci};
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_cistruct compat_ipmi_req_settime {
65662306a36Sopenharmony_ci	struct compat_ipmi_req	req;
65762306a36Sopenharmony_ci	compat_int_t		retries;
65862306a36Sopenharmony_ci	compat_uint_t		retry_time_ms;
65962306a36Sopenharmony_ci};
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci/*
66262306a36Sopenharmony_ci * Define some helper functions for copying IPMI data
66362306a36Sopenharmony_ci */
66462306a36Sopenharmony_cistatic void get_compat_ipmi_msg(struct ipmi_msg *p64,
66562306a36Sopenharmony_ci				struct compat_ipmi_msg *p32)
66662306a36Sopenharmony_ci{
66762306a36Sopenharmony_ci	p64->netfn = p32->netfn;
66862306a36Sopenharmony_ci	p64->cmd = p32->cmd;
66962306a36Sopenharmony_ci	p64->data_len = p32->data_len;
67062306a36Sopenharmony_ci	p64->data = compat_ptr(p32->data);
67162306a36Sopenharmony_ci}
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_cistatic void get_compat_ipmi_req(struct ipmi_req *p64,
67462306a36Sopenharmony_ci				struct compat_ipmi_req *p32)
67562306a36Sopenharmony_ci{
67662306a36Sopenharmony_ci	p64->addr = compat_ptr(p32->addr);
67762306a36Sopenharmony_ci	p64->addr_len = p32->addr_len;
67862306a36Sopenharmony_ci	p64->msgid = p32->msgid;
67962306a36Sopenharmony_ci	get_compat_ipmi_msg(&p64->msg, &p32->msg);
68062306a36Sopenharmony_ci}
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_cistatic void get_compat_ipmi_req_settime(struct ipmi_req_settime *p64,
68362306a36Sopenharmony_ci		struct compat_ipmi_req_settime *p32)
68462306a36Sopenharmony_ci{
68562306a36Sopenharmony_ci	get_compat_ipmi_req(&p64->req, &p32->req);
68662306a36Sopenharmony_ci	p64->retries = p32->retries;
68762306a36Sopenharmony_ci	p64->retry_time_ms = p32->retry_time_ms;
68862306a36Sopenharmony_ci}
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_cistatic void get_compat_ipmi_recv(struct ipmi_recv *p64,
69162306a36Sopenharmony_ci				 struct compat_ipmi_recv *p32)
69262306a36Sopenharmony_ci{
69362306a36Sopenharmony_ci	memset(p64, 0, sizeof(struct ipmi_recv));
69462306a36Sopenharmony_ci	p64->recv_type = p32->recv_type;
69562306a36Sopenharmony_ci	p64->addr = compat_ptr(p32->addr);
69662306a36Sopenharmony_ci	p64->addr_len = p32->addr_len;
69762306a36Sopenharmony_ci	p64->msgid = p32->msgid;
69862306a36Sopenharmony_ci	get_compat_ipmi_msg(&p64->msg, &p32->msg);
69962306a36Sopenharmony_ci}
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_cistatic int copyout_recv32(struct ipmi_recv *p64, void __user *to)
70262306a36Sopenharmony_ci{
70362306a36Sopenharmony_ci	struct compat_ipmi_recv v32;
70462306a36Sopenharmony_ci	memset(&v32, 0, sizeof(struct compat_ipmi_recv));
70562306a36Sopenharmony_ci	v32.recv_type = p64->recv_type;
70662306a36Sopenharmony_ci	v32.addr = ptr_to_compat(p64->addr);
70762306a36Sopenharmony_ci	v32.addr_len = p64->addr_len;
70862306a36Sopenharmony_ci	v32.msgid = p64->msgid;
70962306a36Sopenharmony_ci	v32.msg.netfn = p64->msg.netfn;
71062306a36Sopenharmony_ci	v32.msg.cmd = p64->msg.cmd;
71162306a36Sopenharmony_ci	v32.msg.data_len = p64->msg.data_len;
71262306a36Sopenharmony_ci	v32.msg.data = ptr_to_compat(p64->msg.data);
71362306a36Sopenharmony_ci	return copy_to_user(to, &v32, sizeof(v32)) ? -EFAULT : 0;
71462306a36Sopenharmony_ci}
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci/*
71762306a36Sopenharmony_ci * Handle compatibility ioctls
71862306a36Sopenharmony_ci */
71962306a36Sopenharmony_cistatic long compat_ipmi_ioctl(struct file *filep, unsigned int cmd,
72062306a36Sopenharmony_ci			      unsigned long arg)
72162306a36Sopenharmony_ci{
72262306a36Sopenharmony_ci	struct ipmi_file_private *priv = filep->private_data;
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci	switch(cmd) {
72562306a36Sopenharmony_ci	case COMPAT_IPMICTL_SEND_COMMAND:
72662306a36Sopenharmony_ci	{
72762306a36Sopenharmony_ci		struct ipmi_req	rp;
72862306a36Sopenharmony_ci		struct compat_ipmi_req r32;
72962306a36Sopenharmony_ci		int retries;
73062306a36Sopenharmony_ci		unsigned int retry_time_ms;
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci		if (copy_from_user(&r32, compat_ptr(arg), sizeof(r32)))
73362306a36Sopenharmony_ci			return -EFAULT;
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci		get_compat_ipmi_req(&rp, &r32);
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci		mutex_lock(&priv->recv_mutex);
73862306a36Sopenharmony_ci		retries = priv->default_retries;
73962306a36Sopenharmony_ci		retry_time_ms = priv->default_retry_time_ms;
74062306a36Sopenharmony_ci		mutex_unlock(&priv->recv_mutex);
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci		return handle_send_req(priv->user, &rp,
74362306a36Sopenharmony_ci				       retries, retry_time_ms);
74462306a36Sopenharmony_ci	}
74562306a36Sopenharmony_ci	case COMPAT_IPMICTL_SEND_COMMAND_SETTIME:
74662306a36Sopenharmony_ci	{
74762306a36Sopenharmony_ci		struct ipmi_req_settime	sp;
74862306a36Sopenharmony_ci		struct compat_ipmi_req_settime sp32;
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci		if (copy_from_user(&sp32, compat_ptr(arg), sizeof(sp32)))
75162306a36Sopenharmony_ci			return -EFAULT;
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_ci		get_compat_ipmi_req_settime(&sp, &sp32);
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci		return handle_send_req(priv->user, &sp.req,
75662306a36Sopenharmony_ci				sp.retries, sp.retry_time_ms);
75762306a36Sopenharmony_ci	}
75862306a36Sopenharmony_ci	case COMPAT_IPMICTL_RECEIVE_MSG:
75962306a36Sopenharmony_ci	case COMPAT_IPMICTL_RECEIVE_MSG_TRUNC:
76062306a36Sopenharmony_ci	{
76162306a36Sopenharmony_ci		struct ipmi_recv   recv64;
76262306a36Sopenharmony_ci		struct compat_ipmi_recv recv32;
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci		if (copy_from_user(&recv32, compat_ptr(arg), sizeof(recv32)))
76562306a36Sopenharmony_ci			return -EFAULT;
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci		get_compat_ipmi_recv(&recv64, &recv32);
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci		return handle_recv(priv,
77062306a36Sopenharmony_ci				 cmd == COMPAT_IPMICTL_RECEIVE_MSG_TRUNC,
77162306a36Sopenharmony_ci				 &recv64, copyout_recv32, compat_ptr(arg));
77262306a36Sopenharmony_ci	}
77362306a36Sopenharmony_ci	default:
77462306a36Sopenharmony_ci		return ipmi_ioctl(filep, cmd, arg);
77562306a36Sopenharmony_ci	}
77662306a36Sopenharmony_ci}
77762306a36Sopenharmony_ci#endif
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_cistatic const struct file_operations ipmi_fops = {
78062306a36Sopenharmony_ci	.owner		= THIS_MODULE,
78162306a36Sopenharmony_ci	.unlocked_ioctl	= ipmi_ioctl,
78262306a36Sopenharmony_ci#ifdef CONFIG_COMPAT
78362306a36Sopenharmony_ci	.compat_ioctl   = compat_ipmi_ioctl,
78462306a36Sopenharmony_ci#endif
78562306a36Sopenharmony_ci	.open		= ipmi_open,
78662306a36Sopenharmony_ci	.release	= ipmi_release,
78762306a36Sopenharmony_ci	.fasync		= ipmi_fasync,
78862306a36Sopenharmony_ci	.poll		= ipmi_poll,
78962306a36Sopenharmony_ci	.llseek		= noop_llseek,
79062306a36Sopenharmony_ci};
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci#define DEVICE_NAME     "ipmidev"
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_cistatic int ipmi_major;
79562306a36Sopenharmony_cimodule_param(ipmi_major, int, 0);
79662306a36Sopenharmony_ciMODULE_PARM_DESC(ipmi_major, "Sets the major number of the IPMI device.  By"
79762306a36Sopenharmony_ci		 " default, or if you set it to zero, it will choose the next"
79862306a36Sopenharmony_ci		 " available device.  Setting it to -1 will disable the"
79962306a36Sopenharmony_ci		 " interface.  Other values will set the major device number"
80062306a36Sopenharmony_ci		 " to that value.");
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci/* Keep track of the devices that are registered. */
80362306a36Sopenharmony_cistruct ipmi_reg_list {
80462306a36Sopenharmony_ci	dev_t            dev;
80562306a36Sopenharmony_ci	struct list_head link;
80662306a36Sopenharmony_ci};
80762306a36Sopenharmony_cistatic LIST_HEAD(reg_list);
80862306a36Sopenharmony_cistatic DEFINE_MUTEX(reg_list_mutex);
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_cistatic const struct class ipmi_class = {
81162306a36Sopenharmony_ci	.name = "ipmi",
81262306a36Sopenharmony_ci};
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_cistatic void ipmi_new_smi(int if_num, struct device *device)
81562306a36Sopenharmony_ci{
81662306a36Sopenharmony_ci	dev_t dev = MKDEV(ipmi_major, if_num);
81762306a36Sopenharmony_ci	struct ipmi_reg_list *entry;
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci	entry = kmalloc(sizeof(*entry), GFP_KERNEL);
82062306a36Sopenharmony_ci	if (!entry) {
82162306a36Sopenharmony_ci		pr_err("ipmi_devintf: Unable to create the ipmi class device link\n");
82262306a36Sopenharmony_ci		return;
82362306a36Sopenharmony_ci	}
82462306a36Sopenharmony_ci	entry->dev = dev;
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci	mutex_lock(&reg_list_mutex);
82762306a36Sopenharmony_ci	device_create(&ipmi_class, device, dev, NULL, "ipmi%d", if_num);
82862306a36Sopenharmony_ci	list_add(&entry->link, &reg_list);
82962306a36Sopenharmony_ci	mutex_unlock(&reg_list_mutex);
83062306a36Sopenharmony_ci}
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_cistatic void ipmi_smi_gone(int if_num)
83362306a36Sopenharmony_ci{
83462306a36Sopenharmony_ci	dev_t dev = MKDEV(ipmi_major, if_num);
83562306a36Sopenharmony_ci	struct ipmi_reg_list *entry;
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci	mutex_lock(&reg_list_mutex);
83862306a36Sopenharmony_ci	list_for_each_entry(entry, &reg_list, link) {
83962306a36Sopenharmony_ci		if (entry->dev == dev) {
84062306a36Sopenharmony_ci			list_del(&entry->link);
84162306a36Sopenharmony_ci			kfree(entry);
84262306a36Sopenharmony_ci			break;
84362306a36Sopenharmony_ci		}
84462306a36Sopenharmony_ci	}
84562306a36Sopenharmony_ci	device_destroy(&ipmi_class, dev);
84662306a36Sopenharmony_ci	mutex_unlock(&reg_list_mutex);
84762306a36Sopenharmony_ci}
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_cistatic struct ipmi_smi_watcher smi_watcher =
85062306a36Sopenharmony_ci{
85162306a36Sopenharmony_ci	.owner    = THIS_MODULE,
85262306a36Sopenharmony_ci	.new_smi  = ipmi_new_smi,
85362306a36Sopenharmony_ci	.smi_gone = ipmi_smi_gone,
85462306a36Sopenharmony_ci};
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_cistatic int __init init_ipmi_devintf(void)
85762306a36Sopenharmony_ci{
85862306a36Sopenharmony_ci	int rv;
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci	if (ipmi_major < 0)
86162306a36Sopenharmony_ci		return -EINVAL;
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_ci	pr_info("ipmi device interface\n");
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_ci	rv = class_register(&ipmi_class);
86662306a36Sopenharmony_ci	if (rv)
86762306a36Sopenharmony_ci		return rv;
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_ci	rv = register_chrdev(ipmi_major, DEVICE_NAME, &ipmi_fops);
87062306a36Sopenharmony_ci	if (rv < 0) {
87162306a36Sopenharmony_ci		class_unregister(&ipmi_class);
87262306a36Sopenharmony_ci		pr_err("ipmi: can't get major %d\n", ipmi_major);
87362306a36Sopenharmony_ci		return rv;
87462306a36Sopenharmony_ci	}
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_ci	if (ipmi_major == 0) {
87762306a36Sopenharmony_ci		ipmi_major = rv;
87862306a36Sopenharmony_ci	}
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_ci	rv = ipmi_smi_watcher_register(&smi_watcher);
88162306a36Sopenharmony_ci	if (rv) {
88262306a36Sopenharmony_ci		unregister_chrdev(ipmi_major, DEVICE_NAME);
88362306a36Sopenharmony_ci		class_unregister(&ipmi_class);
88462306a36Sopenharmony_ci		pr_warn("ipmi: can't register smi watcher\n");
88562306a36Sopenharmony_ci		return rv;
88662306a36Sopenharmony_ci	}
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci	return 0;
88962306a36Sopenharmony_ci}
89062306a36Sopenharmony_cimodule_init(init_ipmi_devintf);
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_cistatic void __exit cleanup_ipmi(void)
89362306a36Sopenharmony_ci{
89462306a36Sopenharmony_ci	struct ipmi_reg_list *entry, *entry2;
89562306a36Sopenharmony_ci	mutex_lock(&reg_list_mutex);
89662306a36Sopenharmony_ci	list_for_each_entry_safe(entry, entry2, &reg_list, link) {
89762306a36Sopenharmony_ci		list_del(&entry->link);
89862306a36Sopenharmony_ci		device_destroy(&ipmi_class, entry->dev);
89962306a36Sopenharmony_ci		kfree(entry);
90062306a36Sopenharmony_ci	}
90162306a36Sopenharmony_ci	mutex_unlock(&reg_list_mutex);
90262306a36Sopenharmony_ci	class_unregister(&ipmi_class);
90362306a36Sopenharmony_ci	ipmi_smi_watcher_unregister(&smi_watcher);
90462306a36Sopenharmony_ci	unregister_chrdev(ipmi_major, DEVICE_NAME);
90562306a36Sopenharmony_ci}
90662306a36Sopenharmony_cimodule_exit(cleanup_ipmi);
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ciMODULE_LICENSE("GPL");
90962306a36Sopenharmony_ciMODULE_AUTHOR("Corey Minyard <minyard@mvista.com>");
91062306a36Sopenharmony_ciMODULE_DESCRIPTION("Linux device interface for the IPMI message handler.");
911