Path: blob/master/drivers/char/ipmi/ipmi_devintf.c
15111 views
/*1* ipmi_devintf.c2*3* Linux device interface for the IPMI message handler.4*5* Author: MontaVista Software, Inc.6* Corey Minyard <[email protected]>7* [email protected]8*9* Copyright 2002 MontaVista Software Inc.10*11* This program is free software; you can redistribute it and/or modify it12* under the terms of the GNU General Public License as published by the13* Free Software Foundation; either version 2 of the License, or (at your14* option) any later version.15*16*17* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED18* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF19* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.20* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,21* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,22* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS23* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND24* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR25* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE26* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.27*28* You should have received a copy of the GNU General Public License along29* with this program; if not, write to the Free Software Foundation, Inc.,30* 675 Mass Ave, Cambridge, MA 02139, USA.31*/3233#include <linux/module.h>34#include <linux/moduleparam.h>35#include <linux/errno.h>36#include <asm/system.h>37#include <linux/poll.h>38#include <linux/sched.h>39#include <linux/spinlock.h>40#include <linux/slab.h>41#include <linux/ipmi.h>42#include <linux/mutex.h>43#include <linux/init.h>44#include <linux/device.h>45#include <linux/compat.h>4647struct ipmi_file_private48{49ipmi_user_t user;50spinlock_t recv_msg_lock;51struct list_head recv_msgs;52struct file *file;53struct fasync_struct *fasync_queue;54wait_queue_head_t wait;55struct mutex recv_mutex;56int default_retries;57unsigned int default_retry_time_ms;58};5960static DEFINE_MUTEX(ipmi_mutex);61static void file_receive_handler(struct ipmi_recv_msg *msg,62void *handler_data)63{64struct ipmi_file_private *priv = handler_data;65int was_empty;66unsigned long flags;6768spin_lock_irqsave(&(priv->recv_msg_lock), flags);6970was_empty = list_empty(&(priv->recv_msgs));71list_add_tail(&(msg->link), &(priv->recv_msgs));7273if (was_empty) {74wake_up_interruptible(&priv->wait);75kill_fasync(&priv->fasync_queue, SIGIO, POLL_IN);76}7778spin_unlock_irqrestore(&(priv->recv_msg_lock), flags);79}8081static unsigned int ipmi_poll(struct file *file, poll_table *wait)82{83struct ipmi_file_private *priv = file->private_data;84unsigned int mask = 0;85unsigned long flags;8687poll_wait(file, &priv->wait, wait);8889spin_lock_irqsave(&priv->recv_msg_lock, flags);9091if (!list_empty(&(priv->recv_msgs)))92mask |= (POLLIN | POLLRDNORM);9394spin_unlock_irqrestore(&priv->recv_msg_lock, flags);9596return mask;97}9899static int ipmi_fasync(int fd, struct file *file, int on)100{101struct ipmi_file_private *priv = file->private_data;102int result;103104mutex_lock(&ipmi_mutex); /* could race against open() otherwise */105result = fasync_helper(fd, file, on, &priv->fasync_queue);106mutex_unlock(&ipmi_mutex);107108return (result);109}110111static struct ipmi_user_hndl ipmi_hndlrs =112{113.ipmi_recv_hndl = file_receive_handler,114};115116static int ipmi_open(struct inode *inode, struct file *file)117{118int if_num = iminor(inode);119int rv;120struct ipmi_file_private *priv;121122123priv = kmalloc(sizeof(*priv), GFP_KERNEL);124if (!priv)125return -ENOMEM;126127mutex_lock(&ipmi_mutex);128priv->file = file;129130rv = ipmi_create_user(if_num,131&ipmi_hndlrs,132priv,133&(priv->user));134if (rv) {135kfree(priv);136goto out;137}138139file->private_data = priv;140141spin_lock_init(&(priv->recv_msg_lock));142INIT_LIST_HEAD(&(priv->recv_msgs));143init_waitqueue_head(&priv->wait);144priv->fasync_queue = NULL;145mutex_init(&priv->recv_mutex);146147/* Use the low-level defaults. */148priv->default_retries = -1;149priv->default_retry_time_ms = 0;150151out:152mutex_unlock(&ipmi_mutex);153return rv;154}155156static int ipmi_release(struct inode *inode, struct file *file)157{158struct ipmi_file_private *priv = file->private_data;159int rv;160161rv = ipmi_destroy_user(priv->user);162if (rv)163return rv;164165/* FIXME - free the messages in the list. */166kfree(priv);167168return 0;169}170171static int handle_send_req(ipmi_user_t user,172struct ipmi_req *req,173int retries,174unsigned int retry_time_ms)175{176int rv;177struct ipmi_addr addr;178struct kernel_ipmi_msg msg;179180if (req->addr_len > sizeof(struct ipmi_addr))181return -EINVAL;182183if (copy_from_user(&addr, req->addr, req->addr_len))184return -EFAULT;185186msg.netfn = req->msg.netfn;187msg.cmd = req->msg.cmd;188msg.data_len = req->msg.data_len;189msg.data = kmalloc(IPMI_MAX_MSG_LENGTH, GFP_KERNEL);190if (!msg.data)191return -ENOMEM;192193/* From here out we cannot return, we must jump to "out" for194error exits to free msgdata. */195196rv = ipmi_validate_addr(&addr, req->addr_len);197if (rv)198goto out;199200if (req->msg.data != NULL) {201if (req->msg.data_len > IPMI_MAX_MSG_LENGTH) {202rv = -EMSGSIZE;203goto out;204}205206if (copy_from_user(msg.data,207req->msg.data,208req->msg.data_len))209{210rv = -EFAULT;211goto out;212}213} else {214msg.data_len = 0;215}216217rv = ipmi_request_settime(user,218&addr,219req->msgid,220&msg,221NULL,2220,223retries,224retry_time_ms);225out:226kfree(msg.data);227return rv;228}229230static int ipmi_ioctl(struct file *file,231unsigned int cmd,232unsigned long data)233{234int rv = -EINVAL;235struct ipmi_file_private *priv = file->private_data;236void __user *arg = (void __user *)data;237238switch (cmd)239{240case IPMICTL_SEND_COMMAND:241{242struct ipmi_req req;243244if (copy_from_user(&req, arg, sizeof(req))) {245rv = -EFAULT;246break;247}248249rv = handle_send_req(priv->user,250&req,251priv->default_retries,252priv->default_retry_time_ms);253break;254}255256case IPMICTL_SEND_COMMAND_SETTIME:257{258struct ipmi_req_settime req;259260if (copy_from_user(&req, arg, sizeof(req))) {261rv = -EFAULT;262break;263}264265rv = handle_send_req(priv->user,266&req.req,267req.retries,268req.retry_time_ms);269break;270}271272case IPMICTL_RECEIVE_MSG:273case IPMICTL_RECEIVE_MSG_TRUNC:274{275struct ipmi_recv rsp;276int addr_len;277struct list_head *entry;278struct ipmi_recv_msg *msg;279unsigned long flags;280281282rv = 0;283if (copy_from_user(&rsp, arg, sizeof(rsp))) {284rv = -EFAULT;285break;286}287288/* We claim a mutex because we don't want two289users getting something from the queue at a time.290Since we have to release the spinlock before we can291copy the data to the user, it's possible another292user will grab something from the queue, too. Then293the messages might get out of order if something294fails and the message gets put back onto the295queue. This mutex prevents that problem. */296mutex_lock(&priv->recv_mutex);297298/* Grab the message off the list. */299spin_lock_irqsave(&(priv->recv_msg_lock), flags);300if (list_empty(&(priv->recv_msgs))) {301spin_unlock_irqrestore(&(priv->recv_msg_lock), flags);302rv = -EAGAIN;303goto recv_err;304}305entry = priv->recv_msgs.next;306msg = list_entry(entry, struct ipmi_recv_msg, link);307list_del(entry);308spin_unlock_irqrestore(&(priv->recv_msg_lock), flags);309310addr_len = ipmi_addr_length(msg->addr.addr_type);311if (rsp.addr_len < addr_len)312{313rv = -EINVAL;314goto recv_putback_on_err;315}316317if (copy_to_user(rsp.addr, &(msg->addr), addr_len)) {318rv = -EFAULT;319goto recv_putback_on_err;320}321rsp.addr_len = addr_len;322323rsp.recv_type = msg->recv_type;324rsp.msgid = msg->msgid;325rsp.msg.netfn = msg->msg.netfn;326rsp.msg.cmd = msg->msg.cmd;327328if (msg->msg.data_len > 0) {329if (rsp.msg.data_len < msg->msg.data_len) {330rv = -EMSGSIZE;331if (cmd == IPMICTL_RECEIVE_MSG_TRUNC) {332msg->msg.data_len = rsp.msg.data_len;333} else {334goto recv_putback_on_err;335}336}337338if (copy_to_user(rsp.msg.data,339msg->msg.data,340msg->msg.data_len))341{342rv = -EFAULT;343goto recv_putback_on_err;344}345rsp.msg.data_len = msg->msg.data_len;346} else {347rsp.msg.data_len = 0;348}349350if (copy_to_user(arg, &rsp, sizeof(rsp))) {351rv = -EFAULT;352goto recv_putback_on_err;353}354355mutex_unlock(&priv->recv_mutex);356ipmi_free_recv_msg(msg);357break;358359recv_putback_on_err:360/* If we got an error, put the message back onto361the head of the queue. */362spin_lock_irqsave(&(priv->recv_msg_lock), flags);363list_add(entry, &(priv->recv_msgs));364spin_unlock_irqrestore(&(priv->recv_msg_lock), flags);365mutex_unlock(&priv->recv_mutex);366break;367368recv_err:369mutex_unlock(&priv->recv_mutex);370break;371}372373case IPMICTL_REGISTER_FOR_CMD:374{375struct ipmi_cmdspec val;376377if (copy_from_user(&val, arg, sizeof(val))) {378rv = -EFAULT;379break;380}381382rv = ipmi_register_for_cmd(priv->user, val.netfn, val.cmd,383IPMI_CHAN_ALL);384break;385}386387case IPMICTL_UNREGISTER_FOR_CMD:388{389struct ipmi_cmdspec val;390391if (copy_from_user(&val, arg, sizeof(val))) {392rv = -EFAULT;393break;394}395396rv = ipmi_unregister_for_cmd(priv->user, val.netfn, val.cmd,397IPMI_CHAN_ALL);398break;399}400401case IPMICTL_REGISTER_FOR_CMD_CHANS:402{403struct ipmi_cmdspec_chans val;404405if (copy_from_user(&val, arg, sizeof(val))) {406rv = -EFAULT;407break;408}409410rv = ipmi_register_for_cmd(priv->user, val.netfn, val.cmd,411val.chans);412break;413}414415case IPMICTL_UNREGISTER_FOR_CMD_CHANS:416{417struct ipmi_cmdspec_chans val;418419if (copy_from_user(&val, arg, sizeof(val))) {420rv = -EFAULT;421break;422}423424rv = ipmi_unregister_for_cmd(priv->user, val.netfn, val.cmd,425val.chans);426break;427}428429case IPMICTL_SET_GETS_EVENTS_CMD:430{431int val;432433if (copy_from_user(&val, arg, sizeof(val))) {434rv = -EFAULT;435break;436}437438rv = ipmi_set_gets_events(priv->user, val);439break;440}441442/* The next four are legacy, not per-channel. */443case IPMICTL_SET_MY_ADDRESS_CMD:444{445unsigned int val;446447if (copy_from_user(&val, arg, sizeof(val))) {448rv = -EFAULT;449break;450}451452rv = ipmi_set_my_address(priv->user, 0, val);453break;454}455456case IPMICTL_GET_MY_ADDRESS_CMD:457{458unsigned int val;459unsigned char rval;460461rv = ipmi_get_my_address(priv->user, 0, &rval);462if (rv)463break;464465val = rval;466467if (copy_to_user(arg, &val, sizeof(val))) {468rv = -EFAULT;469break;470}471break;472}473474case IPMICTL_SET_MY_LUN_CMD:475{476unsigned int val;477478if (copy_from_user(&val, arg, sizeof(val))) {479rv = -EFAULT;480break;481}482483rv = ipmi_set_my_LUN(priv->user, 0, val);484break;485}486487case IPMICTL_GET_MY_LUN_CMD:488{489unsigned int val;490unsigned char rval;491492rv = ipmi_get_my_LUN(priv->user, 0, &rval);493if (rv)494break;495496val = rval;497498if (copy_to_user(arg, &val, sizeof(val))) {499rv = -EFAULT;500break;501}502break;503}504505case IPMICTL_SET_MY_CHANNEL_ADDRESS_CMD:506{507struct ipmi_channel_lun_address_set val;508509if (copy_from_user(&val, arg, sizeof(val))) {510rv = -EFAULT;511break;512}513514return ipmi_set_my_address(priv->user, val.channel, val.value);515break;516}517518case IPMICTL_GET_MY_CHANNEL_ADDRESS_CMD:519{520struct ipmi_channel_lun_address_set val;521522if (copy_from_user(&val, arg, sizeof(val))) {523rv = -EFAULT;524break;525}526527rv = ipmi_get_my_address(priv->user, val.channel, &val.value);528if (rv)529break;530531if (copy_to_user(arg, &val, sizeof(val))) {532rv = -EFAULT;533break;534}535break;536}537538case IPMICTL_SET_MY_CHANNEL_LUN_CMD:539{540struct ipmi_channel_lun_address_set val;541542if (copy_from_user(&val, arg, sizeof(val))) {543rv = -EFAULT;544break;545}546547rv = ipmi_set_my_LUN(priv->user, val.channel, val.value);548break;549}550551case IPMICTL_GET_MY_CHANNEL_LUN_CMD:552{553struct ipmi_channel_lun_address_set val;554555if (copy_from_user(&val, arg, sizeof(val))) {556rv = -EFAULT;557break;558}559560rv = ipmi_get_my_LUN(priv->user, val.channel, &val.value);561if (rv)562break;563564if (copy_to_user(arg, &val, sizeof(val))) {565rv = -EFAULT;566break;567}568break;569}570571case IPMICTL_SET_TIMING_PARMS_CMD:572{573struct ipmi_timing_parms parms;574575if (copy_from_user(&parms, arg, sizeof(parms))) {576rv = -EFAULT;577break;578}579580priv->default_retries = parms.retries;581priv->default_retry_time_ms = parms.retry_time_ms;582rv = 0;583break;584}585586case IPMICTL_GET_TIMING_PARMS_CMD:587{588struct ipmi_timing_parms parms;589590parms.retries = priv->default_retries;591parms.retry_time_ms = priv->default_retry_time_ms;592593if (copy_to_user(arg, &parms, sizeof(parms))) {594rv = -EFAULT;595break;596}597598rv = 0;599break;600}601602case IPMICTL_GET_MAINTENANCE_MODE_CMD:603{604int mode;605606mode = ipmi_get_maintenance_mode(priv->user);607if (copy_to_user(arg, &mode, sizeof(mode))) {608rv = -EFAULT;609break;610}611rv = 0;612break;613}614615case IPMICTL_SET_MAINTENANCE_MODE_CMD:616{617int mode;618619if (copy_from_user(&mode, arg, sizeof(mode))) {620rv = -EFAULT;621break;622}623rv = ipmi_set_maintenance_mode(priv->user, mode);624break;625}626}627628return rv;629}630631/*632* Note: it doesn't make sense to take the BKL here but633* not in compat_ipmi_ioctl. -arnd634*/635static long ipmi_unlocked_ioctl(struct file *file,636unsigned int cmd,637unsigned long data)638{639int ret;640641mutex_lock(&ipmi_mutex);642ret = ipmi_ioctl(file, cmd, data);643mutex_unlock(&ipmi_mutex);644645return ret;646}647648#ifdef CONFIG_COMPAT649650/*651* The following code contains code for supporting 32-bit compatible652* ioctls on 64-bit kernels. This allows running 32-bit apps on the653* 64-bit kernel654*/655#define COMPAT_IPMICTL_SEND_COMMAND \656_IOR(IPMI_IOC_MAGIC, 13, struct compat_ipmi_req)657#define COMPAT_IPMICTL_SEND_COMMAND_SETTIME \658_IOR(IPMI_IOC_MAGIC, 21, struct compat_ipmi_req_settime)659#define COMPAT_IPMICTL_RECEIVE_MSG \660_IOWR(IPMI_IOC_MAGIC, 12, struct compat_ipmi_recv)661#define COMPAT_IPMICTL_RECEIVE_MSG_TRUNC \662_IOWR(IPMI_IOC_MAGIC, 11, struct compat_ipmi_recv)663664struct compat_ipmi_msg {665u8 netfn;666u8 cmd;667u16 data_len;668compat_uptr_t data;669};670671struct compat_ipmi_req {672compat_uptr_t addr;673compat_uint_t addr_len;674compat_long_t msgid;675struct compat_ipmi_msg msg;676};677678struct compat_ipmi_recv {679compat_int_t recv_type;680compat_uptr_t addr;681compat_uint_t addr_len;682compat_long_t msgid;683struct compat_ipmi_msg msg;684};685686struct compat_ipmi_req_settime {687struct compat_ipmi_req req;688compat_int_t retries;689compat_uint_t retry_time_ms;690};691692/*693* Define some helper functions for copying IPMI data694*/695static long get_compat_ipmi_msg(struct ipmi_msg *p64,696struct compat_ipmi_msg __user *p32)697{698compat_uptr_t tmp;699700if (!access_ok(VERIFY_READ, p32, sizeof(*p32)) ||701__get_user(p64->netfn, &p32->netfn) ||702__get_user(p64->cmd, &p32->cmd) ||703__get_user(p64->data_len, &p32->data_len) ||704__get_user(tmp, &p32->data))705return -EFAULT;706p64->data = compat_ptr(tmp);707return 0;708}709710static long put_compat_ipmi_msg(struct ipmi_msg *p64,711struct compat_ipmi_msg __user *p32)712{713if (!access_ok(VERIFY_WRITE, p32, sizeof(*p32)) ||714__put_user(p64->netfn, &p32->netfn) ||715__put_user(p64->cmd, &p32->cmd) ||716__put_user(p64->data_len, &p32->data_len))717return -EFAULT;718return 0;719}720721static long get_compat_ipmi_req(struct ipmi_req *p64,722struct compat_ipmi_req __user *p32)723{724725compat_uptr_t tmp;726727if (!access_ok(VERIFY_READ, p32, sizeof(*p32)) ||728__get_user(tmp, &p32->addr) ||729__get_user(p64->addr_len, &p32->addr_len) ||730__get_user(p64->msgid, &p32->msgid) ||731get_compat_ipmi_msg(&p64->msg, &p32->msg))732return -EFAULT;733p64->addr = compat_ptr(tmp);734return 0;735}736737static long get_compat_ipmi_req_settime(struct ipmi_req_settime *p64,738struct compat_ipmi_req_settime __user *p32)739{740if (!access_ok(VERIFY_READ, p32, sizeof(*p32)) ||741get_compat_ipmi_req(&p64->req, &p32->req) ||742__get_user(p64->retries, &p32->retries) ||743__get_user(p64->retry_time_ms, &p32->retry_time_ms))744return -EFAULT;745return 0;746}747748static long get_compat_ipmi_recv(struct ipmi_recv *p64,749struct compat_ipmi_recv __user *p32)750{751compat_uptr_t tmp;752753if (!access_ok(VERIFY_READ, p32, sizeof(*p32)) ||754__get_user(p64->recv_type, &p32->recv_type) ||755__get_user(tmp, &p32->addr) ||756__get_user(p64->addr_len, &p32->addr_len) ||757__get_user(p64->msgid, &p32->msgid) ||758get_compat_ipmi_msg(&p64->msg, &p32->msg))759return -EFAULT;760p64->addr = compat_ptr(tmp);761return 0;762}763764static long put_compat_ipmi_recv(struct ipmi_recv *p64,765struct compat_ipmi_recv __user *p32)766{767if (!access_ok(VERIFY_WRITE, p32, sizeof(*p32)) ||768__put_user(p64->recv_type, &p32->recv_type) ||769__put_user(p64->addr_len, &p32->addr_len) ||770__put_user(p64->msgid, &p32->msgid) ||771put_compat_ipmi_msg(&p64->msg, &p32->msg))772return -EFAULT;773return 0;774}775776/*777* Handle compatibility ioctls778*/779static long compat_ipmi_ioctl(struct file *filep, unsigned int cmd,780unsigned long arg)781{782int rc;783struct ipmi_file_private *priv = filep->private_data;784785switch(cmd) {786case COMPAT_IPMICTL_SEND_COMMAND:787{788struct ipmi_req rp;789790if (get_compat_ipmi_req(&rp, compat_ptr(arg)))791return -EFAULT;792793return handle_send_req(priv->user, &rp,794priv->default_retries,795priv->default_retry_time_ms);796}797case COMPAT_IPMICTL_SEND_COMMAND_SETTIME:798{799struct ipmi_req_settime sp;800801if (get_compat_ipmi_req_settime(&sp, compat_ptr(arg)))802return -EFAULT;803804return handle_send_req(priv->user, &sp.req,805sp.retries, sp.retry_time_ms);806}807case COMPAT_IPMICTL_RECEIVE_MSG:808case COMPAT_IPMICTL_RECEIVE_MSG_TRUNC:809{810struct ipmi_recv __user *precv64;811struct ipmi_recv recv64;812813if (get_compat_ipmi_recv(&recv64, compat_ptr(arg)))814return -EFAULT;815816precv64 = compat_alloc_user_space(sizeof(recv64));817if (copy_to_user(precv64, &recv64, sizeof(recv64)))818return -EFAULT;819820rc = ipmi_ioctl(filep,821((cmd == COMPAT_IPMICTL_RECEIVE_MSG)822? IPMICTL_RECEIVE_MSG823: IPMICTL_RECEIVE_MSG_TRUNC),824(unsigned long) precv64);825if (rc != 0)826return rc;827828if (copy_from_user(&recv64, precv64, sizeof(recv64)))829return -EFAULT;830831if (put_compat_ipmi_recv(&recv64, compat_ptr(arg)))832return -EFAULT;833834return rc;835}836default:837return ipmi_ioctl(filep, cmd, arg);838}839}840#endif841842static const struct file_operations ipmi_fops = {843.owner = THIS_MODULE,844.unlocked_ioctl = ipmi_unlocked_ioctl,845#ifdef CONFIG_COMPAT846.compat_ioctl = compat_ipmi_ioctl,847#endif848.open = ipmi_open,849.release = ipmi_release,850.fasync = ipmi_fasync,851.poll = ipmi_poll,852.llseek = noop_llseek,853};854855#define DEVICE_NAME "ipmidev"856857static int ipmi_major;858module_param(ipmi_major, int, 0);859MODULE_PARM_DESC(ipmi_major, "Sets the major number of the IPMI device. By"860" default, or if you set it to zero, it will choose the next"861" available device. Setting it to -1 will disable the"862" interface. Other values will set the major device number"863" to that value.");864865/* Keep track of the devices that are registered. */866struct ipmi_reg_list {867dev_t dev;868struct list_head link;869};870static LIST_HEAD(reg_list);871static DEFINE_MUTEX(reg_list_mutex);872873static struct class *ipmi_class;874875static void ipmi_new_smi(int if_num, struct device *device)876{877dev_t dev = MKDEV(ipmi_major, if_num);878struct ipmi_reg_list *entry;879880entry = kmalloc(sizeof(*entry), GFP_KERNEL);881if (!entry) {882printk(KERN_ERR "ipmi_devintf: Unable to create the"883" ipmi class device link\n");884return;885}886entry->dev = dev;887888mutex_lock(®_list_mutex);889device_create(ipmi_class, device, dev, NULL, "ipmi%d", if_num);890list_add(&entry->link, ®_list);891mutex_unlock(®_list_mutex);892}893894static void ipmi_smi_gone(int if_num)895{896dev_t dev = MKDEV(ipmi_major, if_num);897struct ipmi_reg_list *entry;898899mutex_lock(®_list_mutex);900list_for_each_entry(entry, ®_list, link) {901if (entry->dev == dev) {902list_del(&entry->link);903kfree(entry);904break;905}906}907device_destroy(ipmi_class, dev);908mutex_unlock(®_list_mutex);909}910911static struct ipmi_smi_watcher smi_watcher =912{913.owner = THIS_MODULE,914.new_smi = ipmi_new_smi,915.smi_gone = ipmi_smi_gone,916};917918static int __init init_ipmi_devintf(void)919{920int rv;921922if (ipmi_major < 0)923return -EINVAL;924925printk(KERN_INFO "ipmi device interface\n");926927ipmi_class = class_create(THIS_MODULE, "ipmi");928if (IS_ERR(ipmi_class)) {929printk(KERN_ERR "ipmi: can't register device class\n");930return PTR_ERR(ipmi_class);931}932933rv = register_chrdev(ipmi_major, DEVICE_NAME, &ipmi_fops);934if (rv < 0) {935class_destroy(ipmi_class);936printk(KERN_ERR "ipmi: can't get major %d\n", ipmi_major);937return rv;938}939940if (ipmi_major == 0) {941ipmi_major = rv;942}943944rv = ipmi_smi_watcher_register(&smi_watcher);945if (rv) {946unregister_chrdev(ipmi_major, DEVICE_NAME);947class_destroy(ipmi_class);948printk(KERN_WARNING "ipmi: can't register smi watcher\n");949return rv;950}951952return 0;953}954module_init(init_ipmi_devintf);955956static void __exit cleanup_ipmi(void)957{958struct ipmi_reg_list *entry, *entry2;959mutex_lock(®_list_mutex);960list_for_each_entry_safe(entry, entry2, ®_list, link) {961list_del(&entry->link);962device_destroy(ipmi_class, entry->dev);963kfree(entry);964}965mutex_unlock(®_list_mutex);966class_destroy(ipmi_class);967ipmi_smi_watcher_unregister(&smi_watcher);968unregister_chrdev(ipmi_major, DEVICE_NAME);969}970module_exit(cleanup_ipmi);971972MODULE_LICENSE("GPL");973MODULE_AUTHOR("Corey Minyard <[email protected]>");974MODULE_DESCRIPTION("Linux device interface for the IPMI message handler.");975MODULE_ALIAS("platform:ipmi_si");976977978