linux-old/drivers/char/ipmi/ipmi_devintf.c
<<
>>
Prefs
   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/config.h>
  35#include <linux/module.h>
  36#include <linux/errno.h>
  37#include <asm/system.h>
  38#include <linux/sched.h>
  39#include <linux/poll.h>
  40#include <linux/spinlock.h>
  41#include <linux/slab.h>
  42#include <linux/devfs_fs_kernel.h>
  43#include <linux/ipmi.h>
  44#include <asm/semaphore.h>
  45#include <linux/init.h>
  46
  47struct ipmi_file_private
  48{
  49        ipmi_user_t          user;
  50        spinlock_t           recv_msg_lock;
  51        struct list_head     recv_msgs;
  52        struct file          *file;
  53        struct fasync_struct *fasync_queue;
  54        wait_queue_head_t    wait;
  55        struct semaphore     recv_sem;
  56};
  57
  58static void file_receive_handler(struct ipmi_recv_msg *msg,
  59                                 void                 *handler_data)
  60{
  61        struct ipmi_file_private *priv = handler_data;
  62        int                      was_empty;
  63        unsigned long            flags;
  64
  65        spin_lock_irqsave(&(priv->recv_msg_lock), flags);
  66
  67        was_empty = list_empty(&(priv->recv_msgs));
  68        list_add_tail(&(msg->link), &(priv->recv_msgs));
  69
  70        if (was_empty) {
  71                wake_up_interruptible(&priv->wait);
  72                kill_fasync(&priv->fasync_queue, SIGIO, POLL_IN);
  73        }
  74
  75        spin_unlock_irqrestore(&(priv->recv_msg_lock), flags);
  76}
  77
  78static unsigned int ipmi_poll(struct file *file, poll_table *wait)
  79{
  80        struct ipmi_file_private *priv = file->private_data;
  81        unsigned int             mask = 0;
  82        unsigned long            flags;
  83
  84        poll_wait(file, &priv->wait, wait);
  85
  86        spin_lock_irqsave(&priv->recv_msg_lock, flags);
  87
  88        if (! list_empty(&(priv->recv_msgs)))
  89                mask |= (POLLIN | POLLRDNORM);
  90
  91        spin_unlock_irqrestore(&priv->recv_msg_lock, flags);
  92
  93        return mask;
  94}
  95
  96static int ipmi_fasync(int fd, struct file *file, int on)
  97{
  98        struct ipmi_file_private *priv = file->private_data;
  99        int                      result;
 100
 101        result = fasync_helper(fd, file, on, &priv->fasync_queue);
 102
 103        return (result);
 104}
 105
 106static struct ipmi_user_hndl ipmi_hndlrs =
 107{
 108        ipmi_recv_hndl : file_receive_handler
 109};
 110
 111static int ipmi_open(struct inode *inode, struct file *file)
 112{
 113        int                      if_num = minor(inode->i_rdev);
 114        int                      rv;
 115        struct ipmi_file_private *priv;
 116
 117
 118        priv = kmalloc(sizeof(*priv), GFP_KERNEL);
 119        if (!priv)
 120                return -ENOMEM;
 121
 122        priv->file = file;
 123
 124        rv = ipmi_create_user(if_num,
 125                              &ipmi_hndlrs,
 126                              priv,
 127                              &(priv->user));
 128        if (rv) {
 129                kfree(priv);
 130                return rv;
 131        }
 132
 133        file->private_data = priv;
 134
 135        spin_lock_init(&(priv->recv_msg_lock));
 136        INIT_LIST_HEAD(&(priv->recv_msgs));
 137        init_waitqueue_head(&priv->wait);
 138        priv->fasync_queue = NULL;
 139        sema_init(&(priv->recv_sem), 1);
 140
 141        return 0;
 142}
 143
 144static int ipmi_release(struct inode *inode, struct file *file)
 145{
 146        struct ipmi_file_private *priv = file->private_data;
 147        int                      rv;
 148
 149        rv = ipmi_destroy_user(priv->user);
 150        if (rv)
 151                return rv;
 152
 153        ipmi_fasync (-1, file, 0);
 154
 155        /* FIXME - free the messages in the list. */
 156        kfree(priv);
 157
 158        return 0;
 159}
 160
 161static int ipmi_ioctl(struct inode  *inode,
 162                      struct file   *file,
 163                      unsigned int  cmd,
 164                      unsigned long data)
 165{
 166        int                      rv = -EINVAL;
 167        struct ipmi_file_private *priv = file->private_data;
 168
 169        switch (cmd) 
 170        {
 171        case IPMICTL_SEND_COMMAND:
 172        {
 173                struct ipmi_req    req;
 174                struct ipmi_addr   addr;
 175                unsigned char msgdata[IPMI_MAX_MSG_LENGTH];
 176
 177                if (copy_from_user(&req, (void *) data, sizeof(req))) {
 178                        rv = -EFAULT;
 179                        break;
 180                }
 181
 182                if (req.addr_len > sizeof(struct ipmi_addr))
 183                {
 184                        rv = -EINVAL;
 185                        break;
 186                }
 187
 188                if (copy_from_user(&addr, req.addr, req.addr_len)) {
 189                        rv = -EFAULT;
 190                        break;
 191                }
 192
 193                rv = ipmi_validate_addr(&addr, req.addr_len);
 194                if (rv)
 195                        break;
 196
 197                if (req.msg.data != NULL) {
 198                        if (req.msg.data_len > IPMI_MAX_MSG_LENGTH) {
 199                                rv = -EMSGSIZE;
 200                                break;
 201                        }
 202
 203                        if (copy_from_user(&msgdata,
 204                                           req.msg.data,
 205                                           req.msg.data_len))
 206                        {
 207                                rv = -EFAULT;
 208                                break;
 209                        }
 210                } else {
 211                        req.msg.data_len = 0;
 212                }
 213
 214                req.msg.data = msgdata;
 215
 216                rv = ipmi_request(priv->user,
 217                                  &addr,
 218                                  req.msgid,
 219                                  &(req.msg),
 220                                  0);
 221                break;
 222        }
 223
 224        case IPMICTL_RECEIVE_MSG:
 225        case IPMICTL_RECEIVE_MSG_TRUNC:
 226        {
 227                struct ipmi_recv      rsp;
 228                int              addr_len;
 229                struct list_head *entry;
 230                struct ipmi_recv_msg  *msg;
 231                unsigned long    flags;
 232                
 233
 234                rv = 0;
 235                if (copy_from_user(&rsp, (void *) data, sizeof(rsp))) {
 236                        rv = -EFAULT;
 237                        break;
 238                }
 239
 240                /* We claim a semaphore because we don't want two
 241                   users getting something from the queue at a time.
 242                   Since we have to release the spinlock before we can
 243                   copy the data to the user, it's possible another
 244                   user will grab something from the queue, too.  Then
 245                   the messages might get out of order if something
 246                   fails and the message gets put back onto the
 247                   queue.  This semaphore prevents that problem. */
 248                down(&(priv->recv_sem));
 249
 250                /* Grab the message off the list. */
 251                spin_lock_irqsave(&(priv->recv_msg_lock), flags);
 252                if (list_empty(&(priv->recv_msgs))) {
 253                        spin_unlock_irqrestore(&(priv->recv_msg_lock), flags);
 254                        rv = -EAGAIN;
 255                        goto recv_err;
 256                }
 257                entry = priv->recv_msgs.next;
 258                msg = list_entry(entry, struct ipmi_recv_msg, link);
 259                list_del(entry);
 260                spin_unlock_irqrestore(&(priv->recv_msg_lock), flags);
 261
 262                addr_len = ipmi_addr_length(msg->addr.addr_type);
 263                if (rsp.addr_len < addr_len)
 264                {
 265                        rv = -EINVAL;
 266                        goto recv_putback_on_err;
 267                }
 268
 269                if (copy_to_user(rsp.addr, &(msg->addr), addr_len)) {
 270                        rv = -EFAULT;
 271                        goto recv_putback_on_err;
 272                }
 273                rsp.addr_len = addr_len;
 274
 275                rsp.recv_type = msg->recv_type;
 276                rsp.msgid = msg->msgid;
 277                rsp.msg.netfn = msg->msg.netfn;
 278                rsp.msg.cmd = msg->msg.cmd;
 279
 280                if (msg->msg.data_len > 0) {
 281                        if (rsp.msg.data_len < msg->msg.data_len) {
 282                                rv = -EMSGSIZE;
 283                                if (cmd == IPMICTL_RECEIVE_MSG_TRUNC) {
 284                                        msg->msg.data_len = rsp.msg.data_len;
 285                                } else {
 286                                        goto recv_putback_on_err;
 287                                }
 288                        }
 289
 290                        if (copy_to_user(rsp.msg.data,
 291                                         msg->msg.data,
 292                                         msg->msg.data_len))
 293                        {
 294                                rv = -EFAULT;
 295                                goto recv_putback_on_err;
 296                        }
 297                        rsp.msg.data_len = msg->msg.data_len;
 298                } else {
 299                        rsp.msg.data_len = 0;
 300                }
 301
 302                if (copy_to_user((void *) data, &rsp, sizeof(rsp))) {
 303                        rv = -EFAULT;
 304                        goto recv_putback_on_err;
 305                }
 306
 307                up(&(priv->recv_sem));
 308                ipmi_free_recv_msg(msg);
 309                break;
 310
 311        recv_putback_on_err:
 312                /* If we got an error, put the message back onto
 313                   the head of the queue. */
 314                spin_lock_irqsave(&(priv->recv_msg_lock), flags);
 315                list_add(entry, &(priv->recv_msgs));
 316                spin_unlock_irqrestore(&(priv->recv_msg_lock), flags);
 317                up(&(priv->recv_sem));
 318                break;
 319
 320        recv_err:
 321                up(&(priv->recv_sem));
 322                break;
 323        }
 324
 325        case IPMICTL_REGISTER_FOR_CMD:
 326        {
 327                struct ipmi_cmdspec val;
 328
 329                if (copy_from_user(&val, (void *) data, sizeof(val))) {
 330                        rv = -EFAULT;
 331                        break;
 332                }
 333
 334                rv = ipmi_register_for_cmd(priv->user, val.netfn, val.cmd);
 335                break;
 336        }
 337
 338        case IPMICTL_UNREGISTER_FOR_CMD:
 339        {
 340                struct ipmi_cmdspec   val;
 341
 342                if (copy_from_user(&val, (void *) data, sizeof(val))) {
 343                        rv = -EFAULT;
 344                        break;
 345                }
 346
 347                rv = ipmi_unregister_for_cmd(priv->user, val.netfn, val.cmd);
 348                break;
 349        }
 350
 351        case IPMICTL_SET_GETS_EVENTS_CMD:
 352        {
 353                int val;
 354
 355                if (copy_from_user(&val, (void *) data, sizeof(val))) {
 356                        rv = -EFAULT;
 357                        break;
 358                }
 359
 360                rv = ipmi_set_gets_events(priv->user, val);
 361                break;
 362        }
 363
 364        case IPMICTL_SET_MY_ADDRESS_CMD:
 365        {
 366                unsigned int val;
 367
 368                if (copy_from_user(&val, (void *) data, sizeof(val))) {
 369                        rv = -EFAULT;
 370                        break;
 371                }
 372
 373                ipmi_set_my_address(priv->user, val);
 374                rv = 0;
 375                break;
 376        }
 377
 378        case IPMICTL_GET_MY_ADDRESS_CMD:
 379        {
 380                unsigned int val;
 381
 382                val = ipmi_get_my_address(priv->user);
 383
 384                if (copy_to_user((void *) data, &val, sizeof(val))) {
 385                        rv = -EFAULT;
 386                        break;
 387                }
 388                rv = 0;
 389                break;
 390        }
 391
 392        case IPMICTL_SET_MY_LUN_CMD:
 393        {
 394                unsigned int val;
 395
 396                if (copy_from_user(&val, (void *) data, sizeof(val))) {
 397                        rv = -EFAULT;
 398                        break;
 399                }
 400
 401                ipmi_set_my_LUN(priv->user, val);
 402                rv = 0;
 403                break;
 404        }
 405
 406        case IPMICTL_GET_MY_LUN_CMD:
 407        {
 408                unsigned int val;
 409
 410                val = ipmi_get_my_LUN(priv->user);
 411
 412                if (copy_to_user((void *) data, &val, sizeof(val))) {
 413                        rv = -EFAULT;
 414                        break;
 415                }
 416                rv = 0;
 417                break;
 418        }
 419
 420        }
 421  
 422        return rv;
 423}
 424
 425
 426static struct file_operations ipmi_fops = {
 427        owner:   THIS_MODULE,
 428        ioctl:   ipmi_ioctl,
 429        open:    ipmi_open,
 430        release: ipmi_release,
 431        fasync:  ipmi_fasync,
 432        poll:    ipmi_poll
 433};
 434
 435#define DEVICE_NAME     "ipmidev"
 436
 437static int ipmi_major = 0;
 438MODULE_PARM(ipmi_major, "i");
 439
 440static devfs_handle_t devfs_handle;
 441
 442#define MAX_DEVICES 10
 443static devfs_handle_t handles[MAX_DEVICES];
 444
 445static void ipmi_new_smi(int if_num)
 446{
 447        char name[2];
 448
 449        if (if_num > MAX_DEVICES)
 450                return;
 451
 452        name[0] = if_num + '0';
 453        name[1] = '\0';
 454
 455        handles[if_num] = devfs_register(devfs_handle, name, DEVFS_FL_NONE,
 456                                         ipmi_major, if_num,
 457                                         S_IFCHR | S_IRUSR | S_IWUSR,
 458                                         &ipmi_fops, NULL);
 459}
 460
 461static void ipmi_smi_gone(int if_num)
 462{
 463        if (if_num > MAX_DEVICES)
 464                return;
 465
 466        devfs_unregister(handles[if_num]);
 467}
 468
 469static struct ipmi_smi_watcher smi_watcher =
 470{
 471        new_smi  : ipmi_new_smi,
 472        smi_gone : ipmi_smi_gone
 473};
 474
 475static __init int init_ipmi_devintf(void)
 476{
 477        int rv;
 478
 479        if (ipmi_major < 0)
 480                return -EINVAL;
 481
 482        rv = register_chrdev(ipmi_major, DEVICE_NAME, &ipmi_fops);
 483        if (rv < 0) {
 484                printk(KERN_ERR "ipmi: can't get major %d\n", ipmi_major);
 485                return rv;
 486        }
 487
 488        if (ipmi_major == 0) {
 489                ipmi_major = rv;
 490        }
 491
 492        devfs_handle = devfs_mk_dir(NULL, DEVICE_NAME, NULL);
 493
 494        rv = ipmi_smi_watcher_register(&smi_watcher);
 495        if (rv) {
 496                unregister_chrdev(ipmi_major, DEVICE_NAME);
 497                printk(KERN_WARNING "ipmi: can't register smi watcher");
 498                return rv;
 499        }
 500
 501        printk(KERN_INFO "ipmi: device interface at char major %d\n",
 502               ipmi_major);
 503
 504        return 0;
 505}
 506module_init(init_ipmi_devintf);
 507
 508static __exit void cleanup_ipmi(void)
 509{
 510        ipmi_smi_watcher_unregister(&smi_watcher);
 511        devfs_unregister(devfs_handle);
 512        unregister_chrdev(ipmi_major, DEVICE_NAME);
 513}
 514module_exit(cleanup_ipmi);
 515#ifndef MODULE
 516static __init int ipmi_setup (char *str)
 517{
 518        int x;
 519
 520        if (get_option (&str, &x)) {
 521                /* ipmi=x sets the major number to x. */
 522                ipmi_major = x;
 523        } else if (!strcmp(str, "off")) {
 524                ipmi_major = -1;
 525        }
 526
 527        return 1;
 528}
 529#endif
 530
 531__setup("ipmi=", ipmi_setup);
 532MODULE_LICENSE("GPL");
 533
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.