162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * ipmi_devintf.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Linux device interface for the IPMI message handler. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Author: MontaVista Software, Inc. 862306a36Sopenharmony_ci * Corey Minyard <minyard@mvista.com> 962306a36Sopenharmony_ci * source@mvista.com 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * Copyright 2002 MontaVista Software Inc. 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include <linux/moduleparam.h> 1662306a36Sopenharmony_ci#include <linux/errno.h> 1762306a36Sopenharmony_ci#include <linux/poll.h> 1862306a36Sopenharmony_ci#include <linux/sched.h> 1962306a36Sopenharmony_ci#include <linux/spinlock.h> 2062306a36Sopenharmony_ci#include <linux/slab.h> 2162306a36Sopenharmony_ci#include <linux/ipmi.h> 2262306a36Sopenharmony_ci#include <linux/mutex.h> 2362306a36Sopenharmony_ci#include <linux/init.h> 2462306a36Sopenharmony_ci#include <linux/device.h> 2562306a36Sopenharmony_ci#include <linux/compat.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistruct ipmi_file_private 2862306a36Sopenharmony_ci{ 2962306a36Sopenharmony_ci struct ipmi_user *user; 3062306a36Sopenharmony_ci spinlock_t recv_msg_lock; 3162306a36Sopenharmony_ci struct list_head recv_msgs; 3262306a36Sopenharmony_ci struct fasync_struct *fasync_queue; 3362306a36Sopenharmony_ci wait_queue_head_t wait; 3462306a36Sopenharmony_ci struct mutex recv_mutex; 3562306a36Sopenharmony_ci int default_retries; 3662306a36Sopenharmony_ci unsigned int default_retry_time_ms; 3762306a36Sopenharmony_ci}; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistatic void file_receive_handler(struct ipmi_recv_msg *msg, 4062306a36Sopenharmony_ci void *handler_data) 4162306a36Sopenharmony_ci{ 4262306a36Sopenharmony_ci struct ipmi_file_private *priv = handler_data; 4362306a36Sopenharmony_ci int was_empty; 4462306a36Sopenharmony_ci unsigned long flags; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci spin_lock_irqsave(&priv->recv_msg_lock, flags); 4762306a36Sopenharmony_ci was_empty = list_empty(&priv->recv_msgs); 4862306a36Sopenharmony_ci list_add_tail(&msg->link, &priv->recv_msgs); 4962306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->recv_msg_lock, flags); 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci if (was_empty) { 5262306a36Sopenharmony_ci wake_up_interruptible(&priv->wait); 5362306a36Sopenharmony_ci kill_fasync(&priv->fasync_queue, SIGIO, POLL_IN); 5462306a36Sopenharmony_ci } 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic __poll_t ipmi_poll(struct file *file, poll_table *wait) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci struct ipmi_file_private *priv = file->private_data; 6062306a36Sopenharmony_ci __poll_t mask = 0; 6162306a36Sopenharmony_ci unsigned long flags; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci poll_wait(file, &priv->wait, wait); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci spin_lock_irqsave(&priv->recv_msg_lock, flags); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci if (!list_empty(&priv->recv_msgs)) 6862306a36Sopenharmony_ci mask |= (EPOLLIN | EPOLLRDNORM); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->recv_msg_lock, flags); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci return mask; 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic int ipmi_fasync(int fd, struct file *file, int on) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci struct ipmi_file_private *priv = file->private_data; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci return fasync_helper(fd, file, on, &priv->fasync_queue); 8062306a36Sopenharmony_ci} 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic const struct ipmi_user_hndl ipmi_hndlrs = 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci .ipmi_recv_hndl = file_receive_handler, 8562306a36Sopenharmony_ci}; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic int ipmi_open(struct inode *inode, struct file *file) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci int if_num = iminor(inode); 9062306a36Sopenharmony_ci int rv; 9162306a36Sopenharmony_ci struct ipmi_file_private *priv; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci priv = kmalloc(sizeof(*priv), GFP_KERNEL); 9462306a36Sopenharmony_ci if (!priv) 9562306a36Sopenharmony_ci return -ENOMEM; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci rv = ipmi_create_user(if_num, 9862306a36Sopenharmony_ci &ipmi_hndlrs, 9962306a36Sopenharmony_ci priv, 10062306a36Sopenharmony_ci &priv->user); 10162306a36Sopenharmony_ci if (rv) { 10262306a36Sopenharmony_ci kfree(priv); 10362306a36Sopenharmony_ci goto out; 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci file->private_data = priv; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci spin_lock_init(&priv->recv_msg_lock); 10962306a36Sopenharmony_ci INIT_LIST_HEAD(&priv->recv_msgs); 11062306a36Sopenharmony_ci init_waitqueue_head(&priv->wait); 11162306a36Sopenharmony_ci priv->fasync_queue = NULL; 11262306a36Sopenharmony_ci mutex_init(&priv->recv_mutex); 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci /* Use the low-level defaults. */ 11562306a36Sopenharmony_ci priv->default_retries = -1; 11662306a36Sopenharmony_ci priv->default_retry_time_ms = 0; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ciout: 11962306a36Sopenharmony_ci return rv; 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic int ipmi_release(struct inode *inode, struct file *file) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci struct ipmi_file_private *priv = file->private_data; 12562306a36Sopenharmony_ci int rv; 12662306a36Sopenharmony_ci struct ipmi_recv_msg *msg, *next; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci rv = ipmi_destroy_user(priv->user); 12962306a36Sopenharmony_ci if (rv) 13062306a36Sopenharmony_ci return rv; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci list_for_each_entry_safe(msg, next, &priv->recv_msgs, link) 13362306a36Sopenharmony_ci ipmi_free_recv_msg(msg); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci kfree(priv); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci return 0; 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistatic int handle_send_req(struct ipmi_user *user, 14162306a36Sopenharmony_ci struct ipmi_req *req, 14262306a36Sopenharmony_ci int retries, 14362306a36Sopenharmony_ci unsigned int retry_time_ms) 14462306a36Sopenharmony_ci{ 14562306a36Sopenharmony_ci int rv; 14662306a36Sopenharmony_ci struct ipmi_addr addr; 14762306a36Sopenharmony_ci struct kernel_ipmi_msg msg; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci if (req->addr_len > sizeof(struct ipmi_addr)) 15062306a36Sopenharmony_ci return -EINVAL; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci if (copy_from_user(&addr, req->addr, req->addr_len)) 15362306a36Sopenharmony_ci return -EFAULT; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci msg.netfn = req->msg.netfn; 15662306a36Sopenharmony_ci msg.cmd = req->msg.cmd; 15762306a36Sopenharmony_ci msg.data_len = req->msg.data_len; 15862306a36Sopenharmony_ci msg.data = kmalloc(IPMI_MAX_MSG_LENGTH, GFP_KERNEL); 15962306a36Sopenharmony_ci if (!msg.data) 16062306a36Sopenharmony_ci return -ENOMEM; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci /* From here out we cannot return, we must jump to "out" for 16362306a36Sopenharmony_ci error exits to free msgdata. */ 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci rv = ipmi_validate_addr(&addr, req->addr_len); 16662306a36Sopenharmony_ci if (rv) 16762306a36Sopenharmony_ci goto out; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci if (req->msg.data != NULL) { 17062306a36Sopenharmony_ci if (req->msg.data_len > IPMI_MAX_MSG_LENGTH) { 17162306a36Sopenharmony_ci rv = -EMSGSIZE; 17262306a36Sopenharmony_ci goto out; 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci if (copy_from_user(msg.data, 17662306a36Sopenharmony_ci req->msg.data, 17762306a36Sopenharmony_ci req->msg.data_len)) { 17862306a36Sopenharmony_ci rv = -EFAULT; 17962306a36Sopenharmony_ci goto out; 18062306a36Sopenharmony_ci } 18162306a36Sopenharmony_ci } else { 18262306a36Sopenharmony_ci msg.data_len = 0; 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci rv = ipmi_request_settime(user, 18662306a36Sopenharmony_ci &addr, 18762306a36Sopenharmony_ci req->msgid, 18862306a36Sopenharmony_ci &msg, 18962306a36Sopenharmony_ci NULL, 19062306a36Sopenharmony_ci 0, 19162306a36Sopenharmony_ci retries, 19262306a36Sopenharmony_ci retry_time_ms); 19362306a36Sopenharmony_ci out: 19462306a36Sopenharmony_ci kfree(msg.data); 19562306a36Sopenharmony_ci return rv; 19662306a36Sopenharmony_ci} 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_cistatic int handle_recv(struct ipmi_file_private *priv, 19962306a36Sopenharmony_ci bool trunc, struct ipmi_recv *rsp, 20062306a36Sopenharmony_ci int (*copyout)(struct ipmi_recv *, void __user *), 20162306a36Sopenharmony_ci void __user *to) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci int addr_len; 20462306a36Sopenharmony_ci struct list_head *entry; 20562306a36Sopenharmony_ci struct ipmi_recv_msg *msg; 20662306a36Sopenharmony_ci unsigned long flags; 20762306a36Sopenharmony_ci int rv = 0, rv2 = 0; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci /* We claim a mutex because we don't want two 21062306a36Sopenharmony_ci users getting something from the queue at a time. 21162306a36Sopenharmony_ci Since we have to release the spinlock before we can 21262306a36Sopenharmony_ci copy the data to the user, it's possible another 21362306a36Sopenharmony_ci user will grab something from the queue, too. Then 21462306a36Sopenharmony_ci the messages might get out of order if something 21562306a36Sopenharmony_ci fails and the message gets put back onto the 21662306a36Sopenharmony_ci queue. This mutex prevents that problem. */ 21762306a36Sopenharmony_ci mutex_lock(&priv->recv_mutex); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci /* Grab the message off the list. */ 22062306a36Sopenharmony_ci spin_lock_irqsave(&priv->recv_msg_lock, flags); 22162306a36Sopenharmony_ci if (list_empty(&(priv->recv_msgs))) { 22262306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->recv_msg_lock, flags); 22362306a36Sopenharmony_ci rv = -EAGAIN; 22462306a36Sopenharmony_ci goto recv_err; 22562306a36Sopenharmony_ci } 22662306a36Sopenharmony_ci entry = priv->recv_msgs.next; 22762306a36Sopenharmony_ci msg = list_entry(entry, struct ipmi_recv_msg, link); 22862306a36Sopenharmony_ci list_del(entry); 22962306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->recv_msg_lock, flags); 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci addr_len = ipmi_addr_length(msg->addr.addr_type); 23262306a36Sopenharmony_ci if (rsp->addr_len < addr_len) { 23362306a36Sopenharmony_ci rv = -EINVAL; 23462306a36Sopenharmony_ci goto recv_putback_on_err; 23562306a36Sopenharmony_ci } 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci if (copy_to_user(rsp->addr, &msg->addr, addr_len)) { 23862306a36Sopenharmony_ci rv = -EFAULT; 23962306a36Sopenharmony_ci goto recv_putback_on_err; 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci rsp->addr_len = addr_len; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci rsp->recv_type = msg->recv_type; 24462306a36Sopenharmony_ci rsp->msgid = msg->msgid; 24562306a36Sopenharmony_ci rsp->msg.netfn = msg->msg.netfn; 24662306a36Sopenharmony_ci rsp->msg.cmd = msg->msg.cmd; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci if (msg->msg.data_len > 0) { 24962306a36Sopenharmony_ci if (rsp->msg.data_len < msg->msg.data_len) { 25062306a36Sopenharmony_ci if (trunc) { 25162306a36Sopenharmony_ci rv2 = -EMSGSIZE; 25262306a36Sopenharmony_ci msg->msg.data_len = rsp->msg.data_len; 25362306a36Sopenharmony_ci } else { 25462306a36Sopenharmony_ci rv = -EMSGSIZE; 25562306a36Sopenharmony_ci goto recv_putback_on_err; 25662306a36Sopenharmony_ci } 25762306a36Sopenharmony_ci } 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci if (copy_to_user(rsp->msg.data, 26062306a36Sopenharmony_ci msg->msg.data, 26162306a36Sopenharmony_ci msg->msg.data_len)) { 26262306a36Sopenharmony_ci rv = -EFAULT; 26362306a36Sopenharmony_ci goto recv_putback_on_err; 26462306a36Sopenharmony_ci } 26562306a36Sopenharmony_ci rsp->msg.data_len = msg->msg.data_len; 26662306a36Sopenharmony_ci } else { 26762306a36Sopenharmony_ci rsp->msg.data_len = 0; 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci rv = copyout(rsp, to); 27162306a36Sopenharmony_ci if (rv) 27262306a36Sopenharmony_ci goto recv_putback_on_err; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci mutex_unlock(&priv->recv_mutex); 27562306a36Sopenharmony_ci ipmi_free_recv_msg(msg); 27662306a36Sopenharmony_ci return rv2; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_cirecv_putback_on_err: 27962306a36Sopenharmony_ci /* If we got an error, put the message back onto 28062306a36Sopenharmony_ci the head of the queue. */ 28162306a36Sopenharmony_ci spin_lock_irqsave(&priv->recv_msg_lock, flags); 28262306a36Sopenharmony_ci list_add(entry, &priv->recv_msgs); 28362306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->recv_msg_lock, flags); 28462306a36Sopenharmony_cirecv_err: 28562306a36Sopenharmony_ci mutex_unlock(&priv->recv_mutex); 28662306a36Sopenharmony_ci return rv; 28762306a36Sopenharmony_ci} 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_cistatic int copyout_recv(struct ipmi_recv *rsp, void __user *to) 29062306a36Sopenharmony_ci{ 29162306a36Sopenharmony_ci return copy_to_user(to, rsp, sizeof(struct ipmi_recv)) ? -EFAULT : 0; 29262306a36Sopenharmony_ci} 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_cistatic long ipmi_ioctl(struct file *file, 29562306a36Sopenharmony_ci unsigned int cmd, 29662306a36Sopenharmony_ci unsigned long data) 29762306a36Sopenharmony_ci{ 29862306a36Sopenharmony_ci int rv = -EINVAL; 29962306a36Sopenharmony_ci struct ipmi_file_private *priv = file->private_data; 30062306a36Sopenharmony_ci void __user *arg = (void __user *)data; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci switch (cmd) 30362306a36Sopenharmony_ci { 30462306a36Sopenharmony_ci case IPMICTL_SEND_COMMAND: 30562306a36Sopenharmony_ci { 30662306a36Sopenharmony_ci struct ipmi_req req; 30762306a36Sopenharmony_ci int retries; 30862306a36Sopenharmony_ci unsigned int retry_time_ms; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci if (copy_from_user(&req, arg, sizeof(req))) { 31162306a36Sopenharmony_ci rv = -EFAULT; 31262306a36Sopenharmony_ci break; 31362306a36Sopenharmony_ci } 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci mutex_lock(&priv->recv_mutex); 31662306a36Sopenharmony_ci retries = priv->default_retries; 31762306a36Sopenharmony_ci retry_time_ms = priv->default_retry_time_ms; 31862306a36Sopenharmony_ci mutex_unlock(&priv->recv_mutex); 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci rv = handle_send_req(priv->user, &req, retries, retry_time_ms); 32162306a36Sopenharmony_ci break; 32262306a36Sopenharmony_ci } 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci case IPMICTL_SEND_COMMAND_SETTIME: 32562306a36Sopenharmony_ci { 32662306a36Sopenharmony_ci struct ipmi_req_settime req; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci if (copy_from_user(&req, arg, sizeof(req))) { 32962306a36Sopenharmony_ci rv = -EFAULT; 33062306a36Sopenharmony_ci break; 33162306a36Sopenharmony_ci } 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci rv = handle_send_req(priv->user, 33462306a36Sopenharmony_ci &req.req, 33562306a36Sopenharmony_ci req.retries, 33662306a36Sopenharmony_ci req.retry_time_ms); 33762306a36Sopenharmony_ci break; 33862306a36Sopenharmony_ci } 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci case IPMICTL_RECEIVE_MSG: 34162306a36Sopenharmony_ci case IPMICTL_RECEIVE_MSG_TRUNC: 34262306a36Sopenharmony_ci { 34362306a36Sopenharmony_ci struct ipmi_recv rsp; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci if (copy_from_user(&rsp, arg, sizeof(rsp))) 34662306a36Sopenharmony_ci rv = -EFAULT; 34762306a36Sopenharmony_ci else 34862306a36Sopenharmony_ci rv = handle_recv(priv, cmd == IPMICTL_RECEIVE_MSG_TRUNC, 34962306a36Sopenharmony_ci &rsp, copyout_recv, arg); 35062306a36Sopenharmony_ci break; 35162306a36Sopenharmony_ci } 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci case IPMICTL_REGISTER_FOR_CMD: 35462306a36Sopenharmony_ci { 35562306a36Sopenharmony_ci struct ipmi_cmdspec val; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci if (copy_from_user(&val, arg, sizeof(val))) { 35862306a36Sopenharmony_ci rv = -EFAULT; 35962306a36Sopenharmony_ci break; 36062306a36Sopenharmony_ci } 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci rv = ipmi_register_for_cmd(priv->user, val.netfn, val.cmd, 36362306a36Sopenharmony_ci IPMI_CHAN_ALL); 36462306a36Sopenharmony_ci break; 36562306a36Sopenharmony_ci } 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci case IPMICTL_UNREGISTER_FOR_CMD: 36862306a36Sopenharmony_ci { 36962306a36Sopenharmony_ci struct ipmi_cmdspec val; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci if (copy_from_user(&val, arg, sizeof(val))) { 37262306a36Sopenharmony_ci rv = -EFAULT; 37362306a36Sopenharmony_ci break; 37462306a36Sopenharmony_ci } 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci rv = ipmi_unregister_for_cmd(priv->user, val.netfn, val.cmd, 37762306a36Sopenharmony_ci IPMI_CHAN_ALL); 37862306a36Sopenharmony_ci break; 37962306a36Sopenharmony_ci } 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci case IPMICTL_REGISTER_FOR_CMD_CHANS: 38262306a36Sopenharmony_ci { 38362306a36Sopenharmony_ci struct ipmi_cmdspec_chans val; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci if (copy_from_user(&val, arg, sizeof(val))) { 38662306a36Sopenharmony_ci rv = -EFAULT; 38762306a36Sopenharmony_ci break; 38862306a36Sopenharmony_ci } 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci rv = ipmi_register_for_cmd(priv->user, val.netfn, val.cmd, 39162306a36Sopenharmony_ci val.chans); 39262306a36Sopenharmony_ci break; 39362306a36Sopenharmony_ci } 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci case IPMICTL_UNREGISTER_FOR_CMD_CHANS: 39662306a36Sopenharmony_ci { 39762306a36Sopenharmony_ci struct ipmi_cmdspec_chans val; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci if (copy_from_user(&val, arg, sizeof(val))) { 40062306a36Sopenharmony_ci rv = -EFAULT; 40162306a36Sopenharmony_ci break; 40262306a36Sopenharmony_ci } 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci rv = ipmi_unregister_for_cmd(priv->user, val.netfn, val.cmd, 40562306a36Sopenharmony_ci val.chans); 40662306a36Sopenharmony_ci break; 40762306a36Sopenharmony_ci } 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci case IPMICTL_SET_GETS_EVENTS_CMD: 41062306a36Sopenharmony_ci { 41162306a36Sopenharmony_ci int val; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci if (copy_from_user(&val, arg, sizeof(val))) { 41462306a36Sopenharmony_ci rv = -EFAULT; 41562306a36Sopenharmony_ci break; 41662306a36Sopenharmony_ci } 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci rv = ipmi_set_gets_events(priv->user, val); 41962306a36Sopenharmony_ci break; 42062306a36Sopenharmony_ci } 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci /* The next four are legacy, not per-channel. */ 42362306a36Sopenharmony_ci case IPMICTL_SET_MY_ADDRESS_CMD: 42462306a36Sopenharmony_ci { 42562306a36Sopenharmony_ci unsigned int val; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci if (copy_from_user(&val, arg, sizeof(val))) { 42862306a36Sopenharmony_ci rv = -EFAULT; 42962306a36Sopenharmony_ci break; 43062306a36Sopenharmony_ci } 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci rv = ipmi_set_my_address(priv->user, 0, val); 43362306a36Sopenharmony_ci break; 43462306a36Sopenharmony_ci } 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci case IPMICTL_GET_MY_ADDRESS_CMD: 43762306a36Sopenharmony_ci { 43862306a36Sopenharmony_ci unsigned int val; 43962306a36Sopenharmony_ci unsigned char rval; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci rv = ipmi_get_my_address(priv->user, 0, &rval); 44262306a36Sopenharmony_ci if (rv) 44362306a36Sopenharmony_ci break; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci val = rval; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci if (copy_to_user(arg, &val, sizeof(val))) { 44862306a36Sopenharmony_ci rv = -EFAULT; 44962306a36Sopenharmony_ci break; 45062306a36Sopenharmony_ci } 45162306a36Sopenharmony_ci break; 45262306a36Sopenharmony_ci } 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci case IPMICTL_SET_MY_LUN_CMD: 45562306a36Sopenharmony_ci { 45662306a36Sopenharmony_ci unsigned int val; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci if (copy_from_user(&val, arg, sizeof(val))) { 45962306a36Sopenharmony_ci rv = -EFAULT; 46062306a36Sopenharmony_ci break; 46162306a36Sopenharmony_ci } 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci rv = ipmi_set_my_LUN(priv->user, 0, val); 46462306a36Sopenharmony_ci break; 46562306a36Sopenharmony_ci } 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci case IPMICTL_GET_MY_LUN_CMD: 46862306a36Sopenharmony_ci { 46962306a36Sopenharmony_ci unsigned int val; 47062306a36Sopenharmony_ci unsigned char rval; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci rv = ipmi_get_my_LUN(priv->user, 0, &rval); 47362306a36Sopenharmony_ci if (rv) 47462306a36Sopenharmony_ci break; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci val = rval; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci if (copy_to_user(arg, &val, sizeof(val))) { 47962306a36Sopenharmony_ci rv = -EFAULT; 48062306a36Sopenharmony_ci break; 48162306a36Sopenharmony_ci } 48262306a36Sopenharmony_ci break; 48362306a36Sopenharmony_ci } 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci case IPMICTL_SET_MY_CHANNEL_ADDRESS_CMD: 48662306a36Sopenharmony_ci { 48762306a36Sopenharmony_ci struct ipmi_channel_lun_address_set val; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci if (copy_from_user(&val, arg, sizeof(val))) { 49062306a36Sopenharmony_ci rv = -EFAULT; 49162306a36Sopenharmony_ci break; 49262306a36Sopenharmony_ci } 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci return ipmi_set_my_address(priv->user, val.channel, val.value); 49562306a36Sopenharmony_ci } 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci case IPMICTL_GET_MY_CHANNEL_ADDRESS_CMD: 49862306a36Sopenharmony_ci { 49962306a36Sopenharmony_ci struct ipmi_channel_lun_address_set val; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci if (copy_from_user(&val, arg, sizeof(val))) { 50262306a36Sopenharmony_ci rv = -EFAULT; 50362306a36Sopenharmony_ci break; 50462306a36Sopenharmony_ci } 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci rv = ipmi_get_my_address(priv->user, val.channel, &val.value); 50762306a36Sopenharmony_ci if (rv) 50862306a36Sopenharmony_ci break; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci if (copy_to_user(arg, &val, sizeof(val))) { 51162306a36Sopenharmony_ci rv = -EFAULT; 51262306a36Sopenharmony_ci break; 51362306a36Sopenharmony_ci } 51462306a36Sopenharmony_ci break; 51562306a36Sopenharmony_ci } 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci case IPMICTL_SET_MY_CHANNEL_LUN_CMD: 51862306a36Sopenharmony_ci { 51962306a36Sopenharmony_ci struct ipmi_channel_lun_address_set val; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci if (copy_from_user(&val, arg, sizeof(val))) { 52262306a36Sopenharmony_ci rv = -EFAULT; 52362306a36Sopenharmony_ci break; 52462306a36Sopenharmony_ci } 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci rv = ipmi_set_my_LUN(priv->user, val.channel, val.value); 52762306a36Sopenharmony_ci break; 52862306a36Sopenharmony_ci } 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci case IPMICTL_GET_MY_CHANNEL_LUN_CMD: 53162306a36Sopenharmony_ci { 53262306a36Sopenharmony_ci struct ipmi_channel_lun_address_set val; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci if (copy_from_user(&val, arg, sizeof(val))) { 53562306a36Sopenharmony_ci rv = -EFAULT; 53662306a36Sopenharmony_ci break; 53762306a36Sopenharmony_ci } 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci rv = ipmi_get_my_LUN(priv->user, val.channel, &val.value); 54062306a36Sopenharmony_ci if (rv) 54162306a36Sopenharmony_ci break; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci if (copy_to_user(arg, &val, sizeof(val))) { 54462306a36Sopenharmony_ci rv = -EFAULT; 54562306a36Sopenharmony_ci break; 54662306a36Sopenharmony_ci } 54762306a36Sopenharmony_ci break; 54862306a36Sopenharmony_ci } 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci case IPMICTL_SET_TIMING_PARMS_CMD: 55162306a36Sopenharmony_ci { 55262306a36Sopenharmony_ci struct ipmi_timing_parms parms; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci if (copy_from_user(&parms, arg, sizeof(parms))) { 55562306a36Sopenharmony_ci rv = -EFAULT; 55662306a36Sopenharmony_ci break; 55762306a36Sopenharmony_ci } 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci mutex_lock(&priv->recv_mutex); 56062306a36Sopenharmony_ci priv->default_retries = parms.retries; 56162306a36Sopenharmony_ci priv->default_retry_time_ms = parms.retry_time_ms; 56262306a36Sopenharmony_ci mutex_unlock(&priv->recv_mutex); 56362306a36Sopenharmony_ci rv = 0; 56462306a36Sopenharmony_ci break; 56562306a36Sopenharmony_ci } 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci case IPMICTL_GET_TIMING_PARMS_CMD: 56862306a36Sopenharmony_ci { 56962306a36Sopenharmony_ci struct ipmi_timing_parms parms; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci mutex_lock(&priv->recv_mutex); 57262306a36Sopenharmony_ci parms.retries = priv->default_retries; 57362306a36Sopenharmony_ci parms.retry_time_ms = priv->default_retry_time_ms; 57462306a36Sopenharmony_ci mutex_unlock(&priv->recv_mutex); 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci if (copy_to_user(arg, &parms, sizeof(parms))) { 57762306a36Sopenharmony_ci rv = -EFAULT; 57862306a36Sopenharmony_ci break; 57962306a36Sopenharmony_ci } 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci rv = 0; 58262306a36Sopenharmony_ci break; 58362306a36Sopenharmony_ci } 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci case IPMICTL_GET_MAINTENANCE_MODE_CMD: 58662306a36Sopenharmony_ci { 58762306a36Sopenharmony_ci int mode; 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci mode = ipmi_get_maintenance_mode(priv->user); 59062306a36Sopenharmony_ci if (copy_to_user(arg, &mode, sizeof(mode))) { 59162306a36Sopenharmony_ci rv = -EFAULT; 59262306a36Sopenharmony_ci break; 59362306a36Sopenharmony_ci } 59462306a36Sopenharmony_ci rv = 0; 59562306a36Sopenharmony_ci break; 59662306a36Sopenharmony_ci } 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci case IPMICTL_SET_MAINTENANCE_MODE_CMD: 59962306a36Sopenharmony_ci { 60062306a36Sopenharmony_ci int mode; 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci if (copy_from_user(&mode, arg, sizeof(mode))) { 60362306a36Sopenharmony_ci rv = -EFAULT; 60462306a36Sopenharmony_ci break; 60562306a36Sopenharmony_ci } 60662306a36Sopenharmony_ci rv = ipmi_set_maintenance_mode(priv->user, mode); 60762306a36Sopenharmony_ci break; 60862306a36Sopenharmony_ci } 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci default: 61162306a36Sopenharmony_ci rv = -ENOTTY; 61262306a36Sopenharmony_ci break; 61362306a36Sopenharmony_ci } 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci return rv; 61662306a36Sopenharmony_ci} 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci#ifdef CONFIG_COMPAT 61962306a36Sopenharmony_ci/* 62062306a36Sopenharmony_ci * The following code contains code for supporting 32-bit compatible 62162306a36Sopenharmony_ci * ioctls on 64-bit kernels. This allows running 32-bit apps on the 62262306a36Sopenharmony_ci * 64-bit kernel 62362306a36Sopenharmony_ci */ 62462306a36Sopenharmony_ci#define COMPAT_IPMICTL_SEND_COMMAND \ 62562306a36Sopenharmony_ci _IOR(IPMI_IOC_MAGIC, 13, struct compat_ipmi_req) 62662306a36Sopenharmony_ci#define COMPAT_IPMICTL_SEND_COMMAND_SETTIME \ 62762306a36Sopenharmony_ci _IOR(IPMI_IOC_MAGIC, 21, struct compat_ipmi_req_settime) 62862306a36Sopenharmony_ci#define COMPAT_IPMICTL_RECEIVE_MSG \ 62962306a36Sopenharmony_ci _IOWR(IPMI_IOC_MAGIC, 12, struct compat_ipmi_recv) 63062306a36Sopenharmony_ci#define COMPAT_IPMICTL_RECEIVE_MSG_TRUNC \ 63162306a36Sopenharmony_ci _IOWR(IPMI_IOC_MAGIC, 11, struct compat_ipmi_recv) 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_cistruct compat_ipmi_msg { 63462306a36Sopenharmony_ci u8 netfn; 63562306a36Sopenharmony_ci u8 cmd; 63662306a36Sopenharmony_ci u16 data_len; 63762306a36Sopenharmony_ci compat_uptr_t data; 63862306a36Sopenharmony_ci}; 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_cistruct compat_ipmi_req { 64162306a36Sopenharmony_ci compat_uptr_t addr; 64262306a36Sopenharmony_ci compat_uint_t addr_len; 64362306a36Sopenharmony_ci compat_long_t msgid; 64462306a36Sopenharmony_ci struct compat_ipmi_msg msg; 64562306a36Sopenharmony_ci}; 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_cistruct compat_ipmi_recv { 64862306a36Sopenharmony_ci compat_int_t recv_type; 64962306a36Sopenharmony_ci compat_uptr_t addr; 65062306a36Sopenharmony_ci compat_uint_t addr_len; 65162306a36Sopenharmony_ci compat_long_t msgid; 65262306a36Sopenharmony_ci struct compat_ipmi_msg msg; 65362306a36Sopenharmony_ci}; 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_cistruct compat_ipmi_req_settime { 65662306a36Sopenharmony_ci struct compat_ipmi_req req; 65762306a36Sopenharmony_ci compat_int_t retries; 65862306a36Sopenharmony_ci compat_uint_t retry_time_ms; 65962306a36Sopenharmony_ci}; 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci/* 66262306a36Sopenharmony_ci * Define some helper functions for copying IPMI data 66362306a36Sopenharmony_ci */ 66462306a36Sopenharmony_cistatic void get_compat_ipmi_msg(struct ipmi_msg *p64, 66562306a36Sopenharmony_ci struct compat_ipmi_msg *p32) 66662306a36Sopenharmony_ci{ 66762306a36Sopenharmony_ci p64->netfn = p32->netfn; 66862306a36Sopenharmony_ci p64->cmd = p32->cmd; 66962306a36Sopenharmony_ci p64->data_len = p32->data_len; 67062306a36Sopenharmony_ci p64->data = compat_ptr(p32->data); 67162306a36Sopenharmony_ci} 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_cistatic void get_compat_ipmi_req(struct ipmi_req *p64, 67462306a36Sopenharmony_ci struct compat_ipmi_req *p32) 67562306a36Sopenharmony_ci{ 67662306a36Sopenharmony_ci p64->addr = compat_ptr(p32->addr); 67762306a36Sopenharmony_ci p64->addr_len = p32->addr_len; 67862306a36Sopenharmony_ci p64->msgid = p32->msgid; 67962306a36Sopenharmony_ci get_compat_ipmi_msg(&p64->msg, &p32->msg); 68062306a36Sopenharmony_ci} 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_cistatic void get_compat_ipmi_req_settime(struct ipmi_req_settime *p64, 68362306a36Sopenharmony_ci struct compat_ipmi_req_settime *p32) 68462306a36Sopenharmony_ci{ 68562306a36Sopenharmony_ci get_compat_ipmi_req(&p64->req, &p32->req); 68662306a36Sopenharmony_ci p64->retries = p32->retries; 68762306a36Sopenharmony_ci p64->retry_time_ms = p32->retry_time_ms; 68862306a36Sopenharmony_ci} 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_cistatic void get_compat_ipmi_recv(struct ipmi_recv *p64, 69162306a36Sopenharmony_ci struct compat_ipmi_recv *p32) 69262306a36Sopenharmony_ci{ 69362306a36Sopenharmony_ci memset(p64, 0, sizeof(struct ipmi_recv)); 69462306a36Sopenharmony_ci p64->recv_type = p32->recv_type; 69562306a36Sopenharmony_ci p64->addr = compat_ptr(p32->addr); 69662306a36Sopenharmony_ci p64->addr_len = p32->addr_len; 69762306a36Sopenharmony_ci p64->msgid = p32->msgid; 69862306a36Sopenharmony_ci get_compat_ipmi_msg(&p64->msg, &p32->msg); 69962306a36Sopenharmony_ci} 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_cistatic int copyout_recv32(struct ipmi_recv *p64, void __user *to) 70262306a36Sopenharmony_ci{ 70362306a36Sopenharmony_ci struct compat_ipmi_recv v32; 70462306a36Sopenharmony_ci memset(&v32, 0, sizeof(struct compat_ipmi_recv)); 70562306a36Sopenharmony_ci v32.recv_type = p64->recv_type; 70662306a36Sopenharmony_ci v32.addr = ptr_to_compat(p64->addr); 70762306a36Sopenharmony_ci v32.addr_len = p64->addr_len; 70862306a36Sopenharmony_ci v32.msgid = p64->msgid; 70962306a36Sopenharmony_ci v32.msg.netfn = p64->msg.netfn; 71062306a36Sopenharmony_ci v32.msg.cmd = p64->msg.cmd; 71162306a36Sopenharmony_ci v32.msg.data_len = p64->msg.data_len; 71262306a36Sopenharmony_ci v32.msg.data = ptr_to_compat(p64->msg.data); 71362306a36Sopenharmony_ci return copy_to_user(to, &v32, sizeof(v32)) ? -EFAULT : 0; 71462306a36Sopenharmony_ci} 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci/* 71762306a36Sopenharmony_ci * Handle compatibility ioctls 71862306a36Sopenharmony_ci */ 71962306a36Sopenharmony_cistatic long compat_ipmi_ioctl(struct file *filep, unsigned int cmd, 72062306a36Sopenharmony_ci unsigned long arg) 72162306a36Sopenharmony_ci{ 72262306a36Sopenharmony_ci struct ipmi_file_private *priv = filep->private_data; 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci switch(cmd) { 72562306a36Sopenharmony_ci case COMPAT_IPMICTL_SEND_COMMAND: 72662306a36Sopenharmony_ci { 72762306a36Sopenharmony_ci struct ipmi_req rp; 72862306a36Sopenharmony_ci struct compat_ipmi_req r32; 72962306a36Sopenharmony_ci int retries; 73062306a36Sopenharmony_ci unsigned int retry_time_ms; 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci if (copy_from_user(&r32, compat_ptr(arg), sizeof(r32))) 73362306a36Sopenharmony_ci return -EFAULT; 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci get_compat_ipmi_req(&rp, &r32); 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci mutex_lock(&priv->recv_mutex); 73862306a36Sopenharmony_ci retries = priv->default_retries; 73962306a36Sopenharmony_ci retry_time_ms = priv->default_retry_time_ms; 74062306a36Sopenharmony_ci mutex_unlock(&priv->recv_mutex); 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci return handle_send_req(priv->user, &rp, 74362306a36Sopenharmony_ci retries, retry_time_ms); 74462306a36Sopenharmony_ci } 74562306a36Sopenharmony_ci case COMPAT_IPMICTL_SEND_COMMAND_SETTIME: 74662306a36Sopenharmony_ci { 74762306a36Sopenharmony_ci struct ipmi_req_settime sp; 74862306a36Sopenharmony_ci struct compat_ipmi_req_settime sp32; 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci if (copy_from_user(&sp32, compat_ptr(arg), sizeof(sp32))) 75162306a36Sopenharmony_ci return -EFAULT; 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci get_compat_ipmi_req_settime(&sp, &sp32); 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci return handle_send_req(priv->user, &sp.req, 75662306a36Sopenharmony_ci sp.retries, sp.retry_time_ms); 75762306a36Sopenharmony_ci } 75862306a36Sopenharmony_ci case COMPAT_IPMICTL_RECEIVE_MSG: 75962306a36Sopenharmony_ci case COMPAT_IPMICTL_RECEIVE_MSG_TRUNC: 76062306a36Sopenharmony_ci { 76162306a36Sopenharmony_ci struct ipmi_recv recv64; 76262306a36Sopenharmony_ci struct compat_ipmi_recv recv32; 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci if (copy_from_user(&recv32, compat_ptr(arg), sizeof(recv32))) 76562306a36Sopenharmony_ci return -EFAULT; 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci get_compat_ipmi_recv(&recv64, &recv32); 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci return handle_recv(priv, 77062306a36Sopenharmony_ci cmd == COMPAT_IPMICTL_RECEIVE_MSG_TRUNC, 77162306a36Sopenharmony_ci &recv64, copyout_recv32, compat_ptr(arg)); 77262306a36Sopenharmony_ci } 77362306a36Sopenharmony_ci default: 77462306a36Sopenharmony_ci return ipmi_ioctl(filep, cmd, arg); 77562306a36Sopenharmony_ci } 77662306a36Sopenharmony_ci} 77762306a36Sopenharmony_ci#endif 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_cistatic const struct file_operations ipmi_fops = { 78062306a36Sopenharmony_ci .owner = THIS_MODULE, 78162306a36Sopenharmony_ci .unlocked_ioctl = ipmi_ioctl, 78262306a36Sopenharmony_ci#ifdef CONFIG_COMPAT 78362306a36Sopenharmony_ci .compat_ioctl = compat_ipmi_ioctl, 78462306a36Sopenharmony_ci#endif 78562306a36Sopenharmony_ci .open = ipmi_open, 78662306a36Sopenharmony_ci .release = ipmi_release, 78762306a36Sopenharmony_ci .fasync = ipmi_fasync, 78862306a36Sopenharmony_ci .poll = ipmi_poll, 78962306a36Sopenharmony_ci .llseek = noop_llseek, 79062306a36Sopenharmony_ci}; 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci#define DEVICE_NAME "ipmidev" 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_cistatic int ipmi_major; 79562306a36Sopenharmony_cimodule_param(ipmi_major, int, 0); 79662306a36Sopenharmony_ciMODULE_PARM_DESC(ipmi_major, "Sets the major number of the IPMI device. By" 79762306a36Sopenharmony_ci " default, or if you set it to zero, it will choose the next" 79862306a36Sopenharmony_ci " available device. Setting it to -1 will disable the" 79962306a36Sopenharmony_ci " interface. Other values will set the major device number" 80062306a36Sopenharmony_ci " to that value."); 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci/* Keep track of the devices that are registered. */ 80362306a36Sopenharmony_cistruct ipmi_reg_list { 80462306a36Sopenharmony_ci dev_t dev; 80562306a36Sopenharmony_ci struct list_head link; 80662306a36Sopenharmony_ci}; 80762306a36Sopenharmony_cistatic LIST_HEAD(reg_list); 80862306a36Sopenharmony_cistatic DEFINE_MUTEX(reg_list_mutex); 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_cistatic const struct class ipmi_class = { 81162306a36Sopenharmony_ci .name = "ipmi", 81262306a36Sopenharmony_ci}; 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_cistatic void ipmi_new_smi(int if_num, struct device *device) 81562306a36Sopenharmony_ci{ 81662306a36Sopenharmony_ci dev_t dev = MKDEV(ipmi_major, if_num); 81762306a36Sopenharmony_ci struct ipmi_reg_list *entry; 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci entry = kmalloc(sizeof(*entry), GFP_KERNEL); 82062306a36Sopenharmony_ci if (!entry) { 82162306a36Sopenharmony_ci pr_err("ipmi_devintf: Unable to create the ipmi class device link\n"); 82262306a36Sopenharmony_ci return; 82362306a36Sopenharmony_ci } 82462306a36Sopenharmony_ci entry->dev = dev; 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci mutex_lock(®_list_mutex); 82762306a36Sopenharmony_ci device_create(&ipmi_class, device, dev, NULL, "ipmi%d", if_num); 82862306a36Sopenharmony_ci list_add(&entry->link, ®_list); 82962306a36Sopenharmony_ci mutex_unlock(®_list_mutex); 83062306a36Sopenharmony_ci} 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_cistatic void ipmi_smi_gone(int if_num) 83362306a36Sopenharmony_ci{ 83462306a36Sopenharmony_ci dev_t dev = MKDEV(ipmi_major, if_num); 83562306a36Sopenharmony_ci struct ipmi_reg_list *entry; 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci mutex_lock(®_list_mutex); 83862306a36Sopenharmony_ci list_for_each_entry(entry, ®_list, link) { 83962306a36Sopenharmony_ci if (entry->dev == dev) { 84062306a36Sopenharmony_ci list_del(&entry->link); 84162306a36Sopenharmony_ci kfree(entry); 84262306a36Sopenharmony_ci break; 84362306a36Sopenharmony_ci } 84462306a36Sopenharmony_ci } 84562306a36Sopenharmony_ci device_destroy(&ipmi_class, dev); 84662306a36Sopenharmony_ci mutex_unlock(®_list_mutex); 84762306a36Sopenharmony_ci} 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_cistatic struct ipmi_smi_watcher smi_watcher = 85062306a36Sopenharmony_ci{ 85162306a36Sopenharmony_ci .owner = THIS_MODULE, 85262306a36Sopenharmony_ci .new_smi = ipmi_new_smi, 85362306a36Sopenharmony_ci .smi_gone = ipmi_smi_gone, 85462306a36Sopenharmony_ci}; 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_cistatic int __init init_ipmi_devintf(void) 85762306a36Sopenharmony_ci{ 85862306a36Sopenharmony_ci int rv; 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci if (ipmi_major < 0) 86162306a36Sopenharmony_ci return -EINVAL; 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci pr_info("ipmi device interface\n"); 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci rv = class_register(&ipmi_class); 86662306a36Sopenharmony_ci if (rv) 86762306a36Sopenharmony_ci return rv; 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci rv = register_chrdev(ipmi_major, DEVICE_NAME, &ipmi_fops); 87062306a36Sopenharmony_ci if (rv < 0) { 87162306a36Sopenharmony_ci class_unregister(&ipmi_class); 87262306a36Sopenharmony_ci pr_err("ipmi: can't get major %d\n", ipmi_major); 87362306a36Sopenharmony_ci return rv; 87462306a36Sopenharmony_ci } 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci if (ipmi_major == 0) { 87762306a36Sopenharmony_ci ipmi_major = rv; 87862306a36Sopenharmony_ci } 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci rv = ipmi_smi_watcher_register(&smi_watcher); 88162306a36Sopenharmony_ci if (rv) { 88262306a36Sopenharmony_ci unregister_chrdev(ipmi_major, DEVICE_NAME); 88362306a36Sopenharmony_ci class_unregister(&ipmi_class); 88462306a36Sopenharmony_ci pr_warn("ipmi: can't register smi watcher\n"); 88562306a36Sopenharmony_ci return rv; 88662306a36Sopenharmony_ci } 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci return 0; 88962306a36Sopenharmony_ci} 89062306a36Sopenharmony_cimodule_init(init_ipmi_devintf); 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_cistatic void __exit cleanup_ipmi(void) 89362306a36Sopenharmony_ci{ 89462306a36Sopenharmony_ci struct ipmi_reg_list *entry, *entry2; 89562306a36Sopenharmony_ci mutex_lock(®_list_mutex); 89662306a36Sopenharmony_ci list_for_each_entry_safe(entry, entry2, ®_list, link) { 89762306a36Sopenharmony_ci list_del(&entry->link); 89862306a36Sopenharmony_ci device_destroy(&ipmi_class, entry->dev); 89962306a36Sopenharmony_ci kfree(entry); 90062306a36Sopenharmony_ci } 90162306a36Sopenharmony_ci mutex_unlock(®_list_mutex); 90262306a36Sopenharmony_ci class_unregister(&ipmi_class); 90362306a36Sopenharmony_ci ipmi_smi_watcher_unregister(&smi_watcher); 90462306a36Sopenharmony_ci unregister_chrdev(ipmi_major, DEVICE_NAME); 90562306a36Sopenharmony_ci} 90662306a36Sopenharmony_cimodule_exit(cleanup_ipmi); 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 90962306a36Sopenharmony_ciMODULE_AUTHOR("Corey Minyard <minyard@mvista.com>"); 91062306a36Sopenharmony_ciMODULE_DESCRIPTION("Linux device interface for the IPMI message handler."); 911