xref: /openbmc/linux/drivers/char/ipmi/ipmi_devintf.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
1243ac210SCorey Minyard // SPDX-License-Identifier: GPL-2.0+
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  * ipmi_devintf.c
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  * Linux device interface for the IPMI message handler.
61da177e4SLinus Torvalds  *
71da177e4SLinus Torvalds  * Author: MontaVista Software, Inc.
81da177e4SLinus Torvalds  *         Corey Minyard <minyard@mvista.com>
91da177e4SLinus Torvalds  *         source@mvista.com
101da177e4SLinus Torvalds  *
111da177e4SLinus Torvalds  * Copyright 2002 MontaVista Software Inc.
121da177e4SLinus Torvalds  */
131da177e4SLinus Torvalds 
141da177e4SLinus Torvalds #include <linux/module.h>
151da177e4SLinus Torvalds #include <linux/moduleparam.h>
161da177e4SLinus Torvalds #include <linux/errno.h>
171da177e4SLinus Torvalds #include <linux/poll.h>
18a99bbaf5SAlexey Dobriyan #include <linux/sched.h>
191da177e4SLinus Torvalds #include <linux/spinlock.h>
201da177e4SLinus Torvalds #include <linux/slab.h>
211da177e4SLinus Torvalds #include <linux/ipmi.h>
22d6dfd131SCorey Minyard #include <linux/mutex.h>
231da177e4SLinus Torvalds #include <linux/init.h>
2437e0915bSCorey Minyard #include <linux/device.h>
256a94f920SAndrew Morton #include <linux/compat.h>
261da177e4SLinus Torvalds 
271da177e4SLinus Torvalds struct ipmi_file_private
281da177e4SLinus Torvalds {
292911c988SCorey Minyard 	struct ipmi_user     *user;
301da177e4SLinus Torvalds 	spinlock_t           recv_msg_lock;
311da177e4SLinus Torvalds 	struct list_head     recv_msgs;
321da177e4SLinus Torvalds 	struct fasync_struct *fasync_queue;
331da177e4SLinus Torvalds 	wait_queue_head_t    wait;
34d6dfd131SCorey Minyard 	struct mutex	     recv_mutex;
351da177e4SLinus Torvalds 	int                  default_retries;
361da177e4SLinus Torvalds 	unsigned int         default_retry_time_ms;
371da177e4SLinus Torvalds };
381da177e4SLinus Torvalds 
file_receive_handler(struct ipmi_recv_msg * msg,void * handler_data)391da177e4SLinus Torvalds static void file_receive_handler(struct ipmi_recv_msg *msg,
401da177e4SLinus Torvalds 				 void                 *handler_data)
411da177e4SLinus Torvalds {
421da177e4SLinus Torvalds 	struct ipmi_file_private *priv = handler_data;
431da177e4SLinus Torvalds 	int                      was_empty;
441da177e4SLinus Torvalds 	unsigned long            flags;
451da177e4SLinus Torvalds 
46c81c5fc2SCorey Minyard 	spin_lock_irqsave(&priv->recv_msg_lock, flags);
47c81c5fc2SCorey Minyard 	was_empty = list_empty(&priv->recv_msgs);
48c81c5fc2SCorey Minyard 	list_add_tail(&msg->link, &priv->recv_msgs);
496aa2dd00SCorey Minyard 	spin_unlock_irqrestore(&priv->recv_msg_lock, flags);
501da177e4SLinus Torvalds 
511da177e4SLinus Torvalds 	if (was_empty) {
521da177e4SLinus Torvalds 		wake_up_interruptible(&priv->wait);
531da177e4SLinus Torvalds 		kill_fasync(&priv->fasync_queue, SIGIO, POLL_IN);
541da177e4SLinus Torvalds 	}
551da177e4SLinus Torvalds }
561da177e4SLinus Torvalds 
ipmi_poll(struct file * file,poll_table * wait)57afc9a42bSAl Viro static __poll_t ipmi_poll(struct file *file, poll_table *wait)
581da177e4SLinus Torvalds {
591da177e4SLinus Torvalds 	struct ipmi_file_private *priv = file->private_data;
60afc9a42bSAl Viro 	__poll_t             mask = 0;
611da177e4SLinus Torvalds 	unsigned long            flags;
621da177e4SLinus Torvalds 
631da177e4SLinus Torvalds 	poll_wait(file, &priv->wait, wait);
641da177e4SLinus Torvalds 
651da177e4SLinus Torvalds 	spin_lock_irqsave(&priv->recv_msg_lock, flags);
661da177e4SLinus Torvalds 
67c81c5fc2SCorey Minyard 	if (!list_empty(&priv->recv_msgs))
68a9a08845SLinus Torvalds 		mask |= (EPOLLIN | EPOLLRDNORM);
691da177e4SLinus Torvalds 
701da177e4SLinus Torvalds 	spin_unlock_irqrestore(&priv->recv_msg_lock, flags);
711da177e4SLinus Torvalds 
721da177e4SLinus Torvalds 	return mask;
731da177e4SLinus Torvalds }
741da177e4SLinus Torvalds 
ipmi_fasync(int fd,struct file * file,int on)751da177e4SLinus Torvalds static int ipmi_fasync(int fd, struct file *file, int on)
761da177e4SLinus Torvalds {
771da177e4SLinus Torvalds 	struct ipmi_file_private *priv = file->private_data;
781da177e4SLinus Torvalds 
796aa2dd00SCorey Minyard 	return fasync_helper(fd, file, on, &priv->fasync_queue);
801da177e4SLinus Torvalds }
811da177e4SLinus Torvalds 
82210af2a5SCorey Minyard static const struct ipmi_user_hndl ipmi_hndlrs =
831da177e4SLinus Torvalds {
841da177e4SLinus Torvalds 	.ipmi_recv_hndl	= file_receive_handler,
851da177e4SLinus Torvalds };
861da177e4SLinus Torvalds 
ipmi_open(struct inode * inode,struct file * file)871da177e4SLinus Torvalds static int ipmi_open(struct inode *inode, struct file *file)
881da177e4SLinus Torvalds {
891da177e4SLinus Torvalds 	int                      if_num = iminor(inode);
901da177e4SLinus Torvalds 	int                      rv;
911da177e4SLinus Torvalds 	struct ipmi_file_private *priv;
921da177e4SLinus Torvalds 
931da177e4SLinus Torvalds 	priv = kmalloc(sizeof(*priv), GFP_KERNEL);
941da177e4SLinus Torvalds 	if (!priv)
951da177e4SLinus Torvalds 		return -ENOMEM;
961da177e4SLinus Torvalds 
971da177e4SLinus Torvalds 	rv = ipmi_create_user(if_num,
981da177e4SLinus Torvalds 			      &ipmi_hndlrs,
991da177e4SLinus Torvalds 			      priv,
100c81c5fc2SCorey Minyard 			      &priv->user);
1011da177e4SLinus Torvalds 	if (rv) {
1021da177e4SLinus Torvalds 		kfree(priv);
103ecc38983SJonathan Corbet 		goto out;
1041da177e4SLinus Torvalds 	}
1051da177e4SLinus Torvalds 
1061da177e4SLinus Torvalds 	file->private_data = priv;
1071da177e4SLinus Torvalds 
108c81c5fc2SCorey Minyard 	spin_lock_init(&priv->recv_msg_lock);
109c81c5fc2SCorey Minyard 	INIT_LIST_HEAD(&priv->recv_msgs);
1101da177e4SLinus Torvalds 	init_waitqueue_head(&priv->wait);
1111da177e4SLinus Torvalds 	priv->fasync_queue = NULL;
112d6dfd131SCorey Minyard 	mutex_init(&priv->recv_mutex);
1131da177e4SLinus Torvalds 
1141da177e4SLinus Torvalds 	/* Use the low-level defaults. */
1151da177e4SLinus Torvalds 	priv->default_retries = -1;
1161da177e4SLinus Torvalds 	priv->default_retry_time_ms = 0;
1171da177e4SLinus Torvalds 
118ecc38983SJonathan Corbet out:
119ecc38983SJonathan Corbet 	return rv;
1201da177e4SLinus Torvalds }
1211da177e4SLinus Torvalds 
ipmi_release(struct inode * inode,struct file * file)1221da177e4SLinus Torvalds static int ipmi_release(struct inode *inode, struct file *file)
1231da177e4SLinus Torvalds {
1241da177e4SLinus Torvalds 	struct ipmi_file_private *priv = file->private_data;
1251da177e4SLinus Torvalds 	int                      rv;
126bdf2829cSNicholas Krause 	struct ipmi_recv_msg *msg, *next;
1271da177e4SLinus Torvalds 
1281da177e4SLinus Torvalds 	rv = ipmi_destroy_user(priv->user);
1291da177e4SLinus Torvalds 	if (rv)
1301da177e4SLinus Torvalds 		return rv;
1311da177e4SLinus Torvalds 
132bdf2829cSNicholas Krause 	list_for_each_entry_safe(msg, next, &priv->recv_msgs, link)
133bdf2829cSNicholas Krause 		ipmi_free_recv_msg(msg);
134bdf2829cSNicholas Krause 
1351da177e4SLinus Torvalds 	kfree(priv);
1361da177e4SLinus Torvalds 
1371da177e4SLinus Torvalds 	return 0;
1381da177e4SLinus Torvalds }
1391da177e4SLinus Torvalds 
handle_send_req(struct ipmi_user * user,struct ipmi_req * req,int retries,unsigned int retry_time_ms)1402911c988SCorey Minyard static int handle_send_req(struct ipmi_user *user,
1411da177e4SLinus Torvalds 			   struct ipmi_req *req,
1421da177e4SLinus Torvalds 			   int             retries,
1431da177e4SLinus Torvalds 			   unsigned int    retry_time_ms)
1441da177e4SLinus Torvalds {
1451da177e4SLinus Torvalds 	int              rv;
1461da177e4SLinus Torvalds 	struct ipmi_addr addr;
1471da177e4SLinus Torvalds 	struct kernel_ipmi_msg msg;
1481da177e4SLinus Torvalds 
1491da177e4SLinus Torvalds 	if (req->addr_len > sizeof(struct ipmi_addr))
1501da177e4SLinus Torvalds 		return -EINVAL;
1511da177e4SLinus Torvalds 
1521da177e4SLinus Torvalds 	if (copy_from_user(&addr, req->addr, req->addr_len))
1531da177e4SLinus Torvalds 		return -EFAULT;
1541da177e4SLinus Torvalds 
1551da177e4SLinus Torvalds 	msg.netfn = req->msg.netfn;
1561da177e4SLinus Torvalds 	msg.cmd = req->msg.cmd;
1571da177e4SLinus Torvalds 	msg.data_len = req->msg.data_len;
1581da177e4SLinus Torvalds 	msg.data = kmalloc(IPMI_MAX_MSG_LENGTH, GFP_KERNEL);
1591da177e4SLinus Torvalds 	if (!msg.data)
1601da177e4SLinus Torvalds 		return -ENOMEM;
1611da177e4SLinus Torvalds 
1621da177e4SLinus Torvalds 	/* From here out we cannot return, we must jump to "out" for
1631da177e4SLinus Torvalds 	   error exits to free msgdata. */
1641da177e4SLinus Torvalds 
1651da177e4SLinus Torvalds 	rv = ipmi_validate_addr(&addr, req->addr_len);
1661da177e4SLinus Torvalds 	if (rv)
1671da177e4SLinus Torvalds 		goto out;
1681da177e4SLinus Torvalds 
1691da177e4SLinus Torvalds 	if (req->msg.data != NULL) {
1701da177e4SLinus Torvalds 		if (req->msg.data_len > IPMI_MAX_MSG_LENGTH) {
1711da177e4SLinus Torvalds 			rv = -EMSGSIZE;
1721da177e4SLinus Torvalds 			goto out;
1731da177e4SLinus Torvalds 		}
1741da177e4SLinus Torvalds 
1751da177e4SLinus Torvalds 		if (copy_from_user(msg.data,
1761da177e4SLinus Torvalds 				   req->msg.data,
177c81c5fc2SCorey Minyard 				   req->msg.data_len)) {
1781da177e4SLinus Torvalds 			rv = -EFAULT;
1791da177e4SLinus Torvalds 			goto out;
1801da177e4SLinus Torvalds 		}
1811da177e4SLinus Torvalds 	} else {
1821da177e4SLinus Torvalds 		msg.data_len = 0;
1831da177e4SLinus Torvalds 	}
1841da177e4SLinus Torvalds 
1851da177e4SLinus Torvalds 	rv = ipmi_request_settime(user,
1861da177e4SLinus Torvalds 				  &addr,
1871da177e4SLinus Torvalds 				  req->msgid,
1881da177e4SLinus Torvalds 				  &msg,
1891da177e4SLinus Torvalds 				  NULL,
1901da177e4SLinus Torvalds 				  0,
1911da177e4SLinus Torvalds 				  retries,
1921da177e4SLinus Torvalds 				  retry_time_ms);
1931da177e4SLinus Torvalds  out:
1941da177e4SLinus Torvalds 	kfree(msg.data);
1951da177e4SLinus Torvalds 	return rv;
1961da177e4SLinus Torvalds }
1971da177e4SLinus Torvalds 
handle_recv(struct ipmi_file_private * priv,bool trunc,struct ipmi_recv * rsp,int (* copyout)(struct ipmi_recv *,void __user *),void __user * to)1988b9e04f2SAl Viro static int handle_recv(struct ipmi_file_private *priv,
1998b9e04f2SAl Viro 			bool trunc, struct ipmi_recv *rsp,
2008b9e04f2SAl Viro 			int (*copyout)(struct ipmi_recv *, void __user *),
2018b9e04f2SAl Viro 			void __user *to)
2028b9e04f2SAl Viro {
2038b9e04f2SAl Viro 	int              addr_len;
2048b9e04f2SAl Viro 	struct list_head *entry;
2058b9e04f2SAl Viro 	struct ipmi_recv_msg  *msg;
2068b9e04f2SAl Viro 	unsigned long    flags;
20795ac0daaSCorey Minyard 	int rv = 0, rv2 = 0;
2088b9e04f2SAl Viro 
2098b9e04f2SAl Viro 	/* We claim a mutex because we don't want two
2108b9e04f2SAl Viro 	   users getting something from the queue at a time.
2118b9e04f2SAl Viro 	   Since we have to release the spinlock before we can
2128b9e04f2SAl Viro 	   copy the data to the user, it's possible another
2138b9e04f2SAl Viro 	   user will grab something from the queue, too.  Then
2148b9e04f2SAl Viro 	   the messages might get out of order if something
2158b9e04f2SAl Viro 	   fails and the message gets put back onto the
2168b9e04f2SAl Viro 	   queue.  This mutex prevents that problem. */
2178b9e04f2SAl Viro 	mutex_lock(&priv->recv_mutex);
2188b9e04f2SAl Viro 
2198b9e04f2SAl Viro 	/* Grab the message off the list. */
220c81c5fc2SCorey Minyard 	spin_lock_irqsave(&priv->recv_msg_lock, flags);
2218b9e04f2SAl Viro 	if (list_empty(&(priv->recv_msgs))) {
222c81c5fc2SCorey Minyard 		spin_unlock_irqrestore(&priv->recv_msg_lock, flags);
2238b9e04f2SAl Viro 		rv = -EAGAIN;
2248b9e04f2SAl Viro 		goto recv_err;
2258b9e04f2SAl Viro 	}
2268b9e04f2SAl Viro 	entry = priv->recv_msgs.next;
2278b9e04f2SAl Viro 	msg = list_entry(entry, struct ipmi_recv_msg, link);
2288b9e04f2SAl Viro 	list_del(entry);
229c81c5fc2SCorey Minyard 	spin_unlock_irqrestore(&priv->recv_msg_lock, flags);
2308b9e04f2SAl Viro 
2318b9e04f2SAl Viro 	addr_len = ipmi_addr_length(msg->addr.addr_type);
232c81c5fc2SCorey Minyard 	if (rsp->addr_len < addr_len) {
2338b9e04f2SAl Viro 		rv = -EINVAL;
2348b9e04f2SAl Viro 		goto recv_putback_on_err;
2358b9e04f2SAl Viro 	}
2368b9e04f2SAl Viro 
237c81c5fc2SCorey Minyard 	if (copy_to_user(rsp->addr, &msg->addr, addr_len)) {
2388b9e04f2SAl Viro 		rv = -EFAULT;
2398b9e04f2SAl Viro 		goto recv_putback_on_err;
2408b9e04f2SAl Viro 	}
2418b9e04f2SAl Viro 	rsp->addr_len = addr_len;
2428b9e04f2SAl Viro 
2438b9e04f2SAl Viro 	rsp->recv_type = msg->recv_type;
2448b9e04f2SAl Viro 	rsp->msgid = msg->msgid;
2458b9e04f2SAl Viro 	rsp->msg.netfn = msg->msg.netfn;
2468b9e04f2SAl Viro 	rsp->msg.cmd = msg->msg.cmd;
2478b9e04f2SAl Viro 
2488b9e04f2SAl Viro 	if (msg->msg.data_len > 0) {
2498b9e04f2SAl Viro 		if (rsp->msg.data_len < msg->msg.data_len) {
25017a42627SCorey Minyard 			if (trunc) {
25195ac0daaSCorey Minyard 				rv2 = -EMSGSIZE;
2528b9e04f2SAl Viro 				msg->msg.data_len = rsp->msg.data_len;
25317a42627SCorey Minyard 			} else {
25417a42627SCorey Minyard 				rv = -EMSGSIZE;
2558b9e04f2SAl Viro 				goto recv_putback_on_err;
2568b9e04f2SAl Viro 			}
25717a42627SCorey Minyard 		}
2588b9e04f2SAl Viro 
2598b9e04f2SAl Viro 		if (copy_to_user(rsp->msg.data,
2608b9e04f2SAl Viro 				 msg->msg.data,
261c81c5fc2SCorey Minyard 				 msg->msg.data_len)) {
2628b9e04f2SAl Viro 			rv = -EFAULT;
2638b9e04f2SAl Viro 			goto recv_putback_on_err;
2648b9e04f2SAl Viro 		}
2658b9e04f2SAl Viro 		rsp->msg.data_len = msg->msg.data_len;
2668b9e04f2SAl Viro 	} else {
2678b9e04f2SAl Viro 		rsp->msg.data_len = 0;
2688b9e04f2SAl Viro 	}
2698b9e04f2SAl Viro 
2708b9e04f2SAl Viro 	rv = copyout(rsp, to);
2718b9e04f2SAl Viro 	if (rv)
2728b9e04f2SAl Viro 		goto recv_putback_on_err;
2738b9e04f2SAl Viro 
2748b9e04f2SAl Viro 	mutex_unlock(&priv->recv_mutex);
2758b9e04f2SAl Viro 	ipmi_free_recv_msg(msg);
27695ac0daaSCorey Minyard 	return rv2;
2778b9e04f2SAl Viro 
2788b9e04f2SAl Viro recv_putback_on_err:
2798b9e04f2SAl Viro 	/* If we got an error, put the message back onto
2808b9e04f2SAl Viro 	   the head of the queue. */
281c81c5fc2SCorey Minyard 	spin_lock_irqsave(&priv->recv_msg_lock, flags);
282c81c5fc2SCorey Minyard 	list_add(entry, &priv->recv_msgs);
283c81c5fc2SCorey Minyard 	spin_unlock_irqrestore(&priv->recv_msg_lock, flags);
2848b9e04f2SAl Viro recv_err:
2858b9e04f2SAl Viro 	mutex_unlock(&priv->recv_mutex);
2868b9e04f2SAl Viro 	return rv;
2878b9e04f2SAl Viro }
2888b9e04f2SAl Viro 
copyout_recv(struct ipmi_recv * rsp,void __user * to)2898b9e04f2SAl Viro static int copyout_recv(struct ipmi_recv *rsp, void __user *to)
2908b9e04f2SAl Viro {
2918b9e04f2SAl Viro 	return copy_to_user(to, rsp, sizeof(struct ipmi_recv)) ? -EFAULT : 0;
2928b9e04f2SAl Viro }
2938b9e04f2SAl Viro 
ipmi_ioctl(struct file * file,unsigned int cmd,unsigned long data)2946aa2dd00SCorey Minyard static long ipmi_ioctl(struct file   *file,
2951da177e4SLinus Torvalds 		       unsigned int  cmd,
2961da177e4SLinus Torvalds 		       unsigned long data)
2971da177e4SLinus Torvalds {
2981da177e4SLinus Torvalds 	int                      rv = -EINVAL;
2991da177e4SLinus Torvalds 	struct ipmi_file_private *priv = file->private_data;
3001da177e4SLinus Torvalds 	void __user *arg = (void __user *)data;
3011da177e4SLinus Torvalds 
3021da177e4SLinus Torvalds 	switch (cmd)
3031da177e4SLinus Torvalds 	{
3041da177e4SLinus Torvalds 	case IPMICTL_SEND_COMMAND:
3051da177e4SLinus Torvalds 	{
3061da177e4SLinus Torvalds 		struct ipmi_req req;
3076aa2dd00SCorey Minyard 		int retries;
3086aa2dd00SCorey Minyard 		unsigned int retry_time_ms;
3091da177e4SLinus Torvalds 
3101da177e4SLinus Torvalds 		if (copy_from_user(&req, arg, sizeof(req))) {
3111da177e4SLinus Torvalds 			rv = -EFAULT;
3121da177e4SLinus Torvalds 			break;
3131da177e4SLinus Torvalds 		}
3141da177e4SLinus Torvalds 
3156aa2dd00SCorey Minyard 		mutex_lock(&priv->recv_mutex);
3166aa2dd00SCorey Minyard 		retries = priv->default_retries;
3176aa2dd00SCorey Minyard 		retry_time_ms = priv->default_retry_time_ms;
3186aa2dd00SCorey Minyard 		mutex_unlock(&priv->recv_mutex);
3196aa2dd00SCorey Minyard 
3206aa2dd00SCorey Minyard 		rv = handle_send_req(priv->user, &req, retries, retry_time_ms);
3211da177e4SLinus Torvalds 		break;
3221da177e4SLinus Torvalds 	}
3231da177e4SLinus Torvalds 
3241da177e4SLinus Torvalds 	case IPMICTL_SEND_COMMAND_SETTIME:
3251da177e4SLinus Torvalds 	{
3261da177e4SLinus Torvalds 		struct ipmi_req_settime req;
3271da177e4SLinus Torvalds 
3281da177e4SLinus Torvalds 		if (copy_from_user(&req, arg, sizeof(req))) {
3291da177e4SLinus Torvalds 			rv = -EFAULT;
3301da177e4SLinus Torvalds 			break;
3311da177e4SLinus Torvalds 		}
3321da177e4SLinus Torvalds 
3331da177e4SLinus Torvalds 		rv = handle_send_req(priv->user,
3341da177e4SLinus Torvalds 				     &req.req,
3351da177e4SLinus Torvalds 				     req.retries,
3361da177e4SLinus Torvalds 				     req.retry_time_ms);
3371da177e4SLinus Torvalds 		break;
3381da177e4SLinus Torvalds 	}
3391da177e4SLinus Torvalds 
3401da177e4SLinus Torvalds 	case IPMICTL_RECEIVE_MSG:
3411da177e4SLinus Torvalds 	case IPMICTL_RECEIVE_MSG_TRUNC:
3421da177e4SLinus Torvalds 	{
3431da177e4SLinus Torvalds 		struct ipmi_recv      rsp;
3441da177e4SLinus Torvalds 
3458b9e04f2SAl Viro 		if (copy_from_user(&rsp, arg, sizeof(rsp)))
3461da177e4SLinus Torvalds 			rv = -EFAULT;
3478b9e04f2SAl Viro 		else
3488b9e04f2SAl Viro 			rv = handle_recv(priv, cmd == IPMICTL_RECEIVE_MSG_TRUNC,
3498b9e04f2SAl Viro 					 &rsp, copyout_recv, arg);
3501da177e4SLinus Torvalds 		break;
3511da177e4SLinus Torvalds 	}
3521da177e4SLinus Torvalds 
3531da177e4SLinus Torvalds 	case IPMICTL_REGISTER_FOR_CMD:
3541da177e4SLinus Torvalds 	{
3551da177e4SLinus Torvalds 		struct ipmi_cmdspec val;
3561da177e4SLinus Torvalds 
3571da177e4SLinus Torvalds 		if (copy_from_user(&val, arg, sizeof(val))) {
3581da177e4SLinus Torvalds 			rv = -EFAULT;
3591da177e4SLinus Torvalds 			break;
3601da177e4SLinus Torvalds 		}
3611da177e4SLinus Torvalds 
362c69c3127SCorey Minyard 		rv = ipmi_register_for_cmd(priv->user, val.netfn, val.cmd,
363c69c3127SCorey Minyard 					   IPMI_CHAN_ALL);
3641da177e4SLinus Torvalds 		break;
3651da177e4SLinus Torvalds 	}
3661da177e4SLinus Torvalds 
3671da177e4SLinus Torvalds 	case IPMICTL_UNREGISTER_FOR_CMD:
3681da177e4SLinus Torvalds 	{
3691da177e4SLinus Torvalds 		struct ipmi_cmdspec   val;
3701da177e4SLinus Torvalds 
3711da177e4SLinus Torvalds 		if (copy_from_user(&val, arg, sizeof(val))) {
3721da177e4SLinus Torvalds 			rv = -EFAULT;
3731da177e4SLinus Torvalds 			break;
3741da177e4SLinus Torvalds 		}
3751da177e4SLinus Torvalds 
376c69c3127SCorey Minyard 		rv = ipmi_unregister_for_cmd(priv->user, val.netfn, val.cmd,
377c69c3127SCorey Minyard 					     IPMI_CHAN_ALL);
378c69c3127SCorey Minyard 		break;
379c69c3127SCorey Minyard 	}
380c69c3127SCorey Minyard 
381c69c3127SCorey Minyard 	case IPMICTL_REGISTER_FOR_CMD_CHANS:
382c69c3127SCorey Minyard 	{
383c69c3127SCorey Minyard 		struct ipmi_cmdspec_chans val;
384c69c3127SCorey Minyard 
385c69c3127SCorey Minyard 		if (copy_from_user(&val, arg, sizeof(val))) {
386c69c3127SCorey Minyard 			rv = -EFAULT;
387c69c3127SCorey Minyard 			break;
388c69c3127SCorey Minyard 		}
389c69c3127SCorey Minyard 
390c69c3127SCorey Minyard 		rv = ipmi_register_for_cmd(priv->user, val.netfn, val.cmd,
391c69c3127SCorey Minyard 					   val.chans);
392c69c3127SCorey Minyard 		break;
393c69c3127SCorey Minyard 	}
394c69c3127SCorey Minyard 
395c69c3127SCorey Minyard 	case IPMICTL_UNREGISTER_FOR_CMD_CHANS:
396c69c3127SCorey Minyard 	{
397c69c3127SCorey Minyard 		struct ipmi_cmdspec_chans val;
398c69c3127SCorey Minyard 
399c69c3127SCorey Minyard 		if (copy_from_user(&val, arg, sizeof(val))) {
400c69c3127SCorey Minyard 			rv = -EFAULT;
401c69c3127SCorey Minyard 			break;
402c69c3127SCorey Minyard 		}
403c69c3127SCorey Minyard 
404c69c3127SCorey Minyard 		rv = ipmi_unregister_for_cmd(priv->user, val.netfn, val.cmd,
405c69c3127SCorey Minyard 					     val.chans);
4061da177e4SLinus Torvalds 		break;
4071da177e4SLinus Torvalds 	}
4081da177e4SLinus Torvalds 
4091da177e4SLinus Torvalds 	case IPMICTL_SET_GETS_EVENTS_CMD:
4101da177e4SLinus Torvalds 	{
4111da177e4SLinus Torvalds 		int val;
4121da177e4SLinus Torvalds 
4131da177e4SLinus Torvalds 		if (copy_from_user(&val, arg, sizeof(val))) {
4141da177e4SLinus Torvalds 			rv = -EFAULT;
4151da177e4SLinus Torvalds 			break;
4161da177e4SLinus Torvalds 		}
4171da177e4SLinus Torvalds 
4181da177e4SLinus Torvalds 		rv = ipmi_set_gets_events(priv->user, val);
4191da177e4SLinus Torvalds 		break;
4201da177e4SLinus Torvalds 	}
4211da177e4SLinus Torvalds 
422c14979b9SCorey Minyard 	/* The next four are legacy, not per-channel. */
4231da177e4SLinus Torvalds 	case IPMICTL_SET_MY_ADDRESS_CMD:
4241da177e4SLinus Torvalds 	{
4251da177e4SLinus Torvalds 		unsigned int val;
4261da177e4SLinus Torvalds 
4271da177e4SLinus Torvalds 		if (copy_from_user(&val, arg, sizeof(val))) {
4281da177e4SLinus Torvalds 			rv = -EFAULT;
4291da177e4SLinus Torvalds 			break;
4301da177e4SLinus Torvalds 		}
4311da177e4SLinus Torvalds 
432c14979b9SCorey Minyard 		rv = ipmi_set_my_address(priv->user, 0, val);
4331da177e4SLinus Torvalds 		break;
4341da177e4SLinus Torvalds 	}
4351da177e4SLinus Torvalds 
4361da177e4SLinus Torvalds 	case IPMICTL_GET_MY_ADDRESS_CMD:
4371da177e4SLinus Torvalds 	{
4381da177e4SLinus Torvalds 		unsigned int  val;
439c14979b9SCorey Minyard 		unsigned char rval;
4401da177e4SLinus Torvalds 
441c14979b9SCorey Minyard 		rv = ipmi_get_my_address(priv->user, 0, &rval);
442c14979b9SCorey Minyard 		if (rv)
443c14979b9SCorey Minyard 			break;
444c14979b9SCorey Minyard 
445c14979b9SCorey Minyard 		val = rval;
4461da177e4SLinus Torvalds 
4471da177e4SLinus Torvalds 		if (copy_to_user(arg, &val, sizeof(val))) {
4481da177e4SLinus Torvalds 			rv = -EFAULT;
4491da177e4SLinus Torvalds 			break;
4501da177e4SLinus Torvalds 		}
4511da177e4SLinus Torvalds 		break;
4521da177e4SLinus Torvalds 	}
4531da177e4SLinus Torvalds 
4541da177e4SLinus Torvalds 	case IPMICTL_SET_MY_LUN_CMD:
4551da177e4SLinus Torvalds 	{
4561da177e4SLinus Torvalds 		unsigned int val;
4571da177e4SLinus Torvalds 
4581da177e4SLinus Torvalds 		if (copy_from_user(&val, arg, sizeof(val))) {
4591da177e4SLinus Torvalds 			rv = -EFAULT;
4601da177e4SLinus Torvalds 			break;
4611da177e4SLinus Torvalds 		}
4621da177e4SLinus Torvalds 
463c14979b9SCorey Minyard 		rv = ipmi_set_my_LUN(priv->user, 0, val);
4641da177e4SLinus Torvalds 		break;
4651da177e4SLinus Torvalds 	}
4661da177e4SLinus Torvalds 
4671da177e4SLinus Torvalds 	case IPMICTL_GET_MY_LUN_CMD:
4681da177e4SLinus Torvalds 	{
4691da177e4SLinus Torvalds 		unsigned int  val;
470c14979b9SCorey Minyard 		unsigned char rval;
4711da177e4SLinus Torvalds 
472c14979b9SCorey Minyard 		rv = ipmi_get_my_LUN(priv->user, 0, &rval);
473c14979b9SCorey Minyard 		if (rv)
474c14979b9SCorey Minyard 			break;
475c14979b9SCorey Minyard 
476c14979b9SCorey Minyard 		val = rval;
4771da177e4SLinus Torvalds 
4781da177e4SLinus Torvalds 		if (copy_to_user(arg, &val, sizeof(val))) {
4791da177e4SLinus Torvalds 			rv = -EFAULT;
4801da177e4SLinus Torvalds 			break;
4811da177e4SLinus Torvalds 		}
4821da177e4SLinus Torvalds 		break;
4831da177e4SLinus Torvalds 	}
484c14979b9SCorey Minyard 
485c14979b9SCorey Minyard 	case IPMICTL_SET_MY_CHANNEL_ADDRESS_CMD:
486c14979b9SCorey Minyard 	{
487c14979b9SCorey Minyard 		struct ipmi_channel_lun_address_set val;
488c14979b9SCorey Minyard 
489c14979b9SCorey Minyard 		if (copy_from_user(&val, arg, sizeof(val))) {
490c14979b9SCorey Minyard 			rv = -EFAULT;
491c14979b9SCorey Minyard 			break;
492c14979b9SCorey Minyard 		}
493c14979b9SCorey Minyard 
494c14979b9SCorey Minyard 		return ipmi_set_my_address(priv->user, val.channel, val.value);
495c14979b9SCorey Minyard 	}
496c14979b9SCorey Minyard 
497c14979b9SCorey Minyard 	case IPMICTL_GET_MY_CHANNEL_ADDRESS_CMD:
498c14979b9SCorey Minyard 	{
499c14979b9SCorey Minyard 		struct ipmi_channel_lun_address_set val;
500c14979b9SCorey Minyard 
501c14979b9SCorey Minyard 		if (copy_from_user(&val, arg, sizeof(val))) {
502c14979b9SCorey Minyard 			rv = -EFAULT;
503c14979b9SCorey Minyard 			break;
504c14979b9SCorey Minyard 		}
505c14979b9SCorey Minyard 
506c14979b9SCorey Minyard 		rv = ipmi_get_my_address(priv->user, val.channel, &val.value);
507c14979b9SCorey Minyard 		if (rv)
508c14979b9SCorey Minyard 			break;
509c14979b9SCorey Minyard 
510c14979b9SCorey Minyard 		if (copy_to_user(arg, &val, sizeof(val))) {
511c14979b9SCorey Minyard 			rv = -EFAULT;
512c14979b9SCorey Minyard 			break;
513c14979b9SCorey Minyard 		}
514c14979b9SCorey Minyard 		break;
515c14979b9SCorey Minyard 	}
516c14979b9SCorey Minyard 
517c14979b9SCorey Minyard 	case IPMICTL_SET_MY_CHANNEL_LUN_CMD:
518c14979b9SCorey Minyard 	{
519c14979b9SCorey Minyard 		struct ipmi_channel_lun_address_set val;
520c14979b9SCorey Minyard 
521c14979b9SCorey Minyard 		if (copy_from_user(&val, arg, sizeof(val))) {
522c14979b9SCorey Minyard 			rv = -EFAULT;
523c14979b9SCorey Minyard 			break;
524c14979b9SCorey Minyard 		}
525c14979b9SCorey Minyard 
526c14979b9SCorey Minyard 		rv = ipmi_set_my_LUN(priv->user, val.channel, val.value);
527c14979b9SCorey Minyard 		break;
528c14979b9SCorey Minyard 	}
529c14979b9SCorey Minyard 
530c14979b9SCorey Minyard 	case IPMICTL_GET_MY_CHANNEL_LUN_CMD:
531c14979b9SCorey Minyard 	{
532c14979b9SCorey Minyard 		struct ipmi_channel_lun_address_set val;
533c14979b9SCorey Minyard 
534c14979b9SCorey Minyard 		if (copy_from_user(&val, arg, sizeof(val))) {
535c14979b9SCorey Minyard 			rv = -EFAULT;
536c14979b9SCorey Minyard 			break;
537c14979b9SCorey Minyard 		}
538c14979b9SCorey Minyard 
539c14979b9SCorey Minyard 		rv = ipmi_get_my_LUN(priv->user, val.channel, &val.value);
540c14979b9SCorey Minyard 		if (rv)
541c14979b9SCorey Minyard 			break;
542c14979b9SCorey Minyard 
543c14979b9SCorey Minyard 		if (copy_to_user(arg, &val, sizeof(val))) {
544c14979b9SCorey Minyard 			rv = -EFAULT;
545c14979b9SCorey Minyard 			break;
546c14979b9SCorey Minyard 		}
547c14979b9SCorey Minyard 		break;
548c14979b9SCorey Minyard 	}
549c14979b9SCorey Minyard 
5501da177e4SLinus Torvalds 	case IPMICTL_SET_TIMING_PARMS_CMD:
5511da177e4SLinus Torvalds 	{
5521da177e4SLinus Torvalds 		struct ipmi_timing_parms parms;
5531da177e4SLinus Torvalds 
5541da177e4SLinus Torvalds 		if (copy_from_user(&parms, arg, sizeof(parms))) {
5551da177e4SLinus Torvalds 			rv = -EFAULT;
5561da177e4SLinus Torvalds 			break;
5571da177e4SLinus Torvalds 		}
5581da177e4SLinus Torvalds 
5596aa2dd00SCorey Minyard 		mutex_lock(&priv->recv_mutex);
5601da177e4SLinus Torvalds 		priv->default_retries = parms.retries;
5611da177e4SLinus Torvalds 		priv->default_retry_time_ms = parms.retry_time_ms;
5626aa2dd00SCorey Minyard 		mutex_unlock(&priv->recv_mutex);
5631da177e4SLinus Torvalds 		rv = 0;
5641da177e4SLinus Torvalds 		break;
5651da177e4SLinus Torvalds 	}
5661da177e4SLinus Torvalds 
5671da177e4SLinus Torvalds 	case IPMICTL_GET_TIMING_PARMS_CMD:
5681da177e4SLinus Torvalds 	{
5691da177e4SLinus Torvalds 		struct ipmi_timing_parms parms;
5701da177e4SLinus Torvalds 
5716aa2dd00SCorey Minyard 		mutex_lock(&priv->recv_mutex);
5721da177e4SLinus Torvalds 		parms.retries = priv->default_retries;
5731da177e4SLinus Torvalds 		parms.retry_time_ms = priv->default_retry_time_ms;
5746aa2dd00SCorey Minyard 		mutex_unlock(&priv->recv_mutex);
5751da177e4SLinus Torvalds 
5761da177e4SLinus Torvalds 		if (copy_to_user(arg, &parms, sizeof(parms))) {
5771da177e4SLinus Torvalds 			rv = -EFAULT;
5781da177e4SLinus Torvalds 			break;
5791da177e4SLinus Torvalds 		}
5801da177e4SLinus Torvalds 
5811da177e4SLinus Torvalds 		rv = 0;
5821da177e4SLinus Torvalds 		break;
5831da177e4SLinus Torvalds 	}
584b9675136SCorey Minyard 
585b9675136SCorey Minyard 	case IPMICTL_GET_MAINTENANCE_MODE_CMD:
586b9675136SCorey Minyard 	{
587b9675136SCorey Minyard 		int mode;
588b9675136SCorey Minyard 
589b9675136SCorey Minyard 		mode = ipmi_get_maintenance_mode(priv->user);
590b9675136SCorey Minyard 		if (copy_to_user(arg, &mode, sizeof(mode))) {
591b9675136SCorey Minyard 			rv = -EFAULT;
592b9675136SCorey Minyard 			break;
593b9675136SCorey Minyard 		}
594b9675136SCorey Minyard 		rv = 0;
595b9675136SCorey Minyard 		break;
596b9675136SCorey Minyard 	}
597b9675136SCorey Minyard 
598b9675136SCorey Minyard 	case IPMICTL_SET_MAINTENANCE_MODE_CMD:
599b9675136SCorey Minyard 	{
600b9675136SCorey Minyard 		int mode;
601b9675136SCorey Minyard 
602b9675136SCorey Minyard 		if (copy_from_user(&mode, arg, sizeof(mode))) {
603b9675136SCorey Minyard 			rv = -EFAULT;
604b9675136SCorey Minyard 			break;
605b9675136SCorey Minyard 		}
606b9675136SCorey Minyard 		rv = ipmi_set_maintenance_mode(priv->user, mode);
607b9675136SCorey Minyard 		break;
608b9675136SCorey Minyard 	}
609e56710d2SCorey Minyard 
610e56710d2SCorey Minyard 	default:
611e56710d2SCorey Minyard 		rv = -ENOTTY;
612e56710d2SCorey Minyard 		break;
6131da177e4SLinus Torvalds 	}
6141da177e4SLinus Torvalds 
6151da177e4SLinus Torvalds 	return rv;
6161da177e4SLinus Torvalds }
6171da177e4SLinus Torvalds 
6186a94f920SAndrew Morton #ifdef CONFIG_COMPAT
6196a94f920SAndrew Morton /*
6206a94f920SAndrew Morton  * The following code contains code for supporting 32-bit compatible
6216a94f920SAndrew Morton  * ioctls on 64-bit kernels.  This allows running 32-bit apps on the
6226a94f920SAndrew Morton  * 64-bit kernel
6236a94f920SAndrew Morton  */
6246a94f920SAndrew Morton #define COMPAT_IPMICTL_SEND_COMMAND	\
6256a94f920SAndrew Morton 	_IOR(IPMI_IOC_MAGIC, 13, struct compat_ipmi_req)
6266a94f920SAndrew Morton #define COMPAT_IPMICTL_SEND_COMMAND_SETTIME	\
6276a94f920SAndrew Morton 	_IOR(IPMI_IOC_MAGIC, 21, struct compat_ipmi_req_settime)
6286a94f920SAndrew Morton #define COMPAT_IPMICTL_RECEIVE_MSG	\
6296a94f920SAndrew Morton 	_IOWR(IPMI_IOC_MAGIC, 12, struct compat_ipmi_recv)
6306a94f920SAndrew Morton #define COMPAT_IPMICTL_RECEIVE_MSG_TRUNC	\
6316a94f920SAndrew Morton 	_IOWR(IPMI_IOC_MAGIC, 11, struct compat_ipmi_recv)
6326a94f920SAndrew Morton 
6336a94f920SAndrew Morton struct compat_ipmi_msg {
6346a94f920SAndrew Morton 	u8		netfn;
6356a94f920SAndrew Morton 	u8		cmd;
6366a94f920SAndrew Morton 	u16		data_len;
6376a94f920SAndrew Morton 	compat_uptr_t	data;
6386a94f920SAndrew Morton };
6396a94f920SAndrew Morton 
6406a94f920SAndrew Morton struct compat_ipmi_req {
6416a94f920SAndrew Morton 	compat_uptr_t		addr;
6426a94f920SAndrew Morton 	compat_uint_t		addr_len;
6436a94f920SAndrew Morton 	compat_long_t		msgid;
6446a94f920SAndrew Morton 	struct compat_ipmi_msg	msg;
6456a94f920SAndrew Morton };
6466a94f920SAndrew Morton 
6476a94f920SAndrew Morton struct compat_ipmi_recv {
6486a94f920SAndrew Morton 	compat_int_t		recv_type;
6496a94f920SAndrew Morton 	compat_uptr_t		addr;
6506a94f920SAndrew Morton 	compat_uint_t		addr_len;
6516a94f920SAndrew Morton 	compat_long_t		msgid;
6526a94f920SAndrew Morton 	struct compat_ipmi_msg	msg;
6536a94f920SAndrew Morton };
6546a94f920SAndrew Morton 
6556a94f920SAndrew Morton struct compat_ipmi_req_settime {
6566a94f920SAndrew Morton 	struct compat_ipmi_req	req;
6576a94f920SAndrew Morton 	compat_int_t		retries;
6586a94f920SAndrew Morton 	compat_uint_t		retry_time_ms;
6596a94f920SAndrew Morton };
6606a94f920SAndrew Morton 
6616a94f920SAndrew Morton /*
6626a94f920SAndrew Morton  * Define some helper functions for copying IPMI data
6636a94f920SAndrew Morton  */
get_compat_ipmi_msg(struct ipmi_msg * p64,struct compat_ipmi_msg * p32)664e5f699d4SAl Viro static void get_compat_ipmi_msg(struct ipmi_msg *p64,
665e5f699d4SAl Viro 				struct compat_ipmi_msg *p32)
6666a94f920SAndrew Morton {
667e5f699d4SAl Viro 	p64->netfn = p32->netfn;
668e5f699d4SAl Viro 	p64->cmd = p32->cmd;
669e5f699d4SAl Viro 	p64->data_len = p32->data_len;
670e5f699d4SAl Viro 	p64->data = compat_ptr(p32->data);
6716a94f920SAndrew Morton }
6726a94f920SAndrew Morton 
get_compat_ipmi_req(struct ipmi_req * p64,struct compat_ipmi_req * p32)673e5f699d4SAl Viro static void get_compat_ipmi_req(struct ipmi_req *p64,
674e5f699d4SAl Viro 				struct compat_ipmi_req *p32)
6756a94f920SAndrew Morton {
676e5f699d4SAl Viro 	p64->addr = compat_ptr(p32->addr);
677e5f699d4SAl Viro 	p64->addr_len = p32->addr_len;
678e5f699d4SAl Viro 	p64->msgid = p32->msgid;
679e5f699d4SAl Viro 	get_compat_ipmi_msg(&p64->msg, &p32->msg);
6806a94f920SAndrew Morton }
6816a94f920SAndrew Morton 
get_compat_ipmi_req_settime(struct ipmi_req_settime * p64,struct compat_ipmi_req_settime * p32)682e5f699d4SAl Viro static void get_compat_ipmi_req_settime(struct ipmi_req_settime *p64,
683e5f699d4SAl Viro 		struct compat_ipmi_req_settime *p32)
6846a94f920SAndrew Morton {
685e5f699d4SAl Viro 	get_compat_ipmi_req(&p64->req, &p32->req);
686e5f699d4SAl Viro 	p64->retries = p32->retries;
687e5f699d4SAl Viro 	p64->retry_time_ms = p32->retry_time_ms;
6886a94f920SAndrew Morton }
6896a94f920SAndrew Morton 
get_compat_ipmi_recv(struct ipmi_recv * p64,struct compat_ipmi_recv * p32)690e5f699d4SAl Viro static void get_compat_ipmi_recv(struct ipmi_recv *p64,
691e5f699d4SAl Viro 				 struct compat_ipmi_recv *p32)
6926a94f920SAndrew Morton {
693e5f699d4SAl Viro 	memset(p64, 0, sizeof(struct ipmi_recv));
694e5f699d4SAl Viro 	p64->recv_type = p32->recv_type;
695e5f699d4SAl Viro 	p64->addr = compat_ptr(p32->addr);
696e5f699d4SAl Viro 	p64->addr_len = p32->addr_len;
697e5f699d4SAl Viro 	p64->msgid = p32->msgid;
698e5f699d4SAl Viro 	get_compat_ipmi_msg(&p64->msg, &p32->msg);
6996a94f920SAndrew Morton }
7006a94f920SAndrew Morton 
copyout_recv32(struct ipmi_recv * p64,void __user * to)7018b9e04f2SAl Viro static int copyout_recv32(struct ipmi_recv *p64, void __user *to)
7026a94f920SAndrew Morton {
7038b9e04f2SAl Viro 	struct compat_ipmi_recv v32;
7048b9e04f2SAl Viro 	memset(&v32, 0, sizeof(struct compat_ipmi_recv));
7058b9e04f2SAl Viro 	v32.recv_type = p64->recv_type;
7068b9e04f2SAl Viro 	v32.addr = ptr_to_compat(p64->addr);
7078b9e04f2SAl Viro 	v32.addr_len = p64->addr_len;
7088b9e04f2SAl Viro 	v32.msgid = p64->msgid;
7098b9e04f2SAl Viro 	v32.msg.netfn = p64->msg.netfn;
7108b9e04f2SAl Viro 	v32.msg.cmd = p64->msg.cmd;
7118b9e04f2SAl Viro 	v32.msg.data_len = p64->msg.data_len;
7128b9e04f2SAl Viro 	v32.msg.data = ptr_to_compat(p64->msg.data);
7138b9e04f2SAl Viro 	return copy_to_user(to, &v32, sizeof(v32)) ? -EFAULT : 0;
7146a94f920SAndrew Morton }
7156a94f920SAndrew Morton 
7166a94f920SAndrew Morton /*
7176a94f920SAndrew Morton  * Handle compatibility ioctls
7186a94f920SAndrew Morton  */
compat_ipmi_ioctl(struct file * filep,unsigned int cmd,unsigned long arg)7196a94f920SAndrew Morton static long compat_ipmi_ioctl(struct file *filep, unsigned int cmd,
7206a94f920SAndrew Morton 			      unsigned long arg)
7216a94f920SAndrew Morton {
7226a94f920SAndrew Morton 	struct ipmi_file_private *priv = filep->private_data;
7236a94f920SAndrew Morton 
7246a94f920SAndrew Morton 	switch(cmd) {
7256a94f920SAndrew Morton 	case COMPAT_IPMICTL_SEND_COMMAND:
7266a94f920SAndrew Morton 	{
7276a94f920SAndrew Morton 		struct ipmi_req	rp;
728e5f699d4SAl Viro 		struct compat_ipmi_req r32;
7296aa2dd00SCorey Minyard 		int retries;
7306aa2dd00SCorey Minyard 		unsigned int retry_time_ms;
7316a94f920SAndrew Morton 
732e5f699d4SAl Viro 		if (copy_from_user(&r32, compat_ptr(arg), sizeof(r32)))
7336a94f920SAndrew Morton 			return -EFAULT;
7346a94f920SAndrew Morton 
735e5f699d4SAl Viro 		get_compat_ipmi_req(&rp, &r32);
736e5f699d4SAl Viro 
7376aa2dd00SCorey Minyard 		mutex_lock(&priv->recv_mutex);
7386aa2dd00SCorey Minyard 		retries = priv->default_retries;
7396aa2dd00SCorey Minyard 		retry_time_ms = priv->default_retry_time_ms;
7406aa2dd00SCorey Minyard 		mutex_unlock(&priv->recv_mutex);
7416aa2dd00SCorey Minyard 
7426a94f920SAndrew Morton 		return handle_send_req(priv->user, &rp,
7436aa2dd00SCorey Minyard 				       retries, retry_time_ms);
7446a94f920SAndrew Morton 	}
7456a94f920SAndrew Morton 	case COMPAT_IPMICTL_SEND_COMMAND_SETTIME:
7466a94f920SAndrew Morton 	{
7476a94f920SAndrew Morton 		struct ipmi_req_settime	sp;
748e5f699d4SAl Viro 		struct compat_ipmi_req_settime sp32;
7496a94f920SAndrew Morton 
750e5f699d4SAl Viro 		if (copy_from_user(&sp32, compat_ptr(arg), sizeof(sp32)))
7516a94f920SAndrew Morton 			return -EFAULT;
7526a94f920SAndrew Morton 
753e5f699d4SAl Viro 		get_compat_ipmi_req_settime(&sp, &sp32);
754e5f699d4SAl Viro 
7556a94f920SAndrew Morton 		return handle_send_req(priv->user, &sp.req,
7566a94f920SAndrew Morton 				sp.retries, sp.retry_time_ms);
7576a94f920SAndrew Morton 	}
7586a94f920SAndrew Morton 	case COMPAT_IPMICTL_RECEIVE_MSG:
7596a94f920SAndrew Morton 	case COMPAT_IPMICTL_RECEIVE_MSG_TRUNC:
7606a94f920SAndrew Morton 	{
76118fb9442Sviro@ZenIV.linux.org.uk 		struct ipmi_recv   recv64;
762e5f699d4SAl Viro 		struct compat_ipmi_recv recv32;
7636a94f920SAndrew Morton 
764e5f699d4SAl Viro 		if (copy_from_user(&recv32, compat_ptr(arg), sizeof(recv32)))
7656a94f920SAndrew Morton 			return -EFAULT;
7666a94f920SAndrew Morton 
767e5f699d4SAl Viro 		get_compat_ipmi_recv(&recv64, &recv32);
768e5f699d4SAl Viro 
7698b9e04f2SAl Viro 		return handle_recv(priv,
7708b9e04f2SAl Viro 				 cmd == COMPAT_IPMICTL_RECEIVE_MSG_TRUNC,
7718b9e04f2SAl Viro 				 &recv64, copyout_recv32, compat_ptr(arg));
7726a94f920SAndrew Morton 	}
7736a94f920SAndrew Morton 	default:
77455929332SArnd Bergmann 		return ipmi_ioctl(filep, cmd, arg);
7756a94f920SAndrew Morton 	}
7766a94f920SAndrew Morton }
7776a94f920SAndrew Morton #endif
7781da177e4SLinus Torvalds 
77962322d25SArjan van de Ven static const struct file_operations ipmi_fops = {
7801da177e4SLinus Torvalds 	.owner		= THIS_MODULE,
7816aa2dd00SCorey Minyard 	.unlocked_ioctl	= ipmi_ioctl,
7826a94f920SAndrew Morton #ifdef CONFIG_COMPAT
7836aa2dd00SCorey Minyard 	.compat_ioctl   = compat_ipmi_ioctl,
7846a94f920SAndrew Morton #endif
7851da177e4SLinus Torvalds 	.open		= ipmi_open,
7861da177e4SLinus Torvalds 	.release	= ipmi_release,
7871da177e4SLinus Torvalds 	.fasync		= ipmi_fasync,
7881da177e4SLinus Torvalds 	.poll		= ipmi_poll,
7896038f373SArnd Bergmann 	.llseek		= noop_llseek,
7901da177e4SLinus Torvalds };
7911da177e4SLinus Torvalds 
7921da177e4SLinus Torvalds #define DEVICE_NAME     "ipmidev"
7931da177e4SLinus Torvalds 
7940c8204b3SRandy Dunlap static int ipmi_major;
7951da177e4SLinus Torvalds module_param(ipmi_major, int, 0);
7961da177e4SLinus Torvalds MODULE_PARM_DESC(ipmi_major, "Sets the major number of the IPMI device.  By"
7971da177e4SLinus Torvalds 		 " default, or if you set it to zero, it will choose the next"
7981da177e4SLinus Torvalds 		 " available device.  Setting it to -1 will disable the"
7991da177e4SLinus Torvalds 		 " interface.  Other values will set the major device number"
8001da177e4SLinus Torvalds 		 " to that value.");
8011da177e4SLinus Torvalds 
80250c812b2SCorey Minyard /* Keep track of the devices that are registered. */
80350c812b2SCorey Minyard struct ipmi_reg_list {
80450c812b2SCorey Minyard 	dev_t            dev;
80550c812b2SCorey Minyard 	struct list_head link;
80650c812b2SCorey Minyard };
80750c812b2SCorey Minyard static LIST_HEAD(reg_list);
80850c812b2SCorey Minyard static DEFINE_MUTEX(reg_list_mutex);
80950c812b2SCorey Minyard 
810*392fa3a3SIvan Orlov static const struct class ipmi_class = {
811*392fa3a3SIvan Orlov 	.name = "ipmi",
812*392fa3a3SIvan Orlov };
81337e0915bSCorey Minyard 
ipmi_new_smi(int if_num,struct device * device)81450c812b2SCorey Minyard static void ipmi_new_smi(int if_num, struct device *device)
8151da177e4SLinus Torvalds {
81637e0915bSCorey Minyard 	dev_t dev = MKDEV(ipmi_major, if_num);
81750c812b2SCorey Minyard 	struct ipmi_reg_list *entry;
81837e0915bSCorey Minyard 
81950c812b2SCorey Minyard 	entry = kmalloc(sizeof(*entry), GFP_KERNEL);
82050c812b2SCorey Minyard 	if (!entry) {
821f993cdd9SJoe Perches 		pr_err("ipmi_devintf: Unable to create the ipmi class device link\n");
82250c812b2SCorey Minyard 		return;
82350c812b2SCorey Minyard 	}
82450c812b2SCorey Minyard 	entry->dev = dev;
82550c812b2SCorey Minyard 
82650c812b2SCorey Minyard 	mutex_lock(&reg_list_mutex);
827*392fa3a3SIvan Orlov 	device_create(&ipmi_class, device, dev, NULL, "ipmi%d", if_num);
82850c812b2SCorey Minyard 	list_add(&entry->link, &reg_list);
82950c812b2SCorey Minyard 	mutex_unlock(&reg_list_mutex);
8301da177e4SLinus Torvalds }
8311da177e4SLinus Torvalds 
ipmi_smi_gone(int if_num)8321da177e4SLinus Torvalds static void ipmi_smi_gone(int if_num)
8331da177e4SLinus Torvalds {
83450c812b2SCorey Minyard 	dev_t dev = MKDEV(ipmi_major, if_num);
83550c812b2SCorey Minyard 	struct ipmi_reg_list *entry;
83650c812b2SCorey Minyard 
83750c812b2SCorey Minyard 	mutex_lock(&reg_list_mutex);
83850c812b2SCorey Minyard 	list_for_each_entry(entry, &reg_list, link) {
83950c812b2SCorey Minyard 		if (entry->dev == dev) {
84050c812b2SCorey Minyard 			list_del(&entry->link);
84150c812b2SCorey Minyard 			kfree(entry);
84250c812b2SCorey Minyard 			break;
84350c812b2SCorey Minyard 		}
84450c812b2SCorey Minyard 	}
845*392fa3a3SIvan Orlov 	device_destroy(&ipmi_class, dev);
84650c812b2SCorey Minyard 	mutex_unlock(&reg_list_mutex);
8471da177e4SLinus Torvalds }
8481da177e4SLinus Torvalds 
8491da177e4SLinus Torvalds static struct ipmi_smi_watcher smi_watcher =
8501da177e4SLinus Torvalds {
8511da177e4SLinus Torvalds 	.owner    = THIS_MODULE,
8521da177e4SLinus Torvalds 	.new_smi  = ipmi_new_smi,
8531da177e4SLinus Torvalds 	.smi_gone = ipmi_smi_gone,
8541da177e4SLinus Torvalds };
8551da177e4SLinus Torvalds 
init_ipmi_devintf(void)85660ee6d5fSCorey Minyard static int __init init_ipmi_devintf(void)
8571da177e4SLinus Torvalds {
8581da177e4SLinus Torvalds 	int rv;
8591da177e4SLinus Torvalds 
8601da177e4SLinus Torvalds 	if (ipmi_major < 0)
8611da177e4SLinus Torvalds 		return -EINVAL;
8621da177e4SLinus Torvalds 
863f993cdd9SJoe Perches 	pr_info("ipmi device interface\n");
8641da177e4SLinus Torvalds 
865*392fa3a3SIvan Orlov 	rv = class_register(&ipmi_class);
866*392fa3a3SIvan Orlov 	if (rv)
867*392fa3a3SIvan Orlov 		return rv;
86837e0915bSCorey Minyard 
8691da177e4SLinus Torvalds 	rv = register_chrdev(ipmi_major, DEVICE_NAME, &ipmi_fops);
8701da177e4SLinus Torvalds 	if (rv < 0) {
871*392fa3a3SIvan Orlov 		class_unregister(&ipmi_class);
872f993cdd9SJoe Perches 		pr_err("ipmi: can't get major %d\n", ipmi_major);
8731da177e4SLinus Torvalds 		return rv;
8741da177e4SLinus Torvalds 	}
8751da177e4SLinus Torvalds 
8761da177e4SLinus Torvalds 	if (ipmi_major == 0) {
8771da177e4SLinus Torvalds 		ipmi_major = rv;
8781da177e4SLinus Torvalds 	}
8791da177e4SLinus Torvalds 
8801da177e4SLinus Torvalds 	rv = ipmi_smi_watcher_register(&smi_watcher);
8811da177e4SLinus Torvalds 	if (rv) {
8821da177e4SLinus Torvalds 		unregister_chrdev(ipmi_major, DEVICE_NAME);
883*392fa3a3SIvan Orlov 		class_unregister(&ipmi_class);
884f993cdd9SJoe Perches 		pr_warn("ipmi: can't register smi watcher\n");
8851da177e4SLinus Torvalds 		return rv;
8861da177e4SLinus Torvalds 	}
8871da177e4SLinus Torvalds 
8881da177e4SLinus Torvalds 	return 0;
8891da177e4SLinus Torvalds }
8901da177e4SLinus Torvalds module_init(init_ipmi_devintf);
8911da177e4SLinus Torvalds 
cleanup_ipmi(void)89260ee6d5fSCorey Minyard static void __exit cleanup_ipmi(void)
8931da177e4SLinus Torvalds {
89450c812b2SCorey Minyard 	struct ipmi_reg_list *entry, *entry2;
89550c812b2SCorey Minyard 	mutex_lock(&reg_list_mutex);
89650c812b2SCorey Minyard 	list_for_each_entry_safe(entry, entry2, &reg_list, link) {
89750c812b2SCorey Minyard 		list_del(&entry->link);
898*392fa3a3SIvan Orlov 		device_destroy(&ipmi_class, entry->dev);
89950c812b2SCorey Minyard 		kfree(entry);
90050c812b2SCorey Minyard 	}
90150c812b2SCorey Minyard 	mutex_unlock(&reg_list_mutex);
902*392fa3a3SIvan Orlov 	class_unregister(&ipmi_class);
9031da177e4SLinus Torvalds 	ipmi_smi_watcher_unregister(&smi_watcher);
9041da177e4SLinus Torvalds 	unregister_chrdev(ipmi_major, DEVICE_NAME);
9051da177e4SLinus Torvalds }
9061da177e4SLinus Torvalds module_exit(cleanup_ipmi);
9071da177e4SLinus Torvalds 
9081da177e4SLinus Torvalds MODULE_LICENSE("GPL");
9091fdd75bdSCorey Minyard MODULE_AUTHOR("Corey Minyard <minyard@mvista.com>");
9101fdd75bdSCorey Minyard MODULE_DESCRIPTION("Linux device interface for the IPMI message handler.");
911