linux 2.6.16.38 w/ vs2.0.3-rc1
[linux-2.6.git] / drivers / char / ipmi / ipmi_devintf.c
index 8d1a08b..7c0684d 100644 (file)
@@ -44,8 +44,8 @@
 #include <linux/ipmi.h>
 #include <asm/semaphore.h>
 #include <linux/init.h>
-
-#define IPMI_DEVINTF_VERSION "v32"
+#include <linux/device.h>
+#include <linux/compat.h>
 
 struct ipmi_file_private
 {
@@ -409,6 +409,7 @@ static int ipmi_ioctl(struct inode  *inode,
                break;
        }
 
+       /* The next four are legacy, not per-channel. */
        case IPMICTL_SET_MY_ADDRESS_CMD:
        {
                unsigned int val;
@@ -418,22 +419,25 @@ static int ipmi_ioctl(struct inode  *inode,
                        break;
                }
 
-               ipmi_set_my_address(priv->user, val);
-               rv = 0;
+               rv = ipmi_set_my_address(priv->user, 0, val);
                break;
        }
 
        case IPMICTL_GET_MY_ADDRESS_CMD:
        {
-               unsigned int val;
+               unsigned int  val;
+               unsigned char rval;
+
+               rv = ipmi_get_my_address(priv->user, 0, &rval);
+               if (rv)
+                       break;
 
-               val = ipmi_get_my_address(priv->user);
+               val = rval;
 
                if (copy_to_user(arg, &val, sizeof(val))) {
                        rv = -EFAULT;
                        break;
                }
-               rv = 0;
                break;
        }
 
@@ -446,24 +450,94 @@ static int ipmi_ioctl(struct inode  *inode,
                        break;
                }
 
-               ipmi_set_my_LUN(priv->user, val);
-               rv = 0;
+               rv = ipmi_set_my_LUN(priv->user, 0, val);
                break;
        }
 
        case IPMICTL_GET_MY_LUN_CMD:
        {
-               unsigned int val;
+               unsigned int  val;
+               unsigned char rval;
+
+               rv = ipmi_get_my_LUN(priv->user, 0, &rval);
+               if (rv)
+                       break;
 
-               val = ipmi_get_my_LUN(priv->user);
+               val = rval;
 
                if (copy_to_user(arg, &val, sizeof(val))) {
                        rv = -EFAULT;
                        break;
                }
-               rv = 0;
                break;
        }
