xref: /linux-6.15/drivers/char/ipmi/ipmi_devintf.c (revision 9b6442a3)
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 <[email protected]>
91da177e4SLinus Torvalds  *         [email protected]
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;
125bdf2829cSNicholas Krause 	struct ipmi_recv_msg *msg, *next;
1261da177e4SLinus Torvalds 
127*9b6442a3SVitaliy Shevtsov 	ipmi_destroy_user(priv->user);
1281da177e4SLinus Torvalds 
129bdf2829cSNicholas Krause 	list_for_each_entry_safe(msg, next, &priv->recv_msgs, link)
130bdf2829cSNicholas Krause 		ipmi_free_recv_msg(msg);
131bdf2829cSNicholas Krause 
1321da177e4SLinus Torvalds 	kfree(priv);
1331da177e4SLinus Torvalds 
1341da177e4SLinus Torvalds 	return 0;
1351da177e4SLinus Torvalds }
1361da177e4SLinus Torvalds 
handle_send_req(struct ipmi_user * user,struct ipmi_req * req,int retries,unsigned int retry_time_ms)1372911c988SCorey Minyard static int handle_send_req(struct ipmi_user *user,
1381da177e4SLinus Torvalds 			   struct ipmi_req *req,
1391da177e4SLinus Torvalds 			   int             retries,
1401da177e4SLinus Torvalds 			   unsigned int    retry_time_ms)
1411da177e4SLinus Torvalds {
1421da177e4SLinus Torvalds 	int              rv;
1431da177e4SLinus Torvalds 	struct ipmi_addr addr;
1441da177e4SLinus Torvalds 	struct kernel_ipmi_msg msg;
1451da177e4SLinus Torvalds 
1461da177e4SLinus Torvalds 	if (req->addr_len > sizeof(struct ipmi_addr))
1471da177e4SLinus Torvalds 		return -EINVAL;
1481da177e4SLinus Torvalds 
1491da177e4SLinus Torvalds 	if (copy_from_user(&addr, req->addr, req->addr_len))
1501da177e4SLinus Torvalds 		return -EFAULT;
1511da177e4SLinus Torvalds 
1521da177e4SLinus Torvalds 	msg.netfn = req->msg.netfn;
1531da177e4SLinus Torvalds 	msg.cmd = req->msg.cmd;
1541da177e4SLinus Torvalds 	msg.data_len = req->msg.data_len;
1551da177e4SLinus Torvalds 	msg.data = kmalloc(IPMI_MAX_MSG_LENGTH, GFP_KERNEL);
1561da177e4SLinus Torvalds 	if (!msg.data)
1571da177e4SLinus Torvalds 		return -ENOMEM;
1581da177e4SLinus Torvalds 
1591da177e4SLinus Torvalds 	/* From here out we cannot return, we must jump to "out" for
1601da177e4SLinus Torvalds 	   error exits to free msgdata. */
1611da177e4SLinus Torvalds 
1621da177e4SLinus Torvalds 	rv = ipmi_validate_addr(&addr, req->addr_len);
1631da177e4SLinus Torvalds 	if (rv)
1641da177e4SLinus Torvalds 		goto out;
1651da177e4SLinus Torvalds 
1661da177e4SLinus Torvalds 	if (req->msg.data != NULL) {
1671da177e4SLinus Torvalds 		if (req->msg.data_len > IPMI_MAX_MSG_LENGTH) {
1681da177e4SLinus Torvalds 			rv = -EMSGSIZE;
1691da177e4SLinus Torvalds 			goto out;
1701da177e4SLinus Torvalds 		}
1711da177e4SLinus Torvalds 
1721da177e4SLinus Torvalds 		if (copy_from_user(msg.data,
1731da177e4SLinus Torvalds 				   req->msg.data,
174c81c5fc2SCorey Minyard 				   req->msg.data_len)) {
1751da177e4SLinus Torvalds 			rv = -EFAULT;
1761da177e4SLinus Torvalds 			goto out;
1771da177e4SLinus Torvalds 		}
1781da177e4SLinus Torvalds 	} else {
1791da177e4SLinus Torvalds 		msg.data_len = 0;
1801da177e4SLinus Torvalds 	}
1811da177e4SLinus Torvalds 
1821da177e4SLinus Torvalds 	rv = ipmi_request_settime(user,
1831da177e4SLinus Torvalds 				  &addr,
1841da177e4SLinus Torvalds 				  req->msgid,
1851da177e4SLinus Torvalds 				  &msg,
1861da177e4SLinus Torvalds 				  NULL,
1871da177e4SLinus Torvalds 				  0,
1881da177e4SLinus Torvalds 				  retries,
1891da177e4SLinus Torvalds 				  retry_time_ms);
1901da177e4SLinus Torvalds  out:
1911da177e4SLinus Torvalds 	kfree(msg.data);
1921da177e4SLinus Torvalds 	return rv;
1931da177e4SLinus Torvalds }
1941da177e4SLinus Torvalds 
handle_recv(struct ipmi_file_private * priv,bool trunc,struct ipmi_recv * rsp,int (* copyout)(struct ipmi_recv *,void __user *),void __user * to)1958b9e04f2SAl Viro static int handle_recv(struct ipmi_file_private *priv,
1968b9e04f2SAl Viro 			bool trunc, struct ipmi_recv *rsp,
1978b9e04f2SAl Viro 			int (*copyout)(struct ipmi_recv *, void __user *),
1988b9e04f2SAl Viro 			void __user *to)
1998b9e04f2SAl Viro {
2008b9e04f2SAl Viro 	int              addr_len;
2018b9e04f2SAl Viro 	struct list_head *entry;
2028b9e04f2SAl Viro 	struct ipmi_recv_msg  *msg;
2038b9e04f2SAl Viro 	unsigned long    flags;
20495ac0daaSCorey Minyard 	int rv = 0, rv2 = 0;
2058b9e04f2SAl Viro 
2068b9e04f2SAl Viro 	/* We claim a mutex because we don't want two
2078b9e04f2SAl Viro 	   users getting something from the queue at a time.
2088b9e04f2SAl Viro 	   Since we have to release the spinlock before we can
2098b9e04f2SAl Viro 	   copy the data to the user, it's possible another
2108b9e04f2SAl Viro 	   user will grab something from the queue, too.  Then
2118b9e04f2SAl Viro 	   the messages might get out of order if something
2128b9e04f2SAl Viro 	   fails and the message gets put back onto the
2138b9e04f2SAl Viro 	   queue.  This mutex prevents that problem. */
2148b9e04f2SAl Viro 	mutex_lock(&priv->recv_mutex);
2158b9e04f2SAl Viro 
2168b9e04f2SAl Viro 	/* Grab the message off the list. */
217c81c5fc2SCorey Minyard 	spin_lock_irqsave(&priv->recv_msg_lock, flags);
2188b9e04f2SAl Viro 	if (list_empty(&(priv->recv_msgs))) {
219c81c5fc2SCorey Minyard 		spin_unlock_irqrestore(&priv->recv_msg_lock, flags);
2208b9e04f2SAl Viro 		rv = -EAGAIN;
2218b9e04f2SAl Viro 		goto recv_err;
2228b9e04f2SAl Viro 	}
2238b9e04f2SAl Viro 	entry = priv->recv_msgs.next;
2248b9e04f2SAl Viro 	msg = list_entry(entry, struct ipmi_recv_msg, link);
2258b9e04f2SAl Viro 	list_del(entry);
226c81c5fc2SCorey Minyard 	spin_unlock_irqrestore(&priv->recv_msg_lock, flags);
2278b9e04f2SAl Viro 
2288b9e04f2SAl Viro 	addr_len = ipmi_addr_length(msg->addr.addr_type);
229c81c5fc2SCorey Minyard 	if (rsp->addr_len < addr_len) {
2308b9e04f2SAl Viro 		rv = -EINVAL;
2318b9e04f2SAl Viro 		goto recv_putback_on_err;
2328b9e04f2SAl Viro 	}
2338b9e04f2SAl Viro 
234c81c5fc2SCorey Minyard 	if (copy_to_user(rsp->addr, &msg->addr, addr_len)) {
2358b9e04f2SAl Viro 		rv = -EFAULT;
2368b9e04f2SAl Viro 		goto recv_putback_on_err;
2378b9e04f2SAl Viro 	}
2388b9e04f2SAl Viro 	rsp->addr_len = addr_len;
2398b9e04f2SAl Viro 
2408b9e04f2SAl Viro 	rsp->recv_type = msg->recv_type;
2418b9e04f2SAl Viro 	rsp->msgid = msg->msgid;
2428b9e04f2SAl Viro 	rsp->msg.netfn = msg->msg.netfn;
2438b9e04f2SAl Viro 	rsp->msg.cmd = msg->msg.cmd;
2448b9e04f2SAl Viro 
2458b9e04f2SAl Viro 	if (msg->msg.data_len > 0) {
2468b9e04f2SAl Viro 		if (rsp->msg.data_len < msg->msg.data_len) {
24717a42627SCorey Minyard 			if (trunc) {
24895ac0daaSCorey Minyard 				rv2 = -EMSGSIZE;
2498b9e04f2SAl Viro 				msg->msg.data_len = rsp->msg.data_len;
25017a42627SCorey Minyard 			} else {
25117a42627SCorey Minyard 				rv = -EMSGSIZE;
2528b9e04f2SAl Viro 				goto recv_putback_on_err;
2538b9e04f2SAl Viro 			}
25417a42627SCorey Minyard 		}
2558b9e04f2SAl Viro 
2568b9e04f2SAl Viro 		if (copy_to_user(rsp->msg.data,
2578b9e04f2SAl Viro 				 msg->msg.data,
258c81c5fc2SCorey Minyard 				 msg->msg.data_len)) {
2598b9e04f2SAl Viro 			rv = -EFAULT;
2608b9e04f2SAl Viro 			goto recv_putback_on_err;
2618b9e04f2SAl Viro 		}
2628b9e04f2SAl Viro 		rsp->msg.data_len = msg->msg.data_len;
2638b9e04f2SAl Viro 	} else {
2648b9e04f2SAl Viro 		rsp->msg.data_len = 0;
2658b9e04f2SAl Viro 	}
2668b9e04f2SAl Viro 
2678b9e04f2SAl Viro 	rv = copyout(rsp, to);
2688b9e04f2SAl Viro 	if (rv)
2698b9e04f2SAl Viro 		goto recv_putback_on_err;
2708b9e04f2SAl Viro 
2718b9e04f2SAl Viro 	mutex_unlock(&priv->recv_mutex);
2728b9e04f2SAl Viro 	ipmi_free_recv_msg(msg);
27395ac0daaSCorey Minyard 	return rv2;
2748b9e04f2SAl Viro 
2758b9e04f2SAl Viro recv_putback_on_err:
2768b9e04f2SAl Viro 	/* If we got an error, put the message back onto
2778b9e04f2SAl Viro 	   the head of the queue. */
278c81c5fc2SCorey Minyard 	spin_lock_irqsave(&priv->recv_msg_lock, flags);
279c81c5fc2SCorey Minyard 	list_add(entry, &priv->recv_msgs);
280c81c5fc2SCorey Minyard 	spin_unlock_irqrestore(&priv->recv_msg_lock, flags);
2818b9e04f2SAl Viro recv_err:
2828b9e04f2SAl Viro 	mutex_unlock(&priv->recv_mutex);
2838b9e04f2SAl Viro 	return rv;
2848b9e04f2SAl Viro }
2858b9e04f2SAl Viro 
copyout_recv(struct ipmi_recv * rsp,void __user * to)2868b9e04f2SAl Viro static int copyout_recv(struct ipmi_recv *rsp, void __user *to)
2878b9e04f2SAl Viro {
2888b9e04f2SAl Viro 	return copy_to_user(to, rsp, sizeof(struct ipmi_recv)) ? -EFAULT : 0;
2898b9e04f2SAl Viro }
2908b9e04f2SAl Viro 
ipmi_ioctl(struct file * file,unsigned int cmd,unsigned long data)2916aa2dd00SCorey Minyard static long ipmi_ioctl(struct file   *file,
2921da177e4SLinus Torvalds 		       unsigned int  cmd,
2931da177e4SLinus Torvalds 		       unsigned long data)
2941da177e4SLinus Torvalds {
2951da177e4SLinus Torvalds 	int                      rv = -EINVAL;
2961da177e4SLinus Torvalds 	struct ipmi_file_private *priv = file->private_data;
2971da177e4SLinus Torvalds 	void __user *arg = (void __user *)data;
2981da177e4SLinus Torvalds 
2991da177e4SLinus Torvalds 	switch (cmd)
3001da177e4SLinus Torvalds 	{
3011da177e4SLinus Torvalds 	case IPMICTL_SEND_COMMAND:
3021da177e4SLinus Torvalds 	{
3031da177e4SLinus Torvalds 		struct ipmi_req req;
3046aa2dd00SCorey Minyard 		int retries;
3056aa2dd00SCorey Minyard 		unsigned int retry_time_ms;
3061da177e4SLinus Torvalds 
3071da177e4SLinus Torvalds 		if (copy_from_user(&req, arg, sizeof(req))) {
3081da177e4SLinus Torvalds 			rv = -EFAULT;
3091da177e4SLinus Torvalds 			break;
3101da177e4SLinus Torvalds 		}
3111da177e4SLinus Torvalds 
3126aa2dd00SCorey Minyard 		mutex_lock(&priv->recv_mutex);
3136aa2dd00SCorey Minyard 		retries = priv->default_retries;
3146aa2dd00SCorey Minyard 		retry_time_ms = priv->default_retry_time_ms;
3156aa2dd00SCorey Minyard 		mutex_unlock(&priv->recv_mutex);
3166aa2dd00SCorey Minyard 
3176aa2dd00SCorey Minyard 		rv = handle_send_req(priv->user, &req, retries, retry_time_ms);
3181da177e4SLinus Torvalds 		break;
3191da177e4SLinus Torvalds 	}
3201da177e4SLinus Torvalds 
3211da177e4SLinus Torvalds 	case IPMICTL_SEND_COMMAND_SETTIME:
3221da177e4SLinus Torvalds 	{
3231da177e4SLinus Torvalds 		struct ipmi_req_settime req;
3241da177e4SLinus Torvalds 
3251da177e4SLinus Torvalds 		if (copy_from_user(&req, arg, sizeof(req))) {
3261da177e4SLinus Torvalds 			rv = -EFAULT;
3271da177e4SLinus Torvalds 			break;
3281da177e4SLinus Torvalds 		}
3291da177e4SLinus Torvalds 
3301da177e4SLinus Torvalds 		rv = handle_send_req(priv->user,
3311da177e4SLinus Torvalds 				     &req.req,
3321da177e4SLinus Torvalds 				     req.retries,
3331da177e4SLinus Torvalds 				     req.retry_time_ms);
3341da177e4SLinus Torvalds 		break;
3351da177e4SLinus Torvalds 	}
3361da177e4SLinus Torvalds 
3371da177e4SLinus Torvalds 	case IPMICTL_RECEIVE_MSG:
3381da177e4SLinus Torvalds 	case IPMICTL_RECEIVE_MSG_TRUNC:
3391da177e4SLinus Torvalds 	{
3401da177e4SLinus Torvalds 		struct ipmi_recv      rsp;
3411da177e4SLinus Torvalds 
3428b9e04f2SAl Viro 		if (copy_from_user(&rsp, arg, sizeof(rsp)))
3431da177e4SLinus Torvalds 			rv = -EFAULT;
3448b9e04f2SAl Viro 		else
3458b9e04f2SAl Viro 			rv = handle_recv(priv, cmd == IPMICTL_RECEIVE_MSG_TRUNC,
3468b9e04f2SAl Viro 					 &rsp, copyout_recv, arg);
3471da177e4SLinus Torvalds 		break;
3481da177e4SLinus Torvalds 	}
3491da177e4SLinus Torvalds 
3501da177e4SLinus Torvalds 	case IPMICTL_REGISTER_FOR_CMD:
3511da177e4SLinus Torvalds 	{
3521da177e4SLinus Torvalds 		struct ipmi_cmdspec val;
3531da177e4SLinus Torvalds 
3541da177e4SLinus Torvalds 		if (copy_from_user(&val, arg, sizeof(val))) {
3551da177e4SLinus Torvalds 			rv = -EFAULT;
3561da177e4SLinus Torvalds 			break;
3571da177e4SLinus Torvalds 		}
3581da177e4SLinus Torvalds 
359c69c3127SCorey Minyard 		rv = ipmi_register_for_cmd(priv->user, val.netfn, val.cmd,
360c69c3127SCorey Minyard 					   IPMI_CHAN_ALL);
3611da177e4SLinus Torvalds 		break;
3621da177e4SLinus Torvalds 	}
3631da177e4SLinus Torvalds 
3641da177e4SLinus Torvalds 	case IPMICTL_UNREGISTER_FOR_CMD:
3651da177e4SLinus Torvalds 	{
3661da177e4SLinus Torvalds 		struct ipmi_cmdspec   val;
3671da177e4SLinus Torvalds 
3681da177e4SLinus Torvalds 		if (copy_from_user(&val, arg, sizeof(val))) {
3691da177e4SLinus Torvalds 			rv = -EFAULT;
3701da177e4SLinus Torvalds 			break;
3711da177e4SLinus Torvalds 		}
3721da177e4SLinus Torvalds 
373c69c3127SCorey Minyard 		rv = ipmi_unregister_for_cmd(priv->user, val.netfn, val.cmd,
374c69c3127SCorey Minyard 					     IPMI_CHAN_ALL);
375c69c3127SCorey Minyard 		break;
376c69c3127SCorey Minyard 	}
377c69c3127SCorey Minyard 
378c69c3127SCorey Minyard 	case IPMICTL_REGISTER_FOR_CMD_CHANS:
379c69c3127SCorey Minyard 	{
380c69c3127SCorey Minyard 		struct ipmi_cmdspec_chans val;
381c69c3127SCorey Minyard 
382c69c3127SCorey Minyard 		if (copy_from_user(&val, arg, sizeof(val))) {
383c69c3127SCorey Minyard 			rv = -EFAULT;
384c69c3127SCorey Minyard 			break;
385c69c3127SCorey Minyard 		}
386c69c3127SCorey Minyard 
387c69c3127SCorey Minyard 		rv = ipmi_register_for_cmd(priv->user, val.netfn, val.cmd,
388c69c3127SCorey Minyard 					   val.chans);
389c69c3127SCorey Minyard 		break;
390c69c3127SCorey Minyard 	}
391c69c3127SCorey Minyard 
392c69c3127SCorey Minyard 	case IPMICTL_UNREGISTER_FOR_CMD_CHANS:
393c69c3127SCorey Minyard 	{
394c69c3127SCorey Minyard 		struct ipmi_cmdspec_chans val;
395c69c3127SCorey Minyard 
396c69c3127SCorey Minyard 		if (copy_from_user(&val, arg, sizeof(val))) {
397c69c3127SCorey Minyard 			rv = -EFAULT;
398c69c3127SCorey Minyard 			break;
399c69c3127SCorey Minyard 		}
400c69c3127SCorey Minyard 
401c69c3127SCorey Minyard 		rv = ipmi_unregister_for_cmd(priv->user, val.netfn, val.cmd,
402c69c3127SCorey Minyard 					     val.chans);
4031da177e4SLinus Torvalds 		break;
4041da177e4SLinus Torvalds 	}
4051da177e4SLinus Torvalds 
4061da177e4SLinus Torvalds 	case IPMICTL_SET_GETS_EVENTS_CMD:
4071da177e4SLinus Torvalds 	{
4081da177e4SLinus Torvalds 		int val;
4091da177e4SLinus Torvalds 
4101da177e4SLinus Torvalds 		if (copy_from_user(&val, arg, sizeof(val))) {
4111da177e4SLinus Torvalds 			rv = -EFAULT;
4121da177e4SLinus Torvalds 			break;
4131da177e4SLinus Torvalds 		}
4141da177e4SLinus Torvalds 
4151da177e4SLinus Torvalds 		rv = ipmi_set_gets_events(priv->user, val);
4161da177e4SLinus Torvalds 		break;
4171da177e4SLinus Torvalds 	}
4181da177e4SLinus Torvalds 
419c14979b9SCorey Minyard 	/* The next four are legacy, not per-channel. */
4201da177e4SLinus Torvalds 	case IPMICTL_SET_MY_ADDRESS_CMD:
4211da177e4SLinus Torvalds 	{
4221da177e4SLinus Torvalds 		unsigned int val;
4231da177e4SLinus Torvalds 
4241da177e4SLinus Torvalds 		if (copy_from_user(&val, arg, sizeof(val))) {
4251da177e4SLinus Torvalds 			rv = -EFAULT;
4261da177e4SLinus Torvalds 			break;
4271da177e4SLinus Torvalds 		}
4281da177e4SLinus Torvalds 
429c14979b9SCorey Minyard 		rv = ipmi_set_my_address(priv->user, 0, val);
4301da177e4SLinus Torvalds 		break;
4311da177e4SLinus Torvalds 	}
4321da177e4SLinus Torvalds 
4331da177e4SLinus Torvalds 	case IPMICTL_GET_MY_ADDRESS_CMD:
4341da177e4SLinus Torvalds 	{
4351da177e4SLinus Torvalds 		unsigned int  val;
436c14979b9SCorey Minyard 		unsigned char rval;
4371da177e4SLinus Torvalds 
438c14979b9SCorey Minyard 		rv = ipmi_get_my_address(priv->user, 0, &rval);
439c14979b9SCorey Minyard 		if (rv)
440c14979b9SCorey Minyard 			break;
441c14979b9SCorey Minyard 
442c14979b9SCorey Minyard 		val = rval;
4431da177e4SLinus Torvalds 
4441da177e4SLinus Torvalds 		if (copy_to_user(arg, &val, sizeof(val))) {
4451da177e4SLinus Torvalds 			rv = -EFAULT;
4461da177e4SLinus Torvalds 			break;
4471da177e4SLinus Torvalds 		}
4481da177e4SLinus Torvalds 		break;
4491da177e4SLinus Torvalds 	}
4501da177e4SLinus Torvalds 
4511da177e4SLinus Torvalds 	case IPMICTL_SET_MY_LUN_CMD:
4521da177e4SLinus Torvalds 	{
4531da177e4SLinus Torvalds 		unsigned int val;
4541da177e4SLinus Torvalds 
4551da177e4SLinus Torvalds 		if (copy_from_user(&val, arg, sizeof(val))) {
4561da177e4SLinus Torvalds 			rv = -EFAULT;
4571da177e4SLinus Torvalds 			break;
4581da177e4SLinus Torvalds 		}
4591da177e4SLinus Torvalds 
460c14979b9SCorey Minyard 		rv = ipmi_set_my_LUN(priv->user, 0, val);
4611da177e4SLinus Torvalds 		break;
4621da177e4SLinus Torvalds 	}
4631da177e4SLinus Torvalds 
4641da177e4SLinus Torvalds 	case IPMICTL_GET_MY_LUN_CMD:
4651da177e4SLinus Torvalds 	{
4661da177e4SLinus Torvalds 		unsigned int  val;
467c14979b9SCorey Minyard 		unsigned char rval;
4681da177e4SLinus Torvalds 
469c14979b9SCorey Minyard 		rv = ipmi_get_my_LUN(priv->user, 0, &rval);
470c14979b9SCorey Minyard 		if (rv)
471c14979b9SCorey Minyard 			break;
472c14979b9SCorey Minyard 
473c14979b9SCorey Minyard 		val = rval;
4741da177e4SLinus Torvalds 
4751da177e4SLinus Torvalds 		if (copy_to_user(arg, &val, sizeof(val))) {
4761da177e4SLinus Torvalds 			rv = -EFAULT;
4771da177e4SLinus Torvalds 			break;
4781da177e4SLinus Torvalds 		}
4791da177e4SLinus Torvalds 		break;
4801da177e4SLinus Torvalds 	}
481c14979b9SCorey Minyard 
482c14979b9SCorey Minyard 	case IPMICTL_SET_MY_CHANNEL_ADDRESS_CMD:
483c14979b9SCorey Minyard 	{
484c14979b9SCorey Minyard 		struct ipmi_channel_lun_address_set val;
485c14979b9SCorey Minyard 
486c14979b9SCorey Minyard 		if (copy_from_user(&val, arg, sizeof(val))) {
487c14979b9SCorey Minyard 			rv = -EFAULT;
488c14979b9SCorey Minyard 			break;
489c14979b9SCorey Minyard 		}
490c14979b9SCorey Minyard 
491c14979b9SCorey Minyard 		return ipmi_set_my_address(priv->user, val.channel, val.value);
492c14979b9SCorey Minyard 	}
493c14979b9SCorey Minyard 
494c14979b9SCorey Minyard 	case IPMICTL_GET_MY_CHANNEL_ADDRESS_CMD:
495c14979b9SCorey Minyard 	{
496c14979b9SCorey Minyard 		struct ipmi_channel_lun_address_set val;
497c14979b9SCorey Minyard 
498c14979b9SCorey Minyard 		if (copy_from_user(&val, arg, sizeof(val))) {
499c14979b9SCorey Minyard 			rv = -EFAULT;
500c14979b9SCorey Minyard 			break;
501c14979b9SCorey Minyard 		}
502c14979b9SCorey Minyard 
503c14979b9SCorey Minyard 		rv = ipmi_get_my_address(priv->user, val.channel, &val.value);
504c14979b9SCorey Minyard 		if (rv)
505c14979b9SCorey Minyard 			break;
506c14979b9SCorey Minyard 
507c14979b9SCorey Minyard 		if (copy_to_user(arg, &val, sizeof(val))) {
508c14979b9SCorey Minyard 			rv = -EFAULT;
509c14979b9SCorey Minyard 			break;
510c14979b9SCorey Minyard 		}
511c14979b9SCorey Minyard 		break;
512c14979b9SCorey Minyard 	}
513c14979b9SCorey Minyard 
514c14979b9SCorey Minyard 	case IPMICTL_SET_MY_CHANNEL_LUN_CMD:
515c14979b9SCorey Minyard 	{
516c14979b9SCorey Minyard 		struct ipmi_channel_lun_address_set val;
517c14979b9SCorey Minyard 
518c14979b9SCorey Minyard 		if (copy_from_user(&val, arg, sizeof(val))) {
519c14979b9SCorey Minyard 			rv = -EFAULT;
520c14979b9SCorey Minyard 			break;
521c14979b9SCorey Minyard 		}
522c14979b9SCorey Minyard 
523c14979b9SCorey Minyard 		rv = ipmi_set_my_LUN(priv->user, val.channel, val.value);
524c14979b9SCorey Minyard 		break;
525c14979b9SCorey Minyard 	}
526c14979b9SCorey Minyard 
527c14979b9SCorey Minyard 	case IPMICTL_GET_MY_CHANNEL_LUN_CMD:
528c14979b9SCorey Minyard 	{
529c14979b9SCorey Minyard 		struct ipmi_channel_lun_address_set val;
530c14979b9SCorey Minyard 
531c14979b9SCorey Minyard 		if (copy_from_user(&val, arg, sizeof(val))) {
532c14979b9SCorey Minyard 			rv = -EFAULT;
533c14979b9SCorey Minyard 			break;
534c14979b9SCorey Minyard 		}
535c14979b9SCorey Minyard 
536c14979b9SCorey Minyard 		rv = ipmi_get_my_LUN(priv->user, val.channel, &val.value);
537c14979b9SCorey Minyard 		if (rv)
538c14979b9SCorey Minyard 			break;
539c14979b9SCorey Minyard 
540c14979b9SCorey Minyard 		if (copy_to_user(arg, &val, sizeof(val))) {
541c14979b9SCorey Minyard 			rv = -EFAULT;
542c14979b9SCorey Minyard 			break;
543c14979b9SCorey Minyard 		}
544c14979b9SCorey Minyard 		break;
545c14979b9SCorey Minyard 	}
546c14979b9SCorey Minyard 
5471da177e4SLinus Torvalds 	case IPMICTL_SET_TIMING_PARMS_CMD:
5481da177e4SLinus Torvalds 	{
5491da177e4SLinus Torvalds 		struct ipmi_timing_parms parms;
5501da177e4SLinus Torvalds 
5511da177e4SLinus Torvalds 		if (copy_from_user(&parms, arg, sizeof(parms))) {
5521da177e4SLinus Torvalds 			rv = -EFAULT;
5531da177e4SLinus Torvalds 			break;
5541da177e4SLinus Torvalds 		}
5551da177e4SLinus Torvalds 
5566aa2dd00SCorey Minyard 		mutex_lock(&priv->recv_mutex);
5571da177e4SLinus Torvalds 		priv->default_retries = parms.retries;
5581da177e4SLinus Torvalds 		priv->default_retry_time_ms = parms.retry_time_ms;
5596aa2dd00SCorey Minyard 		mutex_unlock(&priv->recv_mutex);
5601da177e4SLinus Torvalds 		rv = 0;
5611da177e4SLinus Torvalds 		break;
5621da177e4SLinus Torvalds 	}
5631da177e4SLinus Torvalds 
5641da177e4SLinus Torvalds 	case IPMICTL_GET_TIMING_PARMS_CMD:
5651da177e4SLinus Torvalds 	{
5661da177e4SLinus Torvalds 		struct ipmi_timing_parms parms;
5671da177e4SLinus Torvalds 
5686aa2dd00SCorey Minyard 		mutex_lock(&priv->recv_mutex);
5691da177e4SLinus Torvalds 		parms.retries = priv->default_retries;
5701da177e4SLinus Torvalds 		parms.retry_time_ms = priv->default_retry_time_ms;
5716aa2dd00SCorey Minyard 		mutex_unlock(&priv->recv_mutex);
5721da177e4SLinus Torvalds 
5731da177e4SLinus Torvalds 		if (copy_to_user(arg, &parms, sizeof(parms))) {
5741da177e4SLinus Torvalds 			rv = -EFAULT;
5751da177e4SLinus Torvalds 			break;
5761da177e4SLinus Torvalds 		}
5771da177e4SLinus Torvalds 
5781da177e4SLinus Torvalds 		rv = 0;
5791da177e4SLinus Torvalds 		break;
5801da177e4SLinus Torvalds 	}
581b9675136SCorey Minyard 
582b9675136SCorey Minyard 	case IPMICTL_GET_MAINTENANCE_MODE_CMD:
583b9675136SCorey Minyard 	{
584b9675136SCorey Minyard 		int mode;
585b9675136SCorey Minyard 
586b9675136SCorey Minyard 		mode = ipmi_get_maintenance_mode(priv->user);
587b9675136SCorey Minyard 		if (copy_to_user(arg, &mode, sizeof(mode))) {
588b9675136SCorey Minyard 			rv = -EFAULT;
589b9675136SCorey Minyard 			break;
590b9675136SCorey Minyard 		}
591b9675136SCorey Minyard 		rv = 0;
592b9675136SCorey Minyard 		break;
593b9675136SCorey Minyard 	}
594b9675136SCorey Minyard 
595b9675136SCorey Minyard 	case IPMICTL_SET_MAINTENANCE_MODE_CMD:
596b9675136SCorey Minyard 	{
597b9675136SCorey Minyard 		int mode;
598b9675136SCorey Minyard 
599b9675136SCorey Minyard 		if (copy_from_user(&mode, arg, sizeof(mode))) {
600b9675136SCorey Minyard 			rv = -EFAULT;
601b9675136SCorey Minyard 			break;
602b9675136SCorey Minyard 		}
603b9675136SCorey Minyard 		rv = ipmi_set_maintenance_mode(priv->user, mode);
604b9675136SCorey Minyard 		break;
605b9675136SCorey Minyard 	}
606e56710d2SCorey Minyard 
607e56710d2SCorey Minyard 	default:
608e56710d2SCorey Minyard 		rv = -ENOTTY;
609e56710d2SCorey Minyard 		break;
6101da177e4SLinus Torvalds 	}
6111da177e4SLinus Torvalds 
6121da177e4SLinus Torvalds 	return rv;
6131da177e4SLinus Torvalds }
6141da177e4SLinus Torvalds 
6156a94f920SAndrew Morton #ifdef CONFIG_COMPAT
6166a94f920SAndrew Morton /*
6176a94f920SAndrew Morton  * The following code contains code for supporting 32-bit compatible
6186a94f920SAndrew Morton  * ioctls on 64-bit kernels.  This allows running 32-bit apps on the
6196a94f920SAndrew Morton  * 64-bit kernel
6206a94f920SAndrew Morton  */
6216a94f920SAndrew Morton #define COMPAT_IPMICTL_SEND_COMMAND	\
6226a94f920SAndrew Morton 	_IOR(IPMI_IOC_MAGIC, 13, struct compat_ipmi_req)
6236a94f920SAndrew Morton #define COMPAT_IPMICTL_SEND_COMMAND_SETTIME	\
6246a94f920SAndrew Morton 	_IOR(IPMI_IOC_MAGIC, 21, struct compat_ipmi_req_settime)
6256a94f920SAndrew Morton #define COMPAT_IPMICTL_RECEIVE_MSG	\
6266a94f920SAndrew Morton 	_IOWR(IPMI_IOC_MAGIC, 12, struct compat_ipmi_recv)
6276a94f920SAndrew Morton #define COMPAT_IPMICTL_RECEIVE_MSG_TRUNC	\
6286a94f920SAndrew Morton 	_IOWR(IPMI_IOC_MAGIC, 11, struct compat_ipmi_recv)
6296a94f920SAndrew Morton 
6306a94f920SAndrew Morton struct compat_ipmi_msg {
6316a94f920SAndrew Morton 	u8		netfn;
6326a94f920SAndrew Morton 	u8		cmd;
6336a94f920SAndrew Morton 	u16		data_len;
6346a94f920SAndrew Morton 	compat_uptr_t	data;
6356a94f920SAndrew Morton };
6366a94f920SAndrew Morton 
6376a94f920SAndrew Morton struct compat_ipmi_req {
6386a94f920SAndrew Morton 	compat_uptr_t		addr;
6396a94f920SAndrew Morton 	compat_uint_t		addr_len;
6406a94f920SAndrew Morton 	compat_long_t		msgid;
6416a94f920SAndrew Morton 	struct compat_ipmi_msg	msg;
6426a94f920SAndrew Morton };
6436a94f920SAndrew Morton 
6446a94f920SAndrew Morton struct compat_ipmi_recv {
6456a94f920SAndrew Morton 	compat_int_t		recv_type;
6466a94f920SAndrew Morton 	compat_uptr_t		addr;
6476a94f920SAndrew Morton 	compat_uint_t		addr_len;
6486a94f920SAndrew Morton 	compat_long_t		msgid;
6496a94f920SAndrew Morton 	struct compat_ipmi_msg	msg;
6506a94f920SAndrew Morton };
6516a94f920SAndrew Morton 
6526a94f920SAndrew Morton struct compat_ipmi_req_settime {
6536a94f920SAndrew Morton 	struct compat_ipmi_req	req;
6546a94f920SAndrew Morton 	compat_int_t		retries;
6556a94f920SAndrew Morton 	compat_uint_t		retry_time_ms;
6566a94f920SAndrew Morton };
6576a94f920SAndrew Morton 
6586a94f920SAndrew Morton /*
6596a94f920SAndrew Morton  * Define some helper functions for copying IPMI data
6606a94f920SAndrew Morton  */
get_compat_ipmi_msg(struct ipmi_msg * p64,struct compat_ipmi_msg * p32)661e5f699d4SAl Viro static void get_compat_ipmi_msg(struct ipmi_msg *p64,
662e5f699d4SAl Viro 				struct compat_ipmi_msg *p32)
6636a94f920SAndrew Morton {
664e5f699d4SAl Viro 	p64->netfn = p32->netfn;
665e5f699d4SAl Viro 	p64->cmd = p32->cmd;
666e5f699d4SAl Viro 	p64->data_len = p32->data_len;
667e5f699d4SAl Viro 	p64->data = compat_ptr(p32->data);
6686a94f920SAndrew Morton }
6696a94f920SAndrew Morton 
get_compat_ipmi_req(struct ipmi_req * p64,struct compat_ipmi_req * p32)670e5f699d4SAl Viro static void get_compat_ipmi_req(struct ipmi_req *p64,
671e5f699d4SAl Viro 				struct compat_ipmi_req *p32)
6726a94f920SAndrew Morton {
673e5f699d4SAl Viro 	p64->addr = compat_ptr(p32->addr);
674e5f699d4SAl Viro 	p64->addr_len = p32->addr_len;
675e5f699d4SAl Viro 	p64->msgid = p32->msgid;
676e5f699d4SAl Viro 	get_compat_ipmi_msg(&p64->msg, &p32->msg);
6776a94f920SAndrew Morton }
6786a94f920SAndrew Morton 
get_compat_ipmi_req_settime(struct ipmi_req_settime * p64,struct compat_ipmi_req_settime * p32)679e5f699d4SAl Viro static void get_compat_ipmi_req_settime(struct ipmi_req_settime *p64,
680e5f699d4SAl Viro 		struct compat_ipmi_req_settime *p32)
6816a94f920SAndrew Morton {
682e5f699d4SAl Viro 	get_compat_ipmi_req(&p64->req, &p32->req);
683e5f699d4SAl Viro 	p64->retries = p32->retries;
684e5f699d4SAl Viro 	p64->retry_time_ms = p32->retry_time_ms;
6856a94f920SAndrew Morton }
6866a94f920SAndrew Morton 
get_compat_ipmi_recv(struct ipmi_recv * p64,struct compat_ipmi_recv * p32)687e5f699d4SAl Viro static void get_compat_ipmi_recv(struct ipmi_recv *p64,
688e5f699d4SAl Viro 				 struct compat_ipmi_recv *p32)
6896a94f920SAndrew Morton {
690e5f699d4SAl Viro 	memset(p64, 0, sizeof(struct ipmi_recv));
691e5f699d4SAl Viro 	p64->recv_type = p32->recv_type;
692e5f699d4SAl Viro 	p64->addr = compat_ptr(p32->addr);
693e5f699d4SAl Viro 	p64->addr_len = p32->addr_len;
694e5f699d4SAl Viro 	p64->msgid = p32->msgid;
695e5f699d4SAl Viro 	get_compat_ipmi_msg(&p64->msg, &p32->msg);
6966a94f920SAndrew Morton }
6976a94f920SAndrew Morton 
copyout_recv32(struct ipmi_recv * p64,void __user * to)6988b9e04f2SAl Viro static int copyout_recv32(struct ipmi_recv *p64, void __user *to)
6996a94f920SAndrew Morton {
7008b9e04f2SAl Viro 	struct compat_ipmi_recv v32;
7018b9e04f2SAl Viro 	memset(&v32, 0, sizeof(struct compat_ipmi_recv));
7028b9e04f2SAl Viro 	v32.recv_type = p64->recv_type;
7038b9e04f2SAl Viro 	v32.addr = ptr_to_compat(p64->addr);
7048b9e04f2SAl Viro 	v32.addr_len = p64->addr_len;
7058b9e04f2SAl Viro 	v32.msgid = p64->msgid;
7068b9e04f2SAl Viro 	v32.msg.netfn = p64->msg.netfn;
7078b9e04f2SAl Viro 	v32.msg.cmd = p64->msg.cmd;
7088b9e04f2SAl Viro 	v32.msg.data_len = p64->msg.data_len;
7098b9e04f2SAl Viro 	v32.msg.data = ptr_to_compat(p64->msg.data);
7108b9e04f2SAl Viro 	return copy_to_user(to, &v32, sizeof(v32)) ? -EFAULT : 0;
7116a94f920SAndrew Morton }
7126a94f920SAndrew Morton 
7136a94f920SAndrew Morton /*
7146a94f920SAndrew Morton  * Handle compatibility ioctls
7156a94f920SAndrew Morton  */
compat_ipmi_ioctl(struct file * filep,unsigned int cmd,unsigned long arg)7166a94f920SAndrew Morton static long compat_ipmi_ioctl(struct file *filep, unsigned int cmd,
7176a94f920SAndrew Morton 			      unsigned long arg)
7186a94f920SAndrew Morton {
7196a94f920SAndrew Morton 	struct ipmi_file_private *priv = filep->private_data;
7206a94f920SAndrew Morton 
7216a94f920SAndrew Morton 	switch(cmd) {
7226a94f920SAndrew Morton 	case COMPAT_IPMICTL_SEND_COMMAND:
7236a94f920SAndrew Morton 	{
7246a94f920SAndrew Morton 		struct ipmi_req	rp;
725e5f699d4SAl Viro 		struct compat_ipmi_req r32;
7266aa2dd00SCorey Minyard 		int retries;
7276aa2dd00SCorey Minyard 		unsigned int retry_time_ms;
7286a94f920SAndrew Morton 
729e5f699d4SAl Viro 		if (copy_from_user(&r32, compat_ptr(arg), sizeof(r32)))
7306a94f920SAndrew Morton 			return -EFAULT;
7316a94f920SAndrew Morton 
732e5f699d4SAl Viro 		get_compat_ipmi_req(&rp, &r32);
733e5f699d4SAl Viro 
7346aa2dd00SCorey Minyard 		mutex_lock(&priv->recv_mutex);
7356aa2dd00SCorey Minyard 		retries = priv->default_retries;
7366aa2dd00SCorey Minyard 		retry_time_ms = priv->default_retry_time_ms;
7376aa2dd00SCorey Minyard 		mutex_unlock(&priv->recv_mutex);
7386aa2dd00SCorey Minyard 
7396a94f920SAndrew Morton 		return handle_send_req(priv->user, &rp,
7406aa2dd00SCorey Minyard 				       retries, retry_time_ms);
7416a94f920SAndrew Morton 	}
7426a94f920SAndrew Morton 	case COMPAT_IPMICTL_SEND_COMMAND_SETTIME:
7436a94f920SAndrew Morton 	{
7446a94f920SAndrew Morton 		struct ipmi_req_settime	sp;
745e5f699d4SAl Viro 		struct compat_ipmi_req_settime sp32;
7466a94f920SAndrew Morton 
747e5f699d4SAl Viro 		if (copy_from_user(&sp32, compat_ptr(arg), sizeof(sp32)))
7486a94f920SAndrew Morton 			return -EFAULT;
7496a94f920SAndrew Morton 
750e5f699d4SAl Viro 		get_compat_ipmi_req_settime(&sp, &sp32);
751e5f699d4SAl Viro 
7526a94f920SAndrew Morton 		return handle_send_req(priv->user, &sp.req,
7536a94f920SAndrew Morton 				sp.retries, sp.retry_time_ms);
7546a94f920SAndrew Morton 	}
7556a94f920SAndrew Morton 	case COMPAT_IPMICTL_RECEIVE_MSG:
7566a94f920SAndrew Morton 	case COMPAT_IPMICTL_RECEIVE_MSG_TRUNC:
7576a94f920SAndrew Morton 	{
75818fb9442S[email protected] 		struct ipmi_recv   recv64;
759e5f699d4SAl Viro 		struct compat_ipmi_recv recv32;
7606a94f920SAndrew Morton 
761e5f699d4SAl Viro 		if (copy_from_user(&recv32, compat_ptr(arg), sizeof(recv32)))
7626a94f920SAndrew Morton 			return -EFAULT;
7636a94f920SAndrew Morton 
764e5f699d4SAl Viro 		get_compat_ipmi_recv(&recv64, &recv32);
765e5f699d4SAl Viro 
7668b9e04f2SAl Viro 		return handle_recv(priv,
7678b9e04f2SAl Viro 				 cmd == COMPAT_IPMICTL_RECEIVE_MSG_TRUNC,
7688b9e04f2SAl Viro 				 &recv64, copyout_recv32, compat_ptr(arg));
7696a94f920SAndrew Morton 	}
7706a94f920SAndrew Morton 	default:
77155929332SArnd Bergmann 		return ipmi_ioctl(filep, cmd, arg);
7726a94f920SAndrew Morton 	}
7736a94f920SAndrew Morton }
7746a94f920SAndrew Morton #endif
7751da177e4SLinus Torvalds 
77662322d25SArjan van de Ven static const struct file_operations ipmi_fops = {
7771da177e4SLinus Torvalds 	.owner		= THIS_MODULE,
7786aa2dd00SCorey Minyard 	.unlocked_ioctl	= ipmi_ioctl,
7796a94f920SAndrew Morton #ifdef CONFIG_COMPAT
7806aa2dd00SCorey Minyard 	.compat_ioctl   = compat_ipmi_ioctl,
7816a94f920SAndrew Morton #endif
7821da177e4SLinus Torvalds 	.open		= ipmi_open,
7831da177e4SLinus Torvalds 	.release	= ipmi_release,
7841da177e4SLinus Torvalds 	.fasync		= ipmi_fasync,
7851da177e4SLinus Torvalds 	.poll		= ipmi_poll,
7866038f373SArnd Bergmann 	.llseek		= noop_llseek,
7871da177e4SLinus Torvalds };
7881da177e4SLinus Torvalds 
7891da177e4SLinus Torvalds #define DEVICE_NAME     "ipmidev"
7901da177e4SLinus Torvalds 
7910c8204b3SRandy Dunlap static int ipmi_major;
7921da177e4SLinus Torvalds module_param(ipmi_major, int, 0);
7931da177e4SLinus Torvalds MODULE_PARM_DESC(ipmi_major, "Sets the major number of the IPMI device.  By"
7941da177e4SLinus Torvalds 		 " default, or if you set it to zero, it will choose the next"
7951da177e4SLinus Torvalds 		 " available device.  Setting it to -1 will disable the"
7961da177e4SLinus Torvalds 		 " interface.  Other values will set the major device number"
7971da177e4SLinus Torvalds 		 " to that value.");
7981da177e4SLinus Torvalds 
79950c812b2SCorey Minyard /* Keep track of the devices that are registered. */
80050c812b2SCorey Minyard struct ipmi_reg_list {
80150c812b2SCorey Minyard 	dev_t            dev;
80250c812b2SCorey Minyard 	struct list_head link;
80350c812b2SCorey Minyard };
80450c812b2SCorey Minyard static LIST_HEAD(reg_list);
80550c812b2SCorey Minyard static DEFINE_MUTEX(reg_list_mutex);
80650c812b2SCorey Minyard 
807392fa3a3SIvan Orlov static const struct class ipmi_class = {
808392fa3a3SIvan Orlov 	.name = "ipmi",
809392fa3a3SIvan Orlov };
81037e0915bSCorey Minyard 
ipmi_new_smi(int if_num,struct device * device)81150c812b2SCorey Minyard static void ipmi_new_smi(int if_num, struct device *device)
8121da177e4SLinus Torvalds {
81337e0915bSCorey Minyard 	dev_t dev = MKDEV(ipmi_major, if_num);
81450c812b2SCorey Minyard 	struct ipmi_reg_list *entry;
81537e0915bSCorey Minyard 
81650c812b2SCorey Minyard 	entry = kmalloc(sizeof(*entry), GFP_KERNEL);
81750c812b2SCorey Minyard 	if (!entry) {
818f993cdd9SJoe Perches 		pr_err("ipmi_devintf: Unable to create the ipmi class device link\n");
81950c812b2SCorey Minyard 		return;
82050c812b2SCorey Minyard 	}
82150c812b2SCorey Minyard 	entry->dev = dev;
82250c812b2SCorey Minyard 
82350c812b2SCorey Minyard 	mutex_lock(&reg_list_mutex);
824392fa3a3SIvan Orlov 	device_create(&ipmi_class, device, dev, NULL, "ipmi%d", if_num);
82550c812b2SCorey Minyard 	list_add(&entry->link, &reg_list);
82650c812b2SCorey Minyard 	mutex_unlock(&reg_list_mutex);
8271da177e4SLinus Torvalds }
8281da177e4SLinus Torvalds 
ipmi_smi_gone(int if_num)8291da177e4SLinus Torvalds static void ipmi_smi_gone(int if_num)
8301da177e4SLinus Torvalds {
83150c812b2SCorey Minyard 	dev_t dev = MKDEV(ipmi_major, if_num);
83250c812b2SCorey Minyard 	struct ipmi_reg_list *entry;
83350c812b2SCorey Minyard 
83450c812b2SCorey Minyard 	mutex_lock(&reg_list_mutex);
83550c812b2SCorey Minyard 	list_for_each_entry(entry, &reg_list, link) {
83650c812b2SCorey Minyard 		if (entry->dev == dev) {
83750c812b2SCorey Minyard 			list_del(&entry->link);
83850c812b2SCorey Minyard 			kfree(entry);
83950c812b2SCorey Minyard 			break;
84050c812b2SCorey Minyard 		}
84150c812b2SCorey Minyard 	}
842392fa3a3SIvan Orlov 	device_destroy(&ipmi_class, dev);
84350c812b2SCorey Minyard 	mutex_unlock(&reg_list_mutex);
8441da177e4SLinus Torvalds }
8451da177e4SLinus Torvalds 
8461da177e4SLinus Torvalds static struct ipmi_smi_watcher smi_watcher =
8471da177e4SLinus Torvalds {
8481da177e4SLinus Torvalds 	.owner    = THIS_MODULE,
8491da177e4SLinus Torvalds 	.new_smi  = ipmi_new_smi,
8501da177e4SLinus Torvalds 	.smi_gone = ipmi_smi_gone,
8511da177e4SLinus Torvalds };
8521da177e4SLinus Torvalds 
init_ipmi_devintf(void)85360ee6d5fSCorey Minyard static int __init init_ipmi_devintf(void)
8541da177e4SLinus Torvalds {
8551da177e4SLinus Torvalds 	int rv;
8561da177e4SLinus Torvalds 
8571da177e4SLinus Torvalds 	if (ipmi_major < 0)
8581da177e4SLinus Torvalds 		return -EINVAL;
8591da177e4SLinus Torvalds 
860f993cdd9SJoe Perches 	pr_info("ipmi device interface\n");
8611da177e4SLinus Torvalds 
862392fa3a3SIvan Orlov 	rv = class_register(&ipmi_class);
863392fa3a3SIvan Orlov 	if (rv)
864392fa3a3SIvan Orlov 		return rv;
86537e0915bSCorey Minyard 
8661da177e4SLinus Torvalds 	rv = register_chrdev(ipmi_major, DEVICE_NAME, &ipmi_fops);
8671da177e4SLinus Torvalds 	if (rv < 0) {
868392fa3a3SIvan Orlov 		class_unregister(&ipmi_class);
869f993cdd9SJoe Perches 		pr_err("ipmi: can't get major %d\n", ipmi_major);
8701da177e4SLinus Torvalds 		return rv;
8711da177e4SLinus Torvalds 	}
8721da177e4SLinus Torvalds 
8731da177e4SLinus Torvalds 	if (ipmi_major == 0) {
8741da177e4SLinus Torvalds 		ipmi_major = rv;
8751da177e4SLinus Torvalds 	}
8761da177e4SLinus Torvalds 
8771da177e4SLinus Torvalds 	rv = ipmi_smi_watcher_register(&smi_watcher);
8781da177e4SLinus Torvalds 	if (rv) {
8791da177e4SLinus Torvalds 		unregister_chrdev(ipmi_major, DEVICE_NAME);
880392fa3a3SIvan Orlov 		class_unregister(&ipmi_class);
881f993cdd9SJoe Perches 		pr_warn("ipmi: can't register smi watcher\n");
8821da177e4SLinus Torvalds 		return rv;
8831da177e4SLinus Torvalds 	}
8841da177e4SLinus Torvalds 
8851da177e4SLinus Torvalds 	return 0;
8861da177e4SLinus Torvalds }
8871da177e4SLinus Torvalds module_init(init_ipmi_devintf);
8881da177e4SLinus Torvalds 
cleanup_ipmi(void)88960ee6d5fSCorey Minyard static void __exit cleanup_ipmi(void)
8901da177e4SLinus Torvalds {
89150c812b2SCorey Minyard 	struct ipmi_reg_list *entry, *entry2;
89250c812b2SCorey Minyard 	mutex_lock(&reg_list_mutex);
89350c812b2SCorey Minyard 	list_for_each_entry_safe(entry, entry2, &reg_list, link) {
89450c812b2SCorey Minyard 		list_del(&entry->link);
895392fa3a3SIvan Orlov 		device_destroy(&ipmi_class, entry->dev);
89650c812b2SCorey Minyard 		kfree(entry);
89750c812b2SCorey Minyard 	}
89850c812b2SCorey Minyard 	mutex_unlock(&reg_list_mutex);
899392fa3a3SIvan Orlov 	class_unregister(&ipmi_class);
9001da177e4SLinus Torvalds 	ipmi_smi_watcher_unregister(&smi_watcher);
9011da177e4SLinus Torvalds 	unregister_chrdev(ipmi_major, DEVICE_NAME);
9021da177e4SLinus Torvalds }
9031da177e4SLinus Torvalds module_exit(cleanup_ipmi);
9041da177e4SLinus Torvalds 
9051da177e4SLinus Torvalds MODULE_LICENSE("GPL");
9061fdd75bdSCorey Minyard MODULE_AUTHOR("Corey Minyard <[email protected]>");
9071fdd75bdSCorey Minyard MODULE_DESCRIPTION("Linux device interface for the IPMI message handler.");
908