From: Corey Minyard <minyard@acm.org>

Add a socket interface to IPMI.  IPMI is sort of a network, so it makes some
sense to have a network interface to it.  Since the kernel is out of numbers
for socket interfaces, this also does some updates to increase the number
range.


---

 25-akpm/include/linux/net.h    |    2 
 25-akpm/include/linux/socket.h |    4 
 25-akpm/include/net/af_ipmi.h  |   59 +++
 25-akpm/net/Kconfig            |   11 
 25-akpm/net/Makefile           |    1 
 25-akpm/net/ipmi/Makefile      |    1 
 25-akpm/net/ipmi/af_ipmi.c     |  612 +++++++++++++++++++++++++++++++++++++++++
 7 files changed, 688 insertions(+), 2 deletions(-)

diff -puN include/linux/net.h~ipmi-socket-interface include/linux/net.h
--- 25/include/linux/net.h~ipmi-socket-interface	2004-03-30 20:44:11.716481152 -0800
+++ 25-akpm/include/linux/net.h	2004-03-30 20:44:11.722480240 -0800
@@ -25,7 +25,7 @@
 struct poll_table_struct;
 struct inode;
 
-#define NPROTO		32		/* should be enough for now..	*/
+#define NPROTO		64		/* should be enough for now..	*/
 
 #define SYS_SOCKET	1		/* sys_socket(2)		*/
 #define SYS_BIND	2		/* sys_bind(2)			*/
