⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ipmi_msghandler.c

📁 一个2.4.21版本的嵌入式linux内核
💻 C
📖 第 1 页 / 共 4 页
字号:
/* * ipmi_msghandler.c * * Incoming and outgoing message routing for an IPMI interface. * * 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/config.h>#include <linux/module.h>#include <linux/errno.h>#include <asm/system.h>#include <linux/sched.h>#include <linux/poll.h>#include <linux/spinlock.h>#include <linux/rwsem.h>#include <linux/slab.h>#include <linux/ipmi.h>#include <linux/ipmi_smi.h>#include <linux/notifier.h>#include <linux/init.h>struct ipmi_recv_msg *ipmi_alloc_recv_msg(void);static int ipmi_init_msghandler(void);static int initialized = 0;#define MAX_EVENTS_IN_QUEUE	25/* Don't let a message sit in a queue forever, always time it with at lest   the max message timer. */#define MAX_MSG_TIMEOUT		60000struct ipmi_user{	struct list_head link;	/* The upper layer that handles receive messages. */	struct ipmi_user_hndl *handler;	void             *handler_data;	/* The interface this user is bound to. */	ipmi_smi_t intf;	/* Does this interface receive IPMI events? */	int gets_events;};struct cmd_rcvr{	struct list_head link;	ipmi_user_t   user;	unsigned char netfn;	unsigned char cmd;};struct seq_table{	int                  inuse : 1;	unsigned long        timeout;	unsigned long        orig_timeout;	unsigned int         retries_left;	/* To verify on an incoming send message response that this is           the message that the response is for, we keep a sequence id           and increment it every time we send a message. */	long                 seqid;	/* This is held so we can properly respond to the message on a           timeout, and it is used to hold the temporary data for           retransmission, too. */	struct ipmi_recv_msg *recv_msg;};/* Store the information in a msgid (long) to allow us to find a   sequence table entry from the msgid. */#define STORE_SEQ_IN_MSGID(seq, seqid) (((seq&0xff)<<26) | (seqid&0x3ffffff))#define GET_SEQ_FROM_MSGID(msgid, seq, seqid) \	do {								\		seq = ((msgid >> 26) & 0x3f);				\		seqid = (msgid & 0x3fffff);				\        } while(0)#define NEXT_SEQID(seqid) (((seqid) + 1) & 0x3fffff)#define IPMI_IPMB_NUM_SEQ	64struct ipmi_smi{	/* The list of upper layers that are using me.  We read-lock           this when delivering messages to the upper layer to keep           the user from going away while we are processing the           message.  This means that you cannot add or delete a user           from the receive callback. */	rwlock_t                users_lock;	struct list_head        users;	/* The IPMI version of the BMC on the other end. */	unsigned char       version_major;	unsigned char       version_minor;	/* This is the lower-layer's sender routine. */	struct ipmi_smi_handlers *handlers;	void                     *send_info;	/* A table of sequence numbers for this interface.  We use the           sequence numbers for IPMB messages that go out of the           interface to match them up with their responses.  A routine           is called periodically to time the items in this list. */	spinlock_t       seq_lock;	struct seq_table seq_table[IPMI_IPMB_NUM_SEQ];	int curr_seq;	/* Messages that were delayed for some reason (out of memory,           for instance), will go in here to be processed later in a           periodic timer interrupt. */	spinlock_t       waiting_msgs_lock;	struct list_head waiting_msgs;	/* The list of command receivers that are registered for commands	   on this interface. */	rwlock_t	 cmd_rcvr_lock;	struct list_head cmd_rcvrs;	/* Events that were queues because no one was there to receive           them. */	spinlock_t       events_lock; /* For dealing with event stuff. */	struct list_head waiting_events;	unsigned int     waiting_events_count; /* How many events in queue? */	/* This will be non-null if someone registers to receive all	   IPMI commands (this is for interface emulation).  There	   may not be any things in the cmd_rcvrs list above when	   this is registered. */	ipmi_user_t all_cmd_rcvr;	/* My slave address.  This is initialized to IPMI_BMC_SLAVE_ADDR,	   but may be changed by the user. */	unsigned char my_address;	/* My LUN.  This should generally stay the SMS LUN, but just in	   case... */	unsigned char my_lun;};intipmi_register_all_cmd_rcvr(ipmi_user_t user){	unsigned long flags;	int           rv = -EBUSY;	write_lock_irqsave(&(user->intf->users_lock), flags);	write_lock(&(user->intf->cmd_rcvr_lock));	if ((user->intf->all_cmd_rcvr == NULL)	    && (list_empty(&(user->intf->cmd_rcvrs))))	{		user->intf->all_cmd_rcvr = user;		rv = 0;	}	write_unlock(&(user->intf->cmd_rcvr_lock));	write_unlock_irqrestore(&(user->intf->users_lock), flags);	return rv;}intipmi_unregister_all_cmd_rcvr(ipmi_user_t user){	unsigned long flags;	int           rv = -EINVAL;	write_lock_irqsave(&(user->intf->users_lock), flags);	write_lock(&(user->intf->cmd_rcvr_lock));	if (user->intf->all_cmd_rcvr == user)	{		user->intf->all_cmd_rcvr = NULL;		rv = 0;	}	write_unlock(&(user->intf->cmd_rcvr_lock));	write_unlock_irqrestore(&(user->intf->users_lock), flags);	return rv;}#define MAX_IPMI_INTERFACES 4static ipmi_smi_t ipmi_interfaces[MAX_IPMI_INTERFACES];/* Used to keep interfaces from going away while operations are   operating on interfaces.  Grab read if you are not modifying the   interfaces, write if you are. */static DECLARE_RWSEM(interfaces_sem);/* Directly protects the ipmi_interfaces data structure.  This is   claimed in the timer interrupt. */static spinlock_t interfaces_lock = SPIN_LOCK_UNLOCKED;/* List of watchers that want to know when smi's are added and   deleted. */static struct list_head smi_watchers = LIST_HEAD_INIT(smi_watchers);static DECLARE_RWSEM(smi_watchers_sem);int ipmi_smi_watcher_register(struct ipmi_smi_watcher *watcher){	int i;	down_read(&interfaces_sem);	down_write(&smi_watchers_sem);	list_add(&(watcher->link), &smi_watchers);	for (i=0; i<MAX_IPMI_INTERFACES; i++) {		if (ipmi_interfaces[i] != NULL) {			watcher->new_smi(i);		}	}	up_write(&smi_watchers_sem);	up_read(&interfaces_sem);	return 0;}int ipmi_smi_watcher_unregister(struct ipmi_smi_watcher *watcher){	down_write(&smi_watchers_sem);	list_del(&(watcher->link));	up_write(&smi_watchers_sem);	return 0;}intipmi_addr_equal(struct ipmi_addr *addr1, struct ipmi_addr *addr2){	if (addr1->addr_type != addr2->addr_type)		return 0;	if (addr1->channel != addr2->channel)		return 0;	if (addr1->addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE) {		struct ipmi_system_interface_addr *smi_addr1		    = (struct ipmi_system_interface_addr *) addr1;		struct ipmi_system_interface_addr *smi_addr2		    = (struct ipmi_system_interface_addr *) addr2;		return (smi_addr1->lun == smi_addr2->lun);	}	if ((addr1->addr_type == IPMI_IPMB_ADDR_TYPE)	    || (addr1->addr_type == IPMI_IPMB_BROADCAST_ADDR_TYPE))	{		struct ipmi_ipmb_addr *ipmb_addr1		    = (struct ipmi_ipmb_addr *) addr1;		struct ipmi_ipmb_addr *ipmb_addr2		    = (struct ipmi_ipmb_addr *) addr2;		return ((ipmb_addr1->slave_addr == ipmb_addr2->slave_addr)			&& (ipmb_addr1->lun == ipmb_addr2->lun));	}	return 1;}int ipmi_validate_addr(struct ipmi_addr *addr, int len){	if (len < sizeof(struct ipmi_system_interface_addr)) {		return -EINVAL;	}	if (addr->addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE) {		if (addr->channel != IPMI_BMC_CHANNEL)			return -EINVAL;		return 0;	}	if ((addr->channel == IPMI_BMC_CHANNEL)	    || (addr->channel >= IPMI_NUM_CHANNELS)	    || (addr->channel < 0))		return -EINVAL;	if ((addr->addr_type == IPMI_IPMB_ADDR_TYPE)	    || (addr->addr_type == IPMI_IPMB_BROADCAST_ADDR_TYPE))	{		if (len < sizeof(struct ipmi_ipmb_addr)) {			return -EINVAL;		}		return 0;	}	return -EINVAL;}unsigned int ipmi_addr_length(int addr_type){	if (addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE)		return sizeof(struct ipmi_system_interface_addr);	if ((addr_type == IPMI_IPMB_ADDR_TYPE)	    || (addr_type == IPMI_IPMB_BROADCAST_ADDR_TYPE))	{		return sizeof(struct ipmi_ipmb_addr);	}	return 0;}static void deliver_response(struct ipmi_recv_msg *msg){    msg->user->handler->ipmi_recv_hndl(msg, msg->user->handler_data);}/* Find the next sequence number not being used and add the given   message with the given timeout to the sequence table.  This must be   called with the interface's seq_lock held. */static int intf_next_seq(ipmi_smi_t           intf,			 struct ipmi_recv_msg *recv_msg,			 unsigned long        timeout,			 int                  retries,			 unsigned char        *seq,			 long                 *seqid){	int          rv = 0;	unsigned int i;	for (i=intf->curr_seq;	     (i+1)%IPMI_IPMB_NUM_SEQ != intf->curr_seq;	     i=(i+1)%IPMI_IPMB_NUM_SEQ)	{		if (! intf->seq_table[i].inuse)			break;	}	if (! intf->seq_table[i].inuse) {		intf->seq_table[i].recv_msg = recv_msg;		/* Start with the maximum timeout, when the send response		   comes in we will start the real timer. */		intf->seq_table[i].timeout = MAX_MSG_TIMEOUT;		intf->seq_table[i].orig_timeout = timeout;		intf->seq_table[i].retries_left = retries;		intf->seq_table[i].inuse = 1;		intf->seq_table[i].seqid = NEXT_SEQID(intf->seq_table[i].seqid);		*seq = i;		*seqid = intf->seq_table[i].seqid;		intf->curr_seq = (i+1)%IPMI_IPMB_NUM_SEQ;	} else {		rv = -EAGAIN;	}		return rv;}/* Return the receive message for the given sequence number and   release the sequence number so it can be reused.  Some other data   is passed in to be sure the message matches up correctly (to help   guard against message coming in after their timeout and the   sequence number being reused). */static int intf_find_seq(ipmi_smi_t           intf,			 unsigned char        seq,			 short                channel,			 unsigned char        cmd,			 unsigned char        netfn,			 struct ipmi_addr     *addr,			 struct ipmi_recv_msg **recv_msg){	int           rv = -ENODEV;	unsigned long flags;	if (seq >= IPMI_IPMB_NUM_SEQ)		return -EINVAL;	spin_lock_irqsave(&(intf->seq_lock), flags);	if (intf->seq_table[seq].inuse) {		struct ipmi_recv_msg *msg = intf->seq_table[seq].recv_msg;		if ((msg->addr.channel == channel)		    && (msg->msg.cmd == cmd)		    && (msg->msg.netfn == netfn)		    && (ipmi_addr_equal(addr, &(msg->addr))))		{			*recv_msg = msg;			intf->seq_table[seq].inuse = 0;			rv = 0;		}	}	spin_unlock_irqrestore(&(intf->seq_lock), flags);	return rv;}/* Start the timer for a specific sequence table entry. */static int intf_start_seq_timer(ipmi_smi_t           intf,				long                 msgid){	int           rv = -ENODEV;	unsigned long flags;	unsigned char seq;	unsigned long seqid;	GET_SEQ_FROM_MSGID(msgid, seq, seqid);	spin_lock_irqsave(&(intf->seq_lock), flags);	/* We do this verification because the user can be deleted           while a message is outstanding. */	if ((intf->seq_table[seq].inuse)	    && (intf->seq_table[seq].seqid == seqid))	{		struct seq_table *ent = &(intf->seq_table[seq]);		ent->timeout = ent->orig_timeout;	}	spin_unlock_irqrestore(&(intf->seq_lock), flags);	return rv;}int ipmi_create_user(unsigned int          if_num,		     struct ipmi_user_hndl *handler,		     void                  *handler_data,		     ipmi_user_t           *user){	unsigned long flags;	ipmi_user_t   new_user;	int           rv = 0;	/* There is no module usecount here, because it's not           required.  Since this can only be used by and called from           other modules, they will implicitly use this module, and           thus this can't be removed unless the other modules are           removed. */	if (handler == NULL)		return -EINVAL;	/* Make sure the driver is actually initialized, this handles	   problems with initialization order. */	if (!initialized) {		rv = ipmi_init_msghandler();		if (rv)			return rv;		/* The init code doesn't return an error if it was turned		   off, but it won't initialize.  Check that. */		if (!initialized)			return -ENODEV;	}	new_user = kmalloc(sizeof(*new_user), GFP_KERNEL);	if (! new_user)		return -ENOMEM;	down_read(&interfaces_sem);	if ((if_num > MAX_IPMI_INTERFACES) || ipmi_interfaces[if_num] == NULL)	{		rv = -EINVAL;		goto out_unlock;	}	new_user->handler = handler;	new_user->handler_data = handler_data;	new_user->intf = ipmi_interfaces[if_num];	new_user->gets_events = 0;	rv = new_user->intf->handlers->new_user(new_user->intf->send_info);	if (rv)		goto out_unlock;	write_lock_irqsave(&(new_user->intf->users_lock), flags);	list_add_tail(&(new_user->link), &(new_user->intf->users));	write_unlock_irqrestore(&(new_user->intf->users_lock), flags); out_unlock:		if (rv) {		kfree(new_user);	} else {		*user = new_user;	}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -