/*
* ipmi_devintf.c
*
* Linux device interface for the IPMI message handler.
*
* Author: MontaVista Software, Inc.
* Corey Minyard <minyard@mvista.com>
* source@mvista.com
*
* Copyright 2002 MontaVista Software Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/errno.h>
#include <asm/system.h>
#include <linux/poll.h>
#include <linux/spinlock.h>
#include <linux/slab.h>
#include <linux/ipmi.h>
#include <linux/mutex.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/compat.h>
#include <linux/smp_lock.h>
struct ipmi_file_private
{
ipmi_user_t user;
spinlock_t recv_msg_lock;
struct list_head recv_msgs;
struct file *file;
struct fasync_struct *fasync_queue;
wait_queue_head_t wait;
struct mutex recv_mutex;
int default_retries;
unsigned int default_retry_time_ms;
};
static void file_receive_handler(struct ipmi_recv_msg *msg,
void *handler_data)
{
struct ipmi_file_private *priv = handler_data;
int was_empty;
unsigned long flags;
spin_lock_irqsave(&(priv->recv_msg_lock), flags);
was_empty = list_empty(&(priv->recv_msgs));
list_add_tail(&(msg->link), &(priv->recv_msgs));
if (was_empty) {
wake_up_interruptible(&priv->wait);
kill_fasync(&priv->fasync_queue, SIGIO, POLL_IN);
}
spin_unlock_irqrestore(&(priv->recv_msg_lock), flags);
}
static unsigned int ipmi_poll(struct file *file, poll_table *wait)
{
struct ipmi_file_private *priv = file->private_data;
unsigned int mask = 0;
unsigned long flags;
poll_wait(file, &priv->wait, wait);
spin_lock_irqsave(&priv->recv_msg_lock, flags);
if (!list_empty(&(priv->recv_msgs)))
mask |= (POLLIN | POLLRDNORM);
spin_unlock_irqrestore(&priv->recv_msg_lock, flags);
return mask;
}
static int ipmi_fasync(int fd, struct file *file, int on)
{
struct ipmi_file_private *priv = file->private_data;
int result;
lock_kernel(); /* could race against open() otherwise */
result = fasync_helper(fd, file, on, &priv->fasync_queue);
unlock_kernel();
return (result);
}
static struct ipmi_user_hndl ipmi_hndlrs =
{
.ipmi_recv_hndl = file_receive_handler,
};
static int ipmi_open(struct inode *inode, struct file *file)
{
int if_num = iminor(inode);
int rv;
struct ipmi_file_private *priv;
priv = kmalloc(sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
lock_kernel();
priv->file = file;
rv = ipmi_create_user(if_num,
&ipmi_hndlrs,
priv,
&(priv->user));
if (rv) {
kfree(priv);
goto out;
}
file->private_data = priv;
spin_lock_init(&(priv->recv_msg_lock));
INIT_LIST_HEAD(&(priv->recv_msgs));
init_waitqueue_head(&priv->wait);
priv->fasync_queue = NULL;
mutex_init(&priv->recv_mutex);
/* Use the low-level defaults. */
priv->default_retries = -1;
priv->default_retry_time_ms = 0;
out:
unlock_kernel();
return rv;
}
static int ipmi_release(struct inode *inode, struct file *file)
{
struct ipmi_file_private *priv = file->private_data;
int rv;
rv = ipmi_destroy_user(priv->user);
if (rv)
return rv;
/* FIXME - free the messages in the list. */
kfree(priv);
return 0;
}
static int handle_send_req(ipmi_user_t user,
struct ipmi_req *req,
int retries,
unsigned int retry_time_ms)
{
int rv;
struct ipmi_addr addr;
struct kernel_ipmi_msg msg;
if (req->addr_len > sizeof(struct ipmi_addr))