diff -puN include/linux/socket.h~ipmi-socket-interface include/linux/socket.h
--- 25/include/linux/socket.h~ipmi-socket-interface	2004-03-30 20:44:11.717481000 -0800
+++ 25-akpm/include/linux/socket.h	2004-03-30 20:44:11.723480088 -0800
@@ -178,7 +178,8 @@ struct ucred {
 #define AF_WANPIPE	25	/* Wanpipe API Sockets */
 #define AF_LLC		26	/* Linux LLC			*/
 #define AF_BLUETOOTH	31	/* Bluetooth sockets 		*/
-#define AF_MAX		32	/* For now.. */
+#define AF_IPMI		32	/* IPMI sockers 		*/
+#define AF_MAX		33	/* For now.. */
 
 /* Protocol families, same as address families. */
 #define PF_UNSPEC	AF_UNSPEC
@@ -210,6 +211,7 @@ struct ucred {
 #define PF_WANPIPE	AF_WANPIPE
 #define PF_LLC		AF_LLC
 #define PF_BLUETOOTH	AF_BLUETOOTH
+#define PF_IPMI		AF_IPMI
 #define PF_MAX		AF_MAX
 
 /* Maximum queue length specifiable by listen.  */
diff -puN /dev/null include/net/af_ipmi.h
--- /dev/null	2003-09-15 06:40:47.000000000 -0700
+++ 25-akpm/include/net/af_ipmi.h	2004-03-30 20:44:11.723480088 -0800
@@ -0,0 +1,59 @@
+/*
+ * IPMI Socket Glue
+ *
+ * Author:	Louis Zhuang <louis.zhuang@linux.intel.com>
+ * Copyright by Intel Corp., 2003
+ */
+#ifndef _NET_IPMI_H
+#define _NET_IPMI_H
+
+#include <linux/ipmi.h>
+
+/*
+ * This is ipmi address for socket
+ */
+struct sockaddr_ipmi {
+	sa_family_t      sipmi_family; /* AF_IPMI */
+	int              if_num; /* IPMI interface number */
+	struct ipmi_addr ipmi_addr;
+};
+#define SOCKADDR_IPMI_OVERHEAD (sizeof(struct sockaddr_ipmi) \
+				- sizeof(struct ipmi_addr))
+
+/* A msg_control item, this takes a 'struct ipmi_timing_parms' */
+#define IPMI_CMSG_TIMING_PARMS	0x01
+
+/*
+ * This is ipmi message for socket
+ */
+struct ipmi_sock_msg {
+	int                   recv_type;
+	long                  msgid;
+
+	unsigned char         netfn;
+	unsigned char         cmd;
+	int                   data_len;
+	unsigned char         data[0];
+};
+
+#define IPMI_MAX_SOCK_MSG_LENGTH (sizeof(struct ipmi_sock_msg)+IPMI_MAX_MSG_LENGTH)
+
+/* Register/unregister to receive specific commands.  Uses struct
+   ipmi_cmdspec from linux/ipmi.h */
+#define SIOCIPMIREGCMD		(SIOCPROTOPRIVATE + 0)
+#define SIOCIPMIUNREGCMD	(SIOCPROTOPRIVATE + 1)
+
+/* Register to receive events.  Takes an integer */
+#define SIOCIPMIGETEVENT	(SIOCPROTOPRIVATE + 2)
+
+/* Set the default timing parameters for the socket.  Takes a struct
+   ipmi_timing_parms from linux/ipmi.h */
+#define SIOCIPMISETTIMING	(SIOCPROTOPRIVATE + 3)
+#define SIOCIPMIGETTIMING	(SIOCPROTOPRIVATE + 4)
+
+/* Set/Get the IPMB address of the MC we are connected to, takes an
+   unsigned int. */
+#define SIOCIPMISETADDR		(SIOCPROTOPRIVATE + 5)
+#define SIOCIPMIGETADDR		(SIOCPROTOPRIVATE + 6)
+
+#endif/*_NET_IPMI_H*/
diff -puN /dev/null net/ipmi/af_ipmi.c
--- /dev/null	2003-09-15 06:40:47.000000000 -0700
+++ 25-akpm/net/ipmi/af_ipmi.c	2004-03-30 20:44:11.733478568 -0800
@@ -0,0 +1,612 @@
+/*
+ * IPMI Socket Glue
+ *
+ * Author:	Louis Zhuang <louis.zhuang@linux.intel.com>
+ * Copyright by Intel Corp., 2003
+ */
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+#include <linux/socket.h>
+#include <linux/fcntl.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <linux/in.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <asm/uaccess.h>
+#include <linux/skbuff.h>
+#include <linux/tcp.h>
+#include <net/sock.h>
+#include <linux/proc_fs.h>
+#include <linux/init.h>
+#include <linux/poll.h>
+#include <linux/smp_lock.h>
+#include <linux/mount.h>
+#include <linux/security.h>
+#include <linux/ipmi.h>
+#include <net/af_ipmi.h>
+
+#define IPMI_SOCKINTF_VERSION "v31"
+
+#ifdef CONFIG_DEBUG_KERNEL
+static int debug = 0;
+#define dbg(format, arg...)                                     \
+        do {                                                    \
+                if(debug)                                    \
+                        printk (KERN_DEBUG "%s: " format "\n",  \
+                                __FUNCTION__, ## arg);          \
+        } while(0)
+#else
+#define dbg(format, arg...)
+#endif /* CONFIG_DEBUG_KERNEL */
+
+#define err(format, arg...) \
+                printk(KERN_ERR "%s: " format "\n", \
+                       __FUNCTION__ , ## arg)
+#define info(format, arg...) \
+                printk(KERN_INFO "%s: " format "\n", \
+                       __FUNCTION__ , ## arg)
+#define warn(format, arg...) \
+                printk(KERN_WARNING "%s: " format "\n", \
+                       __FUNCTION__ , ## arg)
+#define trace(format, arg...) \
+                printk(KERN_INFO "%s(" format ")\n", \
+                       __FUNCTION__ , ## arg)
+
+struct ipmi_sock {
+	/* WARNING: sk has to be the first member */
+	struct sock sk;
+
+	ipmi_user_t user;
+	struct sockaddr_ipmi addr;
+	struct list_head msg_list;
+
+	wait_queue_head_t wait;
+	spinlock_t lock;
+
+	int          default_retries;
+	unsigned int default_retry_time_ms;
+};
+
+static kmem_cache_t *ipmi_sk_cachep = NULL;
+
+static atomic_t ipmi_nr_socks = ATOMIC_INIT(0);
+
+
+
+/*
+ * utility functions
+ */
+static inline struct ipmi_sock *to_ipmi_sock(struct sock *sk)
+{
+	return container_of(sk, struct ipmi_sock, sk);
+}
+
+static inline void ipmi_release_sock(struct sock *sk, int embrion)
+{
+	struct ipmi_sock *i = to_ipmi_sock(sk);
+	struct sk_buff   *skb;
+
+	if (i->user) {
+		ipmi_destroy_user(i->user);
+		i->user = NULL;
+	}
+
+	sock_orphan(&i->sk);
+	sk->sk_shutdown = SHUTDOWN_MASK;
+	sk->sk_state = TCP_CLOSE;
+
+	while((skb=skb_dequeue(&sk->sk_receive_queue))!=NULL)
+		kfree_skb(skb);
+
+	sock_put(sk);
+}
+
+static inline long ipmi_wait_for_queue(struct ipmi_sock *i, long timeo)
+{
+
+	DECLARE_WAITQUEUE(wait, current);
+
+	set_current_state(TASK_INTERRUPTIBLE);
+	add_wait_queue_exclusive(&i->wait, &wait);
+	timeo = schedule_timeout(timeo);
+	remove_wait_queue(&i->wait, &wait);
+	return timeo;
+}
+
+/*
+ * IPMI operation functions
+ */
+static void sock_receive_handler(struct ipmi_recv_msg *msg,
+				 void                 *handler_data)
+{
+	struct ipmi_sock *i = (struct ipmi_sock *)handler_data;
+	unsigned long    flags;
+
+	spin_lock_irqsave(&i->lock, flags);
+	list_add_tail(&msg->link, &i->msg_list);
+	spin_unlock_irqrestore(&i->lock, flags);
+
+	wake_up_interruptible(&i->wait);
+}
+
+/*
+ * protocol operation functions
+ */
+static int ipmi_release(struct socket *sock)
+{
+	struct sock *sk = sock->sk;
+
+	if (!sk)
+		return 0;
+
+	sock->sk=NULL;
+	ipmi_release_sock(sk, 0);
+	return 0;
+}
+
+static struct ipmi_user_hndl ipmi_hnd = {
+	.ipmi_recv_hndl = sock_receive_handler
+};
+
+static int ipmi_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
+{
+	struct ipmi_sock *i = to_ipmi_sock(sock->sk);
+	struct sockaddr_ipmi *addr = (struct sockaddr_ipmi *)uaddr;
+	int err = -EINVAL;
+
+	if (i->user != NULL) {
+		dbg("Cannot bind twice: %p", i->user);
+		return -EINVAL;
+	}
+
+	err = ipmi_create_user(addr->if_num, &ipmi_hnd, i, &i->user);
+	if (err) {
+		dbg("Cannot create user for the socket: %p", i->user);
+		return err;
+	}
+
+	memcpy(&i->addr, addr, sizeof(i->addr));
+	return 0;
+}
+
+static int ipmi_getname(struct socket *sock, struct sockaddr *uaddr, int *uaddr_len, int peer)
+{
+	struct ipmi_sock *i = to_ipmi_sock(sock->sk);
+	memcpy(uaddr, &i->addr, sizeof(i->addr));
+	return 0;
+}
+
+static unsigned int ipmi_poll(struct file * file, struct socket *sock, poll_table *wait)
+{
+	unsigned int     has_msg = 0;
+	struct ipmi_sock *i = to_ipmi_sock(sock->sk);
+	unsigned long    flags;
+
+	poll_wait(file, &i->wait, wait);
+	spin_lock_irqsave(&i->lock, flags);
+	if (!list_empty(&i->msg_list))
+		has_msg = 1;
+	spin_unlock_irqrestore(&i->lock, flags);
+
+	if (has_msg)
+		return POLLIN | POLLRDNORM;
+	return 0;
+}
+
+static int ipmi_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+	struct ipmi_sock    *i = to_ipmi_sock(sock->sk);
+	struct ipmi_cmdspec val;
+	int                 ival;
+	unsigned int        uival;
+	int                 err;
+
+	dbg("cmd=%#x, arg=%#lx", cmd, arg);
+	switch(cmd) {
+	case SIOCIPMIREGCMD:
+		err = copy_from_user((void *)&val, (void *)arg,
+				     sizeof(cmd));
+		if (err) {
+			err = -EFAULT;
+			break;
+		}
+
+		err = ipmi_register_for_cmd(i->user, val.netfn,
+					    val.cmd);
+		break;
+
+	case SIOCIPMIUNREGCMD:
+		err = copy_from_user((void *)&val, (void *)arg,
+				     sizeof(cmd));
+		if (err) {
+			err = -EFAULT;
+			break;
+		}
+
+		err = ipmi_unregister_for_cmd(i->user, val.netfn,
+					      val.cmd);
+		break;
+
+	case SIOCIPMIGETEVENT:
+		err = copy_from_user((void *)&ival, (void *)arg,
+				     sizeof(ival));
+		if (err) {
+			err = -EFAULT;
+			break;
+		}
+
+		err = ipmi_set_gets_events(i->user, ival);
+		break;
+
+	case SIOCIPMISETADDR:
+		err = copy_from_user((void *)&uival, (void *)arg,
+				     sizeof(uival));
+		if (err) {
+			err = -EFAULT;
+			break;
+		}
+
+		ipmi_set_my_address(i->user, uival);
+		break;
+
+	case SIOCIPMIGETADDR:
+		uival = ipmi_get_my_address(i->user);
+
+		if (copy_to_user((void *) arg, &uival, sizeof(uival))) {
+			err = -EFAULT;
+			break;
+		}
+		err = 0;
+		break;
+
+	case SIOCIPMISETTIMING:
+	{
+		struct ipmi_timing_parms parms;
+
+		if (copy_from_user(&parms, (void *) arg, sizeof(parms))) {
+			err = -EFAULT;
+			break;
+		}
+
+		i->default_retries = parms.retries;
+		i->default_retry_time_ms = parms.retry_time_ms;
+		err = 0;
+		break;
+	}
+
+	case SIOCIPMIGETTIMING:
+	{
+		struct ipmi_timing_parms parms;
+
+		parms.retries = i->default_retries;
+		parms.retry_time_ms = i->default_retry_time_ms;
+
+		if (copy_to_user((void *) arg, &parms, sizeof(parms))) {
+			err = -EFAULT;
+			break;
+		}
+
+		err = 0;
+		break;
+	}
+
+	default:
+		err = dev_ioctl(cmd, (void *)arg);
+		break;
+	}
+
+	return err;
+}
+
+static int ipmi_recvmsg(struct kiocb *iocb, struct socket *sock,
+			struct msghdr *msg, size_t size,
+			int rflags)
+{
+	struct ipmi_sock     *i = to_ipmi_sock(sock->sk);
+	long                 timeo;
+	struct ipmi_recv_msg *rcvmsg;
+	struct sockaddr_ipmi addr;
+	char                 buf[IPMI_MAX_SOCK_MSG_LENGTH];
+	struct ipmi_sock_msg *smsg = (struct ipmi_sock_msg *)buf;
+	int                  err;
+	unsigned long        flags;
+
+
+	timeo = sock_rcvtimeo(&i->sk, rflags & MSG_DONTWAIT);
+
+	while (1) {
+		spin_lock_irqsave(&i->lock, flags);
+		if (!list_empty(&i->msg_list))
+			break;
+		spin_unlock_irqrestore(&i->lock, flags);
+		if (!timeo) {
+			return -EAGAIN;
+		} else if (signal_pending (current)) {
+			dbg("Signal pending: %d", 1);
+			return -EINTR;
+		}
+
+		timeo = ipmi_wait_for_queue(i, timeo);
+	}
+
+	rcvmsg = list_entry(i->msg_list.next, struct ipmi_recv_msg, link);
+	list_del(&rcvmsg->link);
+	spin_unlock_irqrestore(&i->lock, flags);
+
+	memcpy(&addr.ipmi_addr, &rcvmsg->addr, sizeof(addr.ipmi_addr));
+	addr.if_num = i->addr.if_num;
+	addr.sipmi_family = i->addr.sipmi_family;
+	memcpy(msg->msg_name, &addr, sizeof(addr));
+	msg->msg_namelen = (SOCKADDR_IPMI_OVERHEAD
+			    + ipmi_addr_length(rcvmsg->addr.addr_type));
+
+	smsg->recv_type		= rcvmsg->recv_type;
+	smsg->msgid		= rcvmsg->msgid;
+	smsg->netfn		= rcvmsg->msg.netfn;
+	smsg->cmd		= rcvmsg->msg.cmd;
+	smsg->data_len		= rcvmsg->msg.data_len;
+	memcpy(smsg->data, rcvmsg->msg.data, smsg->data_len);
+
+	ipmi_free_recv_msg(rcvmsg);
+
+	err = memcpy_toiovec(msg->msg_iov, (void *)smsg,
+			     sizeof(struct ipmi_sock_msg) + smsg->data_len);
+	if (err) {
+		dbg("Cannot copy data to user: %p", i->user);
+		return err;
+	}
+
+	dbg("user=%p", i->user);
+	dbg("addr_type=%x, channel=%x",
+	    addr.ipmi_addr.addr_type, addr.ipmi_addr.channel);
+	dbg("netfn=%#02x, cmd=%#02x, data=%p, data_len=%x",
+	    smsg->netfn, smsg->cmd, smsg->data, smsg->data_len);
+
+	return (sizeof(struct ipmi_sock_msg) + smsg->data_len);
+}
+
+static int ipmi_sendmsg(struct kiocb *iocb, struct socket *sock,
+			struct msghdr *msg, size_t len)
+{
+	struct ipmi_sock         *i = to_ipmi_sock(sock->sk);
+	struct sockaddr_ipmi     *addr = (struct sockaddr_ipmi *)msg->msg_name;
+	struct ipmi_msg          imsg;
+	unsigned char            buf[IPMI_MAX_SOCK_MSG_LENGTH];
+	struct ipmi_sock_msg     *smsg = (struct ipmi_sock_msg *) buf;
+	int                      err;
+	struct ipmi_timing_parms tparms;
+	struct cmsghdr           *cmsg;
+
+	err = ipmi_validate_addr(&addr->ipmi_addr,
+				 msg->msg_namelen - SOCKADDR_IPMI_OVERHEAD);
+	if (err) {
+		dbg("Invalid IPMI address: %p", i->user);
+		goto err;
+	}
+
+	if (len > IPMI_MAX_SOCK_MSG_LENGTH) {
+		err = -EINVAL;
+		dbg("Message too long: %p", i->user);
+		goto err;
+	}
+
+	if (len < sizeof(struct ipmi_sock_msg)) {
+		err = -EINVAL;
+		dbg("Msg data too small for header: %p", i->user);
+		goto err;
+	}
+
+	err = memcpy_fromiovec((void *)smsg, msg->msg_iov, len);
+	if (err) {
+		dbg("Cannot copy data to kernel: %p", i->user);
+		goto err;
+	}
+
+	if (len < smsg->data_len+sizeof(struct ipmi_sock_msg)) {
+		err = -EINVAL;
+		dbg("Msg data is out of bound: %p", i->user);
+		goto err;
+	}
+
+	/* Set defaults. */
+	tparms.retries = i->default_retries;
+	tparms.retry_time_ms = i->default_retry_time_ms;
+
+	for (cmsg=CMSG_FIRSTHDR(msg);
+	     cmsg;
+	     cmsg = CMSG_NXTHDR(msg, cmsg))
+	{
+		if (cmsg->cmsg_len < sizeof(struct cmsghdr)) {
+			err = -EINVAL;
+			dbg("cmsg length too short: %p", i->user);
+			goto err;
+		}
+
+		if (cmsg->cmsg_level != SOL_SOCKET)
+			continue;
+
+		if (cmsg->cmsg_type == IPMI_CMSG_TIMING_PARMS) {
+			struct ipmi_timing_parms *pparms;
+
+			if (cmsg->cmsg_len != CMSG_LEN(sizeof(*pparms))) {
+				err = -EINVAL;
+				dbg("timing parms cmsg not right size: %p",
+				    i->user);
+				goto err;
+			}
+			pparms = (struct ipmi_timing_parms *) CMSG_DATA(cmsg);
+			tparms.retries = pparms->retries;
+			tparms.retry_time_ms = pparms->retry_time_ms;
+		}
+	}
+
+	imsg.netfn 	= smsg->netfn;
+	imsg.cmd	= smsg->cmd;
+	imsg.data 	= smsg->data;
+	imsg.data_len 	= smsg->data_len;
+
+	dbg("user=%p", i->user);
+	dbg("addr_type=%x, channel=%x",
+	    addr->ipmi_addr.addr_type, addr->ipmi_addr.channel);
+	dbg("netfn=%#02x, cmd=%#02x, data=%p, data_len=%x",
+	    imsg.netfn, imsg.cmd, imsg.data, imsg.data_len);
+	err = ipmi_request_settime(i->user, &addr->ipmi_addr,
+				   smsg->msgid, &imsg, NULL, 0,
+				   tparms.retries, tparms.retry_time_ms);
+	if (err) {
+		dbg("Cannot send message: %p", i->user);
+		goto err;
+	}
+
+err:
+	return err;
+}
+
+static struct proto_ops ipmi_ops = {
+	.family =	PF_IPMI,
+	.owner =	THIS_MODULE,
+	.release =	ipmi_release,
+	.bind =		ipmi_bind,
+	.connect =	sock_no_connect,
+	.socketpair =	sock_no_socketpair,
+	.accept =	sock_no_accept,
+	.getname =	ipmi_getname,
+	.poll =		ipmi_poll,
+	.ioctl =	ipmi_ioctl,
+	.listen =	sock_no_listen,
+	.shutdown =	sock_no_shutdown,
+	.setsockopt =	sock_no_setsockopt,
+	.getsockopt =	sock_no_getsockopt,
+	.sendmsg =	ipmi_sendmsg,
+	.recvmsg =	ipmi_recvmsg,
+	.mmap =		sock_no_mmap,
+	.sendpage =	sock_no_sendpage
+};
+
+
+static void ipmi_sock_destructor(struct sock *sk)
+{
+	skb_queue_purge(&sk->sk_receive_queue);
+
+	BUG_TRAP(!atomic_read(&sk->sk_wmem_alloc));
+	BUG_TRAP(sk_unhashed(sk));
+	BUG_TRAP(!sk->sk_socket);
+	if (!sock_flag(sk, SOCK_DEAD)) {
+		printk("Attempt to release alive ipmi socket: %p\n", sk);
+		return;
+	}
+
+	atomic_dec(&ipmi_nr_socks);
+}
+
+/*
+ * net protocol functions
+ */
+static struct ipmi_sock *ipmi_socket_create1(struct socket *sock)
+{
+	struct ipmi_sock *i;
+
+	if (atomic_read(&ipmi_nr_socks) >= 2*files_stat.max_files)
+		return NULL;
+
+	i = (struct ipmi_sock *)sk_alloc(PF_IPMI, GFP_KERNEL,
+					 sizeof(struct ipmi_sock), ipmi_sk_cachep);
+	if (!i) {
+		return NULL;
+	}
+
+
+	sock_init_data(sock, &i->sk);
+	sk_set_owner(&i->sk, THIS_MODULE);
+	i->sk.sk_destruct = ipmi_sock_destructor;
+	i->sk.sk_rcvtimeo = 5*HZ;
+	spin_lock_init(&i->lock);
+	INIT_LIST_HEAD(&i->msg_list);
+	init_waitqueue_head(&i->wait);
+
+	/* Set to use default values. */
+	i->default_retries = -1;
+	i->default_retry_time_ms = 0;
+
+	atomic_inc(&ipmi_nr_socks);
+	return i;
+}
+
+static int ipmi_socket_create(struct socket *sock, int protocol)
+{
+	if (!capable(CAP_NET_RAW))
+		return -EPERM;
+	if (protocol && protocol != PF_IPMI)
+		return -EPROTONOSUPPORT;
+
+	sock->state = SS_UNCONNECTED;
+
+	switch (sock->type) {
+	case SOCK_RAW:
+		sock->type=SOCK_DGRAM;
+	case SOCK_DGRAM:
+		sock->ops = &ipmi_ops;
+		break;
+	default:
+		return -EPROTONOSUPPORT;
+	}
+
+	return ipmi_socket_create1(sock)? 0 : -ENOMEM;
+}
+
+static struct net_proto_family ipmi_family_ops = {
+	.family = PF_IPMI,
+	.create = ipmi_socket_create,
+	.owner	= THIS_MODULE,
+};
+
+
+/*
+ * init/exit functions
+ */
+static int __init ipmi_socket_init(void)
+{
+
+	int err=0;
+
+	printk(KERN_INFO "ipmi socket interface version "
+	       IPMI_SOCKINTF_VERSION "\n");
+
+	ipmi_sk_cachep = kmem_cache_create("ipmi_sock",
+					   sizeof(struct ipmi_sock), 0,
+					   SLAB_HWCACHE_ALIGN, 0, 0);
+	if (!ipmi_sk_cachep) {
+		printk(KERN_CRIT "%s: Unable to create ipmi_sock SLAB cache!\n", __func__);
+		err = -ENOMEM;
+		goto out;
+	}
+
+	err = sock_register(&ipmi_family_ops);
+	if (err)
+		kmem_cache_destroy(ipmi_sk_cachep);
+out:
+	return err;
+}
+
+static void __exit ipmi_socket_exit(void)
+{
+	sock_unregister(PF_IPMI);
+	kmem_cache_destroy(ipmi_sk_cachep);
+}
+
+#ifdef CONFIG_DEBUG_KERNEL
+module_param(debug, int, 0);
+#endif
+module_init(ipmi_socket_init);
+module_exit(ipmi_socket_exit);
+
+MODULE_LICENSE("GPL");
diff -puN /dev/null net/ipmi/Makefile
--- /dev/null	2003-09-15 06:40:47.000000000 -0700
+++ 25-akpm/net/ipmi/Makefile	2004-03-30 20:44:11.733478568 -0800
@@ -0,0 +1 @@
+obj-$(CONFIG_IPMI_SOCKET) = af_ipmi.o
diff -puN net/Kconfig~ipmi-socket-interface net/Kconfig
--- 25/net/Kconfig~ipmi-socket-interface	2004-03-30 20:44:11.719480696 -0800
+++ 25-akpm/net/Kconfig	2004-03-30 20:44:11.734478416 -0800
@@ -71,6 +71,17 @@ config UNIX
 
 	  Say Y unless you know what you are doing.
 
+config IPMI_SOCKET
+	tristate "IPMI sockets"
+	depends on IPMI_HANDLER
+	---help---
+	  If you say Y here, you will include support for IPMI sockets;
+	  This way you don't have to use devices to access IPMI.  You
+	  must also enable the IPMI message handler and a low-level
+	  driver in the Character Drivers if you enable this.
+
+	  If unsure, say N.
+
 config NET_KEY
 	tristate "PF_KEY sockets"
 	select XFRM
diff -puN net/Makefile~ipmi-socket-interface net/Makefile
--- 25/net/Makefile~ipmi-socket-interface	2004-03-30 20:44:11.720480544 -0800
+++ 25-akpm/net/Makefile	2004-03-30 20:44:11.735478264 -0800
@@ -40,6 +40,7 @@ obj-$(CONFIG_DECNET)		+= decnet/
 obj-$(CONFIG_ECONET)		+= econet/
 obj-$(CONFIG_VLAN_8021Q)	+= 8021q/
 obj-$(CONFIG_IP_SCTP)		+= sctp/
+obj-$(CONFIG_IPMI_SOCKET)	+= ipmi/
 
 ifeq ($(CONFIG_NET),y)
 obj-$(CONFIG_SYSCTL)		+= sysctl_net.o

_