+
+       case IPMICTL_SET_MY_CHANNEL_ADDRESS_CMD:
+       {
+               struct ipmi_channel_lun_address_set val;
+
+               if (copy_from_user(&val, arg, sizeof(val))) {
+                       rv = -EFAULT;
+                       break;
+               }
+
+               return ipmi_set_my_address(priv->user, val.channel, val.value);
+               break;
+       }
+
+       case IPMICTL_GET_MY_CHANNEL_ADDRESS_CMD:
+       {
+               struct ipmi_channel_lun_address_set val;
+
+               if (copy_from_user(&val, arg, sizeof(val))) {
+                       rv = -EFAULT;
+                       break;
+               }
+
+               rv = ipmi_get_my_address(priv->user, val.channel, &val.value);
+               if (rv)
+                       break;
+
+               if (copy_to_user(arg, &val, sizeof(val))) {
+                       rv = -EFAULT;
+                       break;
+               }
+               break;
+       }
+
+       case IPMICTL_SET_MY_CHANNEL_LUN_CMD:
+       {
+               struct ipmi_channel_lun_address_set val;
+
+               if (copy_from_user(&val, arg, sizeof(val))) {
+                       rv = -EFAULT;
+                       break;
+               }
+
+               rv = ipmi_set_my_LUN(priv->user, val.channel, val.value);
+               break;
+       }
+
+       case IPMICTL_GET_MY_CHANNEL_LUN_CMD:
+       {
+               struct ipmi_channel_lun_address_set val;
+
+               if (copy_from_user(&val, arg, sizeof(val))) {
+                       rv = -EFAULT;
+                       break;
+               }
+
+               rv = ipmi_get_my_LUN(priv->user, val.channel, &val.value);
+               if (rv)
+                       break;
+
+               if (copy_to_user(arg, &val, sizeof(val))) {
+                       rv = -EFAULT;
+                       break;
+               }
+               break;
+       }
+
        case IPMICTL_SET_TIMING_PARMS_CMD:
        {
                struct ipmi_timing_parms parms;
@@ -499,10 +573,206 @@ static int ipmi_ioctl(struct inode  *inode,
        return rv;
 }
 
+#ifdef CONFIG_COMPAT
+
+/*
+ * The following code contains code for supporting 32-bit compatible
+ * ioctls on 64-bit kernels.  This allows running 32-bit apps on the
+ * 64-bit kernel
+ */
+#define COMPAT_IPMICTL_SEND_COMMAND    \
+       _IOR(IPMI_IOC_MAGIC, 13, struct compat_ipmi_req)
+#define COMPAT_IPMICTL_SEND_COMMAND_SETTIME    \
+       _IOR(IPMI_IOC_MAGIC, 21, struct compat_ipmi_req_settime)
+#define COMPAT_IPMICTL_RECEIVE_MSG     \
+       _IOWR(IPMI_IOC_MAGIC, 12, struct compat_ipmi_recv)
+#define COMPAT_IPMICTL_RECEIVE_MSG_TRUNC       \
+       _IOWR(IPMI_IOC_MAGIC, 11, struct compat_ipmi_recv)
+
+struct compat_ipmi_msg {
+       u8              netfn;
+       u8              cmd;
+       u16             data_len;
+       compat_uptr_t   data;
+};
+
+struct compat_ipmi_req {
+       compat_uptr_t           addr;
+       compat_uint_t           addr_len;
+       compat_long_t           msgid;
+       struct compat_ipmi_msg  msg;
+};
+
+struct compat_ipmi_recv {
+       compat_int_t            recv_type;
+       compat_uptr_t           addr;
+       compat_uint_t           addr_len;
+       compat_long_t           msgid;
+       struct compat_ipmi_msg  msg;
+};
+
+struct compat_ipmi_req_settime {
+       struct compat_ipmi_req  req;
+       compat_int_t            retries;
+       compat_uint_t           retry_time_ms;
+};
+
+/*
+ * Define some helper functions for copying IPMI data
+ */
+static long get_compat_ipmi_msg(struct ipmi_msg *p64,
+                               struct compat_ipmi_msg __user *p32)
+{
+       compat_uptr_t tmp;
+
+       if (!access_ok(VERIFY_READ, p32, sizeof(*p32)) ||
+                       __get_user(p64->netfn, &p32->netfn) ||
+                       __get_user(p64->cmd, &p32->cmd) ||
+                       __get_user(p64->data_len, &p32->data_len) ||
+                       __get_user(tmp, &p32->data))
+               return -EFAULT;
+       p64->data = compat_ptr(tmp);
+       return 0;
+}
+
+static long put_compat_ipmi_msg(struct ipmi_msg *p64,
+                               struct compat_ipmi_msg __user *p32)
+{
+       if (!access_ok(VERIFY_WRITE, p32, sizeof(*p32)) ||
+                       __put_user(p64->netfn, &p32->netfn) ||
+                       __put_user(p64->cmd, &p32->cmd) ||
+                       __put_user(p64->data_len, &p32->data_len))
+               return -EFAULT;
+       return 0;
+}
+
+static long get_compat_ipmi_req(struct ipmi_req *p64,
+                               struct compat_ipmi_req __user *p32)
+{
+
+       compat_uptr_t   tmp;
+
+       if (!access_ok(VERIFY_READ, p32, sizeof(*p32)) ||
+                       __get_user(tmp, &p32->addr) ||
+                       __get_user(p64->addr_len, &p32->addr_len) ||
+                       __get_user(p64->msgid, &p32->msgid) ||
+                       get_compat_ipmi_msg(&p64->msg, &p32->msg))
+               return -EFAULT;
+       p64->addr = compat_ptr(tmp);
+       return 0;
+}
+
+static long get_compat_ipmi_req_settime(struct ipmi_req_settime *p64,
+               struct compat_ipmi_req_settime __user *p32)
+{
+       if (!access_ok(VERIFY_READ, p32, sizeof(*p32)) ||
+                       get_compat_ipmi_req(&p64->req, &p32->req) ||
+                       __get_user(p64->retries, &p32->retries) ||
+                       __get_user(p64->retry_time_ms, &p32->retry_time_ms))
+               return -EFAULT;
+       return 0;
+}
+
+static long get_compat_ipmi_recv(struct ipmi_recv *p64,
+                                struct compat_ipmi_recv __user *p32)
+{
+       compat_uptr_t tmp;
+
+       if (!access_ok(VERIFY_READ, p32, sizeof(*p32)) ||
+                       __get_user(p64->recv_type, &p32->recv_type) ||
+                       __get_user(tmp, &p32->addr) ||
+                       __get_user(p64->addr_len, &p32->addr_len) ||
+                       __get_user(p64->msgid, &p32->msgid) ||
+                       get_compat_ipmi_msg(&p64->msg, &p32->msg))
+               return -EFAULT;
+       p64->addr = compat_ptr(tmp);
+       return 0;
+}
+
+static long put_compat_ipmi_recv(struct ipmi_recv *p64,
+                                struct compat_ipmi_recv __user *p32)
+{
+       if (!access_ok(VERIFY_WRITE, p32, sizeof(*p32)) ||
+                       __put_user(p64->recv_type, &p32->recv_type) ||
+                       __put_user(p64->addr_len, &p32->addr_len) ||
+                       __put_user(p64->msgid, &p32->msgid) ||
+                       put_compat_ipmi_msg(&p64->msg, &p32->msg))
+               return -EFAULT;
+       return 0;
+}
+
+/*
+ * Handle compatibility ioctls
+ */
+static long compat_ipmi_ioctl(struct file *filep, unsigned int cmd,
+                             unsigned long arg)
+{
+       int rc;
+       struct ipmi_file_private *priv = filep->private_data;
+
+       switch(cmd) {
+       case COMPAT_IPMICTL_SEND_COMMAND:
+       {
+               struct ipmi_req rp;
+
+               if (get_compat_ipmi_req(&rp, compat_ptr(arg)))
+                       return -EFAULT;
+
+               return handle_send_req(priv->user, &rp,
+                               priv->default_retries,
+                               priv->default_retry_time_ms);
+       }
+       case COMPAT_IPMICTL_SEND_COMMAND_SETTIME:
+       {
+               struct ipmi_req_settime sp;
+
+               if (get_compat_ipmi_req_settime(&sp, compat_ptr(arg)))
+                       return -EFAULT;
+
+               return handle_send_req(priv->user, &sp.req,
+                               sp.retries, sp.retry_time_ms);
+       }
+       case COMPAT_IPMICTL_RECEIVE_MSG:
+       case COMPAT_IPMICTL_RECEIVE_MSG_TRUNC:
+       {
+               struct ipmi_recv   __user *precv64;
+               struct ipmi_recv   recv64;
+
+               if (get_compat_ipmi_recv(&recv64, compat_ptr(arg)))
+                       return -EFAULT;
+
+               precv64 = compat_alloc_user_space(sizeof(recv64));
+               if (copy_to_user(precv64, &recv64, sizeof(recv64)))
+                       return -EFAULT;
+
+               rc = ipmi_ioctl(filep->f_dentry->d_inode, filep,
+                               ((cmd == COMPAT_IPMICTL_RECEIVE_MSG)
+                                ? IPMICTL_RECEIVE_MSG
+                                : IPMICTL_RECEIVE_MSG_TRUNC),
+                               (unsigned long) precv64);
+               if (rc != 0)
+                       return rc;
+
+               if (copy_from_user(&recv64, precv64, sizeof(recv64)))
+                       return -EFAULT;
+
+               if (put_compat_ipmi_recv(&recv64, compat_ptr(arg)))
+                       return -EFAULT;
+
+               return rc;
+       }
+       default:
+               return ipmi_ioctl(filep->f_dentry->d_inode, filep, cmd, arg);
+       }
+}
+#endif
 
 static struct file_operations ipmi_fops = {
        .owner          = THIS_MODULE,
        .ioctl          = ipmi_ioctl,
+#ifdef CONFIG_COMPAT
+       .compat_ioctl   = compat_ipmi_ioctl,
+#endif
        .open           = ipmi_open,
        .release        = ipmi_release,
        .fasync         = ipmi_fasync,
@@ -519,15 +789,21 @@ MODULE_PARM_DESC(ipmi_major, "Sets the major number of the IPMI device.  By"
                 " interface.  Other values will set the major device number"
                 " to that value.");
 
+static struct class *ipmi_class;
+
 static void ipmi_new_smi(int if_num)
 {
-       devfs_mk_cdev(MKDEV(ipmi_major, if_num),
-                     S_IFCHR | S_IRUSR | S_IWUSR,
+       dev_t dev = MKDEV(ipmi_major, if_num);
+
+       devfs_mk_cdev(dev, S_IFCHR | S_IRUSR | S_IWUSR,
                      "ipmidev/%d", if_num);
+
+       class_device_create(ipmi_class, NULL, dev, NULL, "ipmi%d", if_num);
 }
 
 static void ipmi_smi_gone(int if_num)
 {
+       class_device_destroy(ipmi_class, MKDEV(ipmi_major, if_num));
        devfs_remove("ipmidev/%d", if_num);
 }
 
@@ -545,11 +821,17 @@ static __init int init_ipmi_devintf(void)
        if (ipmi_major < 0)
                return -EINVAL;
 
-       printk(KERN_INFO "ipmi device interface version "
-              IPMI_DEVINTF_VERSION "\n");
+       printk(KERN_INFO "ipmi device interface\n");
+
+       ipmi_class = class_create(THIS_MODULE, "ipmi");
+       if (IS_ERR(ipmi_class)) {
+               printk(KERN_ERR "ipmi: can't register device class\n");
+               return PTR_ERR(ipmi_class);
+       }
 
        rv = register_chrdev(ipmi_major, DEVICE_NAME, &ipmi_fops);
        if (rv < 0) {
+               class_destroy(ipmi_class);
                printk(KERN_ERR "ipmi: can't get major %d\n", ipmi_major);
                return rv;
        }
@@ -563,6 +845,7 @@ static __init int init_ipmi_devintf(void)
        rv = ipmi_smi_watcher_register(&smi_watcher);
        if (rv) {
                unregister_chrdev(ipmi_major, DEVICE_NAME);
+               class_destroy(ipmi_class);
                printk(KERN_WARNING "ipmi: can't register smi watcher\n");
                return rv;
        }
@@ -573,6 +856,7 @@ module_init(init_ipmi_devintf);
 
 static __exit void cleanup_ipmi(void)
 {
+       class_destroy(ipmi_class);
        ipmi_smi_watcher_unregister(&smi_watcher);
        devfs_remove(DEVICE_NAME);
        unregister_chrdev(ipmi_major, DEVICE_NAME);
@@ -580,3 +864,5 @@ static __exit void cleanup_ipmi(void)
 module_exit(cleanup_ipmi);
 
 MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Corey Minyard <minyard@mvista.com>");
+MODULE_DESCRIPTION("Linux device interface for the IPMI message handler.");