• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/linux/linux-2.6/drivers/char/ipmi/
1/*
2 * ipmi_devintf.c
3 *
4 * Linux device interface for the IPMI message handler.
5 *
6 * Author: MontaVista Software, Inc.
7 *         Corey Minyard <minyard@mvista.com>
8 *         source@mvista.com
9 *
10 * Copyright 2002 MontaVista Software Inc.
11 *
12 *  This program is free software; you can redistribute it and/or modify it
13 *  under the terms of the GNU General Public License as published by the
14 *  Free Software Foundation; either version 2 of the License, or (at your
15 *  option) any later version.
16 *
17 *
18 *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
19 *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
20 *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 *  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
23 *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
24 *  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 *  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
26 *  TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
27 *  USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 *
29 *  You should have received a copy of the GNU General Public License along
30 *  with this program; if not, write to the Free Software Foundation, Inc.,
31 *  675 Mass Ave, Cambridge, MA 02139, USA.
32 */
33
34#include <linux/module.h>
35#include <linux/moduleparam.h>
36#include <linux/errno.h>
37#include <asm/system.h>
38#include <linux/poll.h>
39#include <linux/sched.h>
40#include <linux/spinlock.h>
41#include <linux/slab.h>
42#include <linux/ipmi.h>
43#include <linux/mutex.h>
44#include <linux/init.h>
45#include <linux/device.h>
46#include <linux/compat.h>
47#include <linux/smp_lock.h>
48
49struct ipmi_file_private
50{
51	ipmi_user_t          user;
52	spinlock_t           recv_msg_lock;
53	struct list_head     recv_msgs;
54	struct file          *file;
55	struct fasync_struct *fasync_queue;
56	wait_queue_head_t    wait;
57	struct mutex	     recv_mutex;
58	int                  default_retries;
59	unsigned int         default_retry_time_ms;
60};
61
62static void file_receive_handler(struct ipmi_recv_msg *msg,
63				 void                 *handler_data)
64{
65	struct ipmi_file_private *priv = handler_data;
66	int                      was_empty;
67	unsigned long            flags;
68
69	spin_lock_irqsave(&(priv->recv_msg_lock), flags);
70
71	was_empty = list_empty(&(priv->recv_msgs));
72	list_add_tail(&(msg->link), &(priv->recv_msgs));
73
74	if (was_empty) {
75		wake_up_interruptible(&priv->wait);
76		kill_fasync(&priv->fasync_queue, SIGIO, POLL_IN);
77	}
78
79	spin_unlock_irqrestore(&(priv->recv_msg_lock), flags);
80}
81
82static unsigned int ipmi_poll(struct file *file, poll_table *wait)
83{
84	struct ipmi_file_private *priv = file->private_data;
85	unsigned int             mask = 0;
86	unsigned long            flags;
87
88	poll_wait(file, &priv->wait, wait);
89
90	spin_lock_irqsave(&priv->recv_msg_lock, flags);
91
92	if (!list_empty(&(priv->recv_msgs)))
93		mask |= (POLLIN | POLLRDNORM);
94
95	spin_unlock_irqrestore(&priv->recv_msg_lock, flags);
96
97	return mask;
98}
99
100static int ipmi_fasync(int fd, struct file *file, int on)
101{
102	struct ipmi_file_private *priv = file->private_data;
103	int                      result;
104
105	lock_kernel(); /* could race against open() otherwise */
106	result = fasync_helper(fd, file, on, &priv->fasync_queue);
107	unlock_kernel();
108
109	return (result);
110}
111
112static struct ipmi_user_hndl ipmi_hndlrs =
113{
114	.ipmi_recv_hndl	= file_receive_handler,
115};
116
117static int ipmi_open(struct inode *inode, struct file *file)
118{
119	int                      if_num = iminor(inode);
120	int                      rv;
121	struct ipmi_file_private *priv;
122
123
124	priv = kmalloc(sizeof(*priv), GFP_KERNEL);
125	if (!priv)
126		return -ENOMEM;
127
128	lock_kernel();
129	priv->file = file;
130
131	rv = ipmi_create_user(if_num,
132			      &ipmi_hndlrs,
133			      priv,
134			      &(priv->user));
135	if (rv) {
136		kfree(priv);
137		goto out;
138	}
139
140	file->private_data = priv;
141
142	spin_lock_init(&(priv->recv_msg_lock));
143	INIT_LIST_HEAD(&(priv->recv_msgs));
144	init_waitqueue_head(&priv->wait);
145	priv->fasync_queue = NULL;
146	mutex_init(&priv->recv_mutex);
147
148	/* Use the low-level defaults. */
149	priv->default_retries = -1;
150	priv->default_retry_time_ms = 0;
151
152out:
153	unlock_kernel();
154	return rv;
155}
156
157static int ipmi_release(struct inode *inode, struct file *file)
158{
159	struct ipmi_file_private *priv = file->private_data;
160	int                      rv;
161
162	rv = ipmi_destroy_user(priv->user);
163	if (rv)
164		return rv;
165
166	kfree(priv);
167
168	return 0;
169}
170
171static int handle_send_req(ipmi_user_t     user,
172			   struct ipmi_req *req,
173			   int             retries,
174			   unsigned int    retry_time_ms)
175{
176	int              rv;
177	struct ipmi_addr addr;
178	struct kernel_ipmi_msg msg;
179
180	if (req->addr_len > sizeof(struct ipmi_addr))
181		return -EINVAL;
182
183	if (copy_from_user(&addr, req->addr, req->addr_len))
184		return -EFAULT;
185
186	msg.netfn = req->msg.netfn;
187	msg.cmd = req->msg.cmd;
188	msg.data_len = req->msg.data_len;
189	msg.data = kmalloc(IPMI_MAX_MSG_LENGTH, GFP_KERNEL);
190	if (!msg.data)
191		return -ENOMEM;
192
193	/* From here out we cannot return, we must jump to "out" for
194	   error exits to free msgdata. */
195
196	rv = ipmi_validate_addr(&addr, req->addr_len);
197	if (rv)
198		goto out;
199
200	if (req->msg.data != NULL) {
201		if (req->msg.data_len > IPMI_MAX_MSG_LENGTH) {
202			rv = -EMSGSIZE;
203			goto out;
204		}
205
206		if (copy_from_user(msg.data,
207				   req->msg.data,
208				   req->msg.data_len))
209		{
210			rv = -EFAULT;
211			goto out;
212		}
213	} else {
214		msg.data_len = 0;
215	}
216
217	rv = ipmi_request_settime(user,
218				  &addr,
219				  req->msgid,
220				  &msg,
221				  NULL,
222				  0,
223				  retries,
224				  retry_time_ms);
225 out:
226	kfree(msg.data);
227	return rv;
228}
229
230static int ipmi_ioctl(struct file   *file,
231		      unsigned int  cmd,
232		      unsigned long data)
233{
234	int                      rv = -EINVAL;
235	struct ipmi_file_private *priv = file->private_data;
236	void __user *arg = (void __user *)data;
237
238	switch (cmd)
239	{
240	case IPMICTL_SEND_COMMAND:
241	{
242		struct ipmi_req req;
243
244		if (copy_from_user(&req, arg, sizeof(req))) {
245			rv = -EFAULT;
246			break;
247		}
248
249		rv = handle_send_req(priv->user,
250				     &req,
251				     priv->default_retries,
252				     priv->default_retry_time_ms);
253		break;
254	}
255
256	case IPMICTL_SEND_COMMAND_SETTIME:
257	{
258		struct ipmi_req_settime req;
259
260		if (copy_from_user(&req, arg, sizeof(req))) {
261			rv = -EFAULT;
262			break;
263		}
264
265		rv = handle_send_req(priv->user,
266				     &req.req,
267				     req.retries,
268				     req.retry_time_ms);
269		break;
270	}
271
272	case IPMICTL_RECEIVE_MSG:
273	case IPMICTL_RECEIVE_MSG_TRUNC:
274	{
275		struct ipmi_recv      rsp;
276		int              addr_len;
277		struct list_head *entry;
278		struct ipmi_recv_msg  *msg;
279		unsigned long    flags;
280
281
282		rv = 0;
283		if (copy_from_user(&rsp, arg, sizeof(rsp))) {
284			rv = -EFAULT;
285			break;
286		}
287
288		/* We claim a mutex because we don't want two
289                   users getting something from the queue at a time.
290                   Since we have to release the spinlock before we can
291                   copy the data to the user, it's possible another
292                   user will grab something from the queue, too.  Then
293                   the messages might get out of order if something
294                   fails and the message gets put back onto the
295                   queue.  This mutex prevents that problem. */
296		mutex_lock(&priv->recv_mutex);
297
298		/* Grab the message off the list. */
299		spin_lock_irqsave(&(priv->recv_msg_lock), flags);
300		if (list_empty(&(priv->recv_msgs))) {
301			spin_unlock_irqrestore(&(priv->recv_msg_lock), flags);
302			rv = -EAGAIN;
303			goto recv_err;
304		}
305		entry = priv->recv_msgs.next;
306		msg = list_entry(entry, struct ipmi_recv_msg, link);
307		list_del(entry);
308		spin_unlock_irqrestore(&(priv->recv_msg_lock), flags);
309
310		addr_len = ipmi_addr_length(msg->addr.addr_type);
311		if (rsp.addr_len < addr_len)
312		{
313			rv = -EINVAL;
314			goto recv_putback_on_err;
315		}
316
317		if (copy_to_user(rsp.addr, &(msg->addr), addr_len)) {
318			rv = -EFAULT;
319			goto recv_putback_on_err;
320		}
321		rsp.addr_len = addr_len;
322
323		rsp.recv_type = msg->recv_type;
324		rsp.msgid = msg->msgid;
325		rsp.msg.netfn = msg->msg.netfn;
326		rsp.msg.cmd = msg->msg.cmd;
327
328		if (msg->msg.data_len > 0) {
329			if (rsp.msg.data_len < msg->msg.data_len) {
330				rv = -EMSGSIZE;
331				if (cmd == IPMICTL_RECEIVE_MSG_TRUNC) {
332					msg->msg.data_len = rsp.msg.data_len;
333				} else {
334					goto recv_putback_on_err;
335				}
336			}
337
338			if (copy_to_user(rsp.msg.data,
339					 msg->msg.data,
340					 msg->msg.data_len))
341			{
342				rv = -EFAULT;
343				goto recv_putback_on_err;
344			}
345			rsp.msg.data_len = msg->msg.data_len;
346		} else {
347			rsp.msg.data_len = 0;
348		}
349
350		if (copy_to_user(arg, &rsp, sizeof(rsp))) {
351			rv = -EFAULT;
352			goto recv_putback_on_err;
353		}
354
355		mutex_unlock(&priv->recv_mutex);
356		ipmi_free_recv_msg(msg);
357		break;
358
359	recv_putback_on_err:
360		/* If we got an error, put the message back onto
361		   the head of the queue. */
362		spin_lock_irqsave(&(priv->recv_msg_lock), flags);
363		list_add(entry, &(priv->recv_msgs));
364		spin_unlock_irqrestore(&(priv->recv_msg_lock), flags);
365		mutex_unlock(&priv->recv_mutex);
366		break;
367
368	recv_err:
369		mutex_unlock(&priv->recv_mutex);
370		break;
371	}
372
373	case IPMICTL_REGISTER_FOR_CMD:
374	{
375		struct ipmi_cmdspec val;
376
377		if (copy_from_user(&val, arg, sizeof(val))) {
378			rv = -EFAULT;
379			break;
380		}
381
382		rv = ipmi_register_for_cmd(priv->user, val.netfn, val.cmd,
383					   IPMI_CHAN_ALL);
384		break;
385	}
386
387	case IPMICTL_UNREGISTER_FOR_CMD:
388	{
389		struct ipmi_cmdspec   val;
390
391		if (copy_from_user(&val, arg, sizeof(val))) {
392			rv = -EFAULT;
393			break;
394		}
395
396		rv = ipmi_unregister_for_cmd(priv->user, val.netfn, val.cmd,
397					     IPMI_CHAN_ALL);
398		break;
399	}
400
401	case IPMICTL_REGISTER_FOR_CMD_CHANS:
402	{
403		struct ipmi_cmdspec_chans val;
404
405		if (copy_from_user(&val, arg, sizeof(val))) {
406			rv = -EFAULT;
407			break;
408		}
409
410		rv = ipmi_register_for_cmd(priv->user, val.netfn, val.cmd,
411					   val.chans);
412		break;
413	}
414
415	case IPMICTL_UNREGISTER_FOR_CMD_CHANS:
416	{
417		struct ipmi_cmdspec_chans val;
418
419		if (copy_from_user(&val, arg, sizeof(val))) {
420			rv = -EFAULT;
421			break;
422		}
423
424		rv = ipmi_unregister_for_cmd(priv->user, val.netfn, val.cmd,
425					     val.chans);
426		break;
427	}
428
429	case IPMICTL_SET_GETS_EVENTS_CMD:
430	{
431		int val;
432
433		if (copy_from_user(&val, arg, sizeof(val))) {
434			rv = -EFAULT;
435			break;
436		}
437
438		rv = ipmi_set_gets_events(priv->user, val);
439		break;
440	}
441
442	/* The next four are legacy, not per-channel. */
443	case IPMICTL_SET_MY_ADDRESS_CMD:
444	{
445		unsigned int val;
446
447		if (copy_from_user(&val, arg, sizeof(val))) {
448			rv = -EFAULT;
449			break;
450		}
451
452		rv = ipmi_set_my_address(priv->user, 0, val);
453		break;
454	}
455
456	case IPMICTL_GET_MY_ADDRESS_CMD:
457	{
458		unsigned int  val;
459		unsigned char rval;
460
461		rv = ipmi_get_my_address(priv->user, 0, &rval);
462		if (rv)
463			break;
464
465		val = rval;
466
467		if (copy_to_user(arg, &val, sizeof(val))) {
468			rv = -EFAULT;
469			break;
470		}
471		break;
472	}
473
474	case IPMICTL_SET_MY_LUN_CMD:
475	{
476		unsigned int val;
477
478		if (copy_from_user(&val, arg, sizeof(val))) {
479			rv = -EFAULT;
480			break;
481		}
482
483		rv = ipmi_set_my_LUN(priv->user, 0, val);
484		break;
485	}
486
487	case IPMICTL_GET_MY_LUN_CMD:
488	{
489		unsigned int  val;
490		unsigned char rval;
491
492		rv = ipmi_get_my_LUN(priv->user, 0, &rval);
493		if (rv)
494			break;
495
496		val = rval;
497
498		if (copy_to_user(arg, &val, sizeof(val))) {
499			rv = -EFAULT;
500			break;
501		}
502		break;
503	}
504
505	case IPMICTL_SET_MY_CHANNEL_ADDRESS_CMD:
506	{
507		struct ipmi_channel_lun_address_set val;
508
509		if (copy_from_user(&val, arg, sizeof(val))) {
510			rv = -EFAULT;
511			break;
512		}
513
514		return ipmi_set_my_address(priv->user, val.channel, val.value);
515		break;
516	}
517
518	case IPMICTL_GET_MY_CHANNEL_ADDRESS_CMD:
519	{
520		struct ipmi_channel_lun_address_set val;
521
522		if (copy_from_user(&val, arg, sizeof(val))) {
523			rv = -EFAULT;
524			break;
525		}
526
527		rv = ipmi_get_my_address(priv->user, val.channel, &val.value);
528		if (rv)
529			break;
530
531		if (copy_to_user(arg, &val, sizeof(val))) {
532			rv = -EFAULT;
533			break;
534		}
535		break;
536	}
537
538	case IPMICTL_SET_MY_CHANNEL_LUN_CMD:
539	{
540		struct ipmi_channel_lun_address_set val;
541
542		if (copy_from_user(&val, arg, sizeof(val))) {
543			rv = -EFAULT;
544			break;
545		}
546
547		rv = ipmi_set_my_LUN(priv->user, val.channel, val.value);
548		break;
549	}
550
551	case IPMICTL_GET_MY_CHANNEL_LUN_CMD:
552	{
553		struct ipmi_channel_lun_address_set val;
554
555		if (copy_from_user(&val, arg, sizeof(val))) {
556			rv = -EFAULT;
557			break;
558		}
559
560		rv = ipmi_get_my_LUN(priv->user, val.channel, &val.value);
561		if (rv)
562			break;
563
564		if (copy_to_user(arg, &val, sizeof(val))) {
565			rv = -EFAULT;
566			break;
567		}
568		break;
569	}
570
571	case IPMICTL_SET_TIMING_PARMS_CMD:
572	{
573		struct ipmi_timing_parms parms;
574
575		if (copy_from_user(&parms, arg, sizeof(parms))) {
576			rv = -EFAULT;
577			break;
578		}
579
580		priv->default_retries = parms.retries;
581		priv->default_retry_time_ms = parms.retry_time_ms;
582		rv = 0;
583		break;
584	}
585
586	case IPMICTL_GET_TIMING_PARMS_CMD:
587	{
588		struct ipmi_timing_parms parms;
589
590		parms.retries = priv->default_retries;
591		parms.retry_time_ms = priv->default_retry_time_ms;
592
593		if (copy_to_user(arg, &parms, sizeof(parms))) {
594			rv = -EFAULT;
595			break;
596		}
597
598		rv = 0;
599		break;
600	}
601
602	case IPMICTL_GET_MAINTENANCE_MODE_CMD:
603	{
604		int mode;
605
606		mode = ipmi_get_maintenance_mode(priv->user);
607		if (copy_to_user(arg, &mode, sizeof(mode))) {
608			rv = -EFAULT;
609			break;
610		}
611		rv = 0;
612		break;
613	}
614
615	case IPMICTL_SET_MAINTENANCE_MODE_CMD:
616	{
617		int mode;
618
619		if (copy_from_user(&mode, arg, sizeof(mode))) {
620			rv = -EFAULT;
621			break;
622		}
623		rv = ipmi_set_maintenance_mode(priv->user, mode);
624		break;
625	}
626	}
627
628	return rv;
629}
630
631/*
632 * Note: it doesn't make sense to take the BKL here but
633 *       not in compat_ipmi_ioctl. -arnd
634 */
635static long ipmi_unlocked_ioctl(struct file   *file,
636			        unsigned int  cmd,
637			        unsigned long data)
638{
639	int ret;
640
641	lock_kernel();
642	ret = ipmi_ioctl(file, cmd, data);
643	unlock_kernel();
644
645	return ret;
646}
647
648#ifdef CONFIG_COMPAT
649
650/*
651 * The following code contains code for supporting 32-bit compatible
652 * ioctls on 64-bit kernels.  This allows running 32-bit apps on the
653 * 64-bit kernel
654 */
655#define COMPAT_IPMICTL_SEND_COMMAND	\
656	_IOR(IPMI_IOC_MAGIC, 13, struct compat_ipmi_req)
657#define COMPAT_IPMICTL_SEND_COMMAND_SETTIME	\
658	_IOR(IPMI_IOC_MAGIC, 21, struct compat_ipmi_req_settime)
659#define COMPAT_IPMICTL_RECEIVE_MSG	\
660	_IOWR(IPMI_IOC_MAGIC, 12, struct compat_ipmi_recv)
661#define COMPAT_IPMICTL_RECEIVE_MSG_TRUNC	\
662	_IOWR(IPMI_IOC_MAGIC, 11, struct compat_ipmi_recv)
663
664struct compat_ipmi_msg {
665	u8		netfn;
666	u8		cmd;
667	u16		data_len;
668	compat_uptr_t	data;
669};
670
671struct compat_ipmi_req {
672	compat_uptr_t		addr;
673	compat_uint_t		addr_len;
674	compat_long_t		msgid;
675	struct compat_ipmi_msg	msg;
676};
677
678struct compat_ipmi_recv {
679	compat_int_t		recv_type;
680	compat_uptr_t		addr;
681	compat_uint_t		addr_len;
682	compat_long_t		msgid;
683	struct compat_ipmi_msg	msg;
684};
685
686struct compat_ipmi_req_settime {
687	struct compat_ipmi_req	req;
688	compat_int_t		retries;
689	compat_uint_t		retry_time_ms;
690};
691
692/*
693 * Define some helper functions for copying IPMI data
694 */
695static long get_compat_ipmi_msg(struct ipmi_msg *p64,
696				struct compat_ipmi_msg __user *p32)
697{
698	compat_uptr_t tmp;
699
700	if (!access_ok(VERIFY_READ, p32, sizeof(*p32)) ||
701			__get_user(p64->netfn, &p32->netfn) ||
702			__get_user(p64->cmd, &p32->cmd) ||
703			__get_user(p64->data_len, &p32->data_len) ||
704			__get_user(tmp, &p32->data))
705		return -EFAULT;
706	p64->data = compat_ptr(tmp);
707	return 0;
708}
709
710static long put_compat_ipmi_msg(struct ipmi_msg *p64,
711				struct compat_ipmi_msg __user *p32)
712{
713	if (!access_ok(VERIFY_WRITE, p32, sizeof(*p32)) ||
714			__put_user(p64->netfn, &p32->netfn) ||
715			__put_user(p64->cmd, &p32->cmd) ||
716			__put_user(p64->data_len, &p32->data_len))
717		return -EFAULT;
718	return 0;
719}
720
721static long get_compat_ipmi_req(struct ipmi_req *p64,
722				struct compat_ipmi_req __user *p32)
723{
724
725	compat_uptr_t	tmp;
726
727	if (!access_ok(VERIFY_READ, p32, sizeof(*p32)) ||
728			__get_user(tmp, &p32->addr) ||
729			__get_user(p64->addr_len, &p32->addr_len) ||
730			__get_user(p64->msgid, &p32->msgid) ||
731			get_compat_ipmi_msg(&p64->msg, &p32->msg))
732		return -EFAULT;
733	p64->addr = compat_ptr(tmp);
734	return 0;
735}
736
737static long get_compat_ipmi_req_settime(struct ipmi_req_settime *p64,
738		struct compat_ipmi_req_settime __user *p32)
739{
740	if (!access_ok(VERIFY_READ, p32, sizeof(*p32)) ||
741			get_compat_ipmi_req(&p64->req, &p32->req) ||
742			__get_user(p64->retries, &p32->retries) ||
743			__get_user(p64->retry_time_ms, &p32->retry_time_ms))
744		return -EFAULT;
745	return 0;
746}
747
748static long get_compat_ipmi_recv(struct ipmi_recv *p64,
749				 struct compat_ipmi_recv __user *p32)
750{
751	compat_uptr_t tmp;
752
753	if (!access_ok(VERIFY_READ, p32, sizeof(*p32)) ||
754			__get_user(p64->recv_type, &p32->recv_type) ||
755			__get_user(tmp, &p32->addr) ||
756			__get_user(p64->addr_len, &p32->addr_len) ||
757			__get_user(p64->msgid, &p32->msgid) ||
758			get_compat_ipmi_msg(&p64->msg, &p32->msg))
759		return -EFAULT;
760	p64->addr = compat_ptr(tmp);
761	return 0;
762}
763
764static long put_compat_ipmi_recv(struct ipmi_recv *p64,
765				 struct compat_ipmi_recv __user *p32)
766{
767	if (!access_ok(VERIFY_WRITE, p32, sizeof(*p32)) ||
768			__put_user(p64->recv_type, &p32->recv_type) ||
769			__put_user(p64->addr_len, &p32->addr_len) ||
770			__put_user(p64->msgid, &p32->msgid) ||
771			put_compat_ipmi_msg(&p64->msg, &p32->msg))
772		return -EFAULT;
773	return 0;
774}
775
776/*
777 * Handle compatibility ioctls
778 */
779static long compat_ipmi_ioctl(struct file *filep, unsigned int cmd,
780			      unsigned long arg)
781{
782	int rc;
783	struct ipmi_file_private *priv = filep->private_data;
784
785	switch(cmd) {
786	case COMPAT_IPMICTL_SEND_COMMAND:
787	{
788		struct ipmi_req	rp;
789
790		if (get_compat_ipmi_req(&rp, compat_ptr(arg)))
791			return -EFAULT;
792
793		return handle_send_req(priv->user, &rp,
794				priv->default_retries,
795				priv->default_retry_time_ms);
796	}
797	case COMPAT_IPMICTL_SEND_COMMAND_SETTIME:
798	{
799		struct ipmi_req_settime	sp;
800
801		if (get_compat_ipmi_req_settime(&sp, compat_ptr(arg)))
802			return -EFAULT;
803
804		return handle_send_req(priv->user, &sp.req,
805				sp.retries, sp.retry_time_ms);
806	}
807	case COMPAT_IPMICTL_RECEIVE_MSG:
808	case COMPAT_IPMICTL_RECEIVE_MSG_TRUNC:
809	{
810		struct ipmi_recv   __user *precv64;
811		struct ipmi_recv   recv64;
812
813		if (get_compat_ipmi_recv(&recv64, compat_ptr(arg)))
814			return -EFAULT;
815
816		precv64 = compat_alloc_user_space(sizeof(recv64));
817		if (copy_to_user(precv64, &recv64, sizeof(recv64)))
818			return -EFAULT;
819
820		rc = ipmi_ioctl(filep,
821				((cmd == COMPAT_IPMICTL_RECEIVE_MSG)
822				 ? IPMICTL_RECEIVE_MSG
823				 : IPMICTL_RECEIVE_MSG_TRUNC),
824				(unsigned long) precv64);
825		if (rc != 0)
826			return rc;
827
828		if (copy_from_user(&recv64, precv64, sizeof(recv64)))
829			return -EFAULT;
830
831		if (put_compat_ipmi_recv(&recv64, compat_ptr(arg)))
832			return -EFAULT;
833
834		return rc;
835	}
836	default:
837		return ipmi_ioctl(filep, cmd, arg);
838	}
839}
840#endif
841
842static const struct file_operations ipmi_fops = {
843	.owner		= THIS_MODULE,
844	.unlocked_ioctl	= ipmi_unlocked_ioctl,
845#ifdef CONFIG_COMPAT
846	.compat_ioctl   = compat_ipmi_ioctl,
847#endif
848	.open		= ipmi_open,
849	.release	= ipmi_release,
850	.fasync		= ipmi_fasync,
851	.poll		= ipmi_poll,
852};
853
854#define DEVICE_NAME     "ipmidev"
855
856static int ipmi_major;
857module_param(ipmi_major, int, 0);
858MODULE_PARM_DESC(ipmi_major, "Sets the major number of the IPMI device.  By"
859		 " default, or if you set it to zero, it will choose the next"
860		 " available device.  Setting it to -1 will disable the"
861		 " interface.  Other values will set the major device number"
862		 " to that value.");
863
864/* Keep track of the devices that are registered. */
865struct ipmi_reg_list {
866	dev_t            dev;
867	struct list_head link;
868};
869static LIST_HEAD(reg_list);
870static DEFINE_MUTEX(reg_list_mutex);
871
872static struct class *ipmi_class;
873
874static void ipmi_new_smi(int if_num, struct device *device)
875{
876	dev_t dev = MKDEV(ipmi_major, if_num);
877	struct ipmi_reg_list *entry;
878
879	entry = kmalloc(sizeof(*entry), GFP_KERNEL);
880	if (!entry) {
881		printk(KERN_ERR "ipmi_devintf: Unable to create the"
882		       " ipmi class device link\n");
883		return;
884	}
885	entry->dev = dev;
886
887	mutex_lock(&reg_list_mutex);
888	device_create(ipmi_class, device, dev, NULL, "ipmi%d", if_num);
889	list_add(&entry->link, &reg_list);
890	mutex_unlock(&reg_list_mutex);
891}
892
893static void ipmi_smi_gone(int if_num)
894{
895	dev_t dev = MKDEV(ipmi_major, if_num);
896	struct ipmi_reg_list *entry;
897
898	mutex_lock(&reg_list_mutex);
899	list_for_each_entry(entry, &reg_list, link) {
900		if (entry->dev == dev) {
901			list_del(&entry->link);
902			kfree(entry);
903			break;
904		}
905	}
906	device_destroy(ipmi_class, dev);
907	mutex_unlock(&reg_list_mutex);
908}
909
910static struct ipmi_smi_watcher smi_watcher =
911{
912	.owner    = THIS_MODULE,
913	.new_smi  = ipmi_new_smi,
914	.smi_gone = ipmi_smi_gone,
915};
916
917static __init int init_ipmi_devintf(void)
918{
919	int rv;
920
921	if (ipmi_major < 0)
922		return -EINVAL;
923
924	printk(KERN_INFO "ipmi device interface\n");
925
926	ipmi_class = class_create(THIS_MODULE, "ipmi");
927	if (IS_ERR(ipmi_class)) {
928		printk(KERN_ERR "ipmi: can't register device class\n");
929		return PTR_ERR(ipmi_class);
930	}
931
932	rv = register_chrdev(ipmi_major, DEVICE_NAME, &ipmi_fops);
933	if (rv < 0) {
934		class_destroy(ipmi_class);
935		printk(KERN_ERR "ipmi: can't get major %d\n", ipmi_major);
936		return rv;
937	}
938
939	if (ipmi_major == 0) {
940		ipmi_major = rv;
941	}
942
943	rv = ipmi_smi_watcher_register(&smi_watcher);
944	if (rv) {
945		unregister_chrdev(ipmi_major, DEVICE_NAME);
946		class_destroy(ipmi_class);
947		printk(KERN_WARNING "ipmi: can't register smi watcher\n");
948		return rv;
949	}
950
951	return 0;
952}
953module_init(init_ipmi_devintf);
954
955static __exit void cleanup_ipmi(void)
956{
957	struct ipmi_reg_list *entry, *entry2;
958	mutex_lock(&reg_list_mutex);
959	list_for_each_entry_safe(entry, entry2, &reg_list, link) {
960		list_del(&entry->link);
961		device_destroy(ipmi_class, entry->dev);
962		kfree(entry);
963	}
964	mutex_unlock(&reg_list_mutex);
965	class_destroy(ipmi_class);
966	ipmi_smi_watcher_unregister(&smi_watcher);
967	unregister_chrdev(ipmi_major, DEVICE_NAME);
968}
969module_exit(cleanup_ipmi);
970
971MODULE_LICENSE("GPL");
972MODULE_AUTHOR("Corey Minyard <minyard@mvista.com>");
973MODULE_DESCRIPTION("Linux device interface for the IPMI message handler.");
974MODULE_ALIAS("platform:ipmi_si");
975