lkml.org 
[lkml]   [2021]   [Apr]   [8]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    /
    From
    SubjectRe: [PATCH v2 09/21] ipmi: kcs_bmc: Split out kcs_bmc_cdev_ipmi
    Date
    On Fri, Mar 19, 2021 at 01:27:40AM CDT, Andrew Jeffery wrote:
    >Take steps towards defining a coherent API to separate the KCS device
    >drivers from the userspace interface. Decreasing the coupling will
    >improve the separation of concerns and enable the introduction of
    >alternative userspace interfaces.
    >
    >For now, simply split the chardev logic out to a separate file. The code
    >continues to build into the same module.
    >
    >Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
    >---
    > drivers/char/ipmi/Makefile | 2 +-
    > drivers/char/ipmi/kcs_bmc.c | 423 +------------------------
    > drivers/char/ipmi/kcs_bmc.h | 10 +-
    > drivers/char/ipmi/kcs_bmc_cdev_ipmi.c | 428 ++++++++++++++++++++++++++
    > 4 files changed, 451 insertions(+), 412 deletions(-)
    > create mode 100644 drivers/char/ipmi/kcs_bmc_cdev_ipmi.c
    >
    >diff --git a/drivers/char/ipmi/Makefile b/drivers/char/ipmi/Makefile
    >index 0822adc2ec41..a302bc865370 100644
    >--- a/drivers/char/ipmi/Makefile
    >+++ b/drivers/char/ipmi/Makefile
    >@@ -22,7 +22,7 @@ obj-$(CONFIG_IPMI_SSIF) += ipmi_ssif.o
    > obj-$(CONFIG_IPMI_POWERNV) += ipmi_powernv.o
    > obj-$(CONFIG_IPMI_WATCHDOG) += ipmi_watchdog.o
    > obj-$(CONFIG_IPMI_POWEROFF) += ipmi_poweroff.o
    >-obj-$(CONFIG_IPMI_KCS_BMC) += kcs_bmc.o
    >+obj-$(CONFIG_IPMI_KCS_BMC) += kcs_bmc.o kcs_bmc_cdev_ipmi.o
    > obj-$(CONFIG_ASPEED_BT_IPMI_BMC) += bt-bmc.o
    > obj-$(CONFIG_ASPEED_KCS_IPMI_BMC) += kcs_bmc_aspeed.o
    > obj-$(CONFIG_NPCM7XX_KCS_IPMI_BMC) += kcs_bmc_npcm7xx.o
    >diff --git a/drivers/char/ipmi/kcs_bmc.c b/drivers/char/ipmi/kcs_bmc.c
    >index c4336c1f2d6d..ef5c48ffe74a 100644
    >--- a/drivers/char/ipmi/kcs_bmc.c
    >+++ b/drivers/char/ipmi/kcs_bmc.c
    >@@ -3,446 +3,51 @@
    > * Copyright (c) 2015-2018, Intel Corporation.
    > */
    >
    >-#define pr_fmt(fmt) "kcs-bmc: " fmt
    >-
    >-#include <linux/errno.h>
    >-#include <linux/io.h>
    >-#include <linux/ipmi_bmc.h>
    > #include <linux/module.h>
    >-#include <linux/platform_device.h>
    >-#include <linux/poll.h>
    >-#include <linux/sched.h>
    >-#include <linux/slab.h>
    >
    > #include "kcs_bmc.h"
    >
    >-#define DEVICE_NAME "ipmi-kcs"
    >-
    >-#define KCS_MSG_BUFSIZ 1000
    >-
    >-#define KCS_ZERO_DATA 0
    >-
    >-
    >-/* IPMI 2.0 - Table 9-1, KCS Interface Status Register Bits */
    >-#define KCS_STATUS_STATE(state) (state << 6)
    >-#define KCS_STATUS_STATE_MASK GENMASK(7, 6)
    >-#define KCS_STATUS_CMD_DAT BIT(3)
    >-#define KCS_STATUS_SMS_ATN BIT(2)
    >-#define KCS_STATUS_IBF BIT(1)
    >-#define KCS_STATUS_OBF BIT(0)
    >-
    >-/* IPMI 2.0 - Table 9-2, KCS Interface State Bits */
    >-enum kcs_states {
    >- IDLE_STATE = 0,
    >- READ_STATE = 1,
    >- WRITE_STATE = 2,
    >- ERROR_STATE = 3,
    >-};
    >-
    >-/* IPMI 2.0 - Table 9-3, KCS Interface Control Codes */
    >-#define KCS_CMD_GET_STATUS_ABORT 0x60
    >-#define KCS_CMD_WRITE_START 0x61
    >-#define KCS_CMD_WRITE_END 0x62
    >-#define KCS_CMD_READ_BYTE 0x68
    >-
    >-static inline u8 kcs_bmc_read_data(struct kcs_bmc *kcs_bmc)
    >+u8 kcs_bmc_read_data(struct kcs_bmc *kcs_bmc)
    > {
    > return kcs_bmc->io_inputb(kcs_bmc, kcs_bmc->ioreg.idr);
    > }
    >+EXPORT_SYMBOL(kcs_bmc_read_data);
    >
    >-static inline void kcs_bmc_write_data(struct kcs_bmc *kcs_bmc, u8 data)
    >+void kcs_bmc_write_data(struct kcs_bmc *kcs_bmc, u8 data)
    > {
    > kcs_bmc->io_outputb(kcs_bmc, kcs_bmc->ioreg.odr, data);
    > }
    >+EXPORT_SYMBOL(kcs_bmc_write_data);
    >
    >-static inline u8 kcs_bmc_read_status(struct kcs_bmc *kcs_bmc)
    >+u8 kcs_bmc_read_status(struct kcs_bmc *kcs_bmc)
    > {
    > return kcs_bmc->io_inputb(kcs_bmc, kcs_bmc->ioreg.str);
    > }
    >+EXPORT_SYMBOL(kcs_bmc_read_status);
    >
    >-static inline void kcs_bmc_write_status(struct kcs_bmc *kcs_bmc, u8 data)
    >+void kcs_bmc_write_status(struct kcs_bmc *kcs_bmc, u8 data)
    > {
    > kcs_bmc->io_outputb(kcs_bmc, kcs_bmc->ioreg.str, data);
    > }
    >+EXPORT_SYMBOL(kcs_bmc_write_status);
    >
    >-static void kcs_bmc_update_status(struct kcs_bmc *kcs_bmc, u8 mask, u8 val)
    >+void kcs_bmc_update_status(struct kcs_bmc *kcs_bmc, u8 mask, u8 val)
    > {
    > kcs_bmc->io_updateb(kcs_bmc, kcs_bmc->ioreg.str, mask, val);
    > }
    >+EXPORT_SYMBOL(kcs_bmc_update_status);
    >
    >-static inline void set_state(struct kcs_bmc *kcs_bmc, u8 state)
    >-{
    >- kcs_bmc_update_status(kcs_bmc, KCS_STATUS_STATE_MASK,
    >- KCS_STATUS_STATE(state));
    >-}
    >-
    >-static void kcs_force_abort(struct kcs_bmc *kcs_bmc)
    >-{
    >- set_state(kcs_bmc, ERROR_STATE);
    >- kcs_bmc_read_data(kcs_bmc);
    >- kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA);
    >-
    >- kcs_bmc->phase = KCS_PHASE_ERROR;
    >- kcs_bmc->data_in_avail = false;
    >- kcs_bmc->data_in_idx = 0;
    >-}
    >-
    >-static void kcs_bmc_handle_data(struct kcs_bmc *kcs_bmc)
    >-{
    >- u8 data;
    >-
    >- switch (kcs_bmc->phase) {
    >- case KCS_PHASE_WRITE_START:
    >- kcs_bmc->phase = KCS_PHASE_WRITE_DATA;
    >- fallthrough;
    >-
    >- case KCS_PHASE_WRITE_DATA:
    >- if (kcs_bmc->data_in_idx < KCS_MSG_BUFSIZ) {
    >- set_state(kcs_bmc, WRITE_STATE);
    >- kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA);
    >- kcs_bmc->data_in[kcs_bmc->data_in_idx++] =
    >- kcs_bmc_read_data(kcs_bmc);
    >- } else {
    >- kcs_force_abort(kcs_bmc);
    >- kcs_bmc->error = KCS_LENGTH_ERROR;
    >- }
    >- break;
    >-
    >- case KCS_PHASE_WRITE_END_CMD:
    >- if (kcs_bmc->data_in_idx < KCS_MSG_BUFSIZ) {
    >- set_state(kcs_bmc, READ_STATE);
    >- kcs_bmc->data_in[kcs_bmc->data_in_idx++] =
    >- kcs_bmc_read_data(kcs_bmc);
    >- kcs_bmc->phase = KCS_PHASE_WRITE_DONE;
    >- kcs_bmc->data_in_avail = true;
    >- wake_up_interruptible(&kcs_bmc->queue);
    >- } else {
    >- kcs_force_abort(kcs_bmc);
    >- kcs_bmc->error = KCS_LENGTH_ERROR;
    >- }
    >- break;
    >-
    >- case KCS_PHASE_READ:
    >- if (kcs_bmc->data_out_idx == kcs_bmc->data_out_len)
    >- set_state(kcs_bmc, IDLE_STATE);
    >-
    >- data = kcs_bmc_read_data(kcs_bmc);
    >- if (data != KCS_CMD_READ_BYTE) {
    >- set_state(kcs_bmc, ERROR_STATE);
    >- kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA);
    >- break;
    >- }
    >-
    >- if (kcs_bmc->data_out_idx == kcs_bmc->data_out_len) {
    >- kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA);
    >- kcs_bmc->phase = KCS_PHASE_IDLE;
    >- break;
    >- }
    >-
    >- kcs_bmc_write_data(kcs_bmc,
    >- kcs_bmc->data_out[kcs_bmc->data_out_idx++]);
    >- break;
    >-
    >- case KCS_PHASE_ABORT_ERROR1:
    >- set_state(kcs_bmc, READ_STATE);
    >- kcs_bmc_read_data(kcs_bmc);
    >- kcs_bmc_write_data(kcs_bmc, kcs_bmc->error);
    >- kcs_bmc->phase = KCS_PHASE_ABORT_ERROR2;
    >- break;
    >-
    >- case KCS_PHASE_ABORT_ERROR2:
    >- set_state(kcs_bmc, IDLE_STATE);
    >- kcs_bmc_read_data(kcs_bmc);
    >- kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA);
    >- kcs_bmc->phase = KCS_PHASE_IDLE;
    >- break;
    >-
    >- default:
    >- kcs_force_abort(kcs_bmc);
    >- break;
    >- }
    >-}
    >-
    >-static void kcs_bmc_handle_cmd(struct kcs_bmc *kcs_bmc)
    >-{
    >- u8 cmd;
    >-
    >- set_state(kcs_bmc, WRITE_STATE);
    >- kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA);
    >-
    >- cmd = kcs_bmc_read_data(kcs_bmc);
    >- switch (cmd) {
    >- case KCS_CMD_WRITE_START:
    >- kcs_bmc->phase = KCS_PHASE_WRITE_START;
    >- kcs_bmc->error = KCS_NO_ERROR;
    >- kcs_bmc->data_in_avail = false;
    >- kcs_bmc->data_in_idx = 0;
    >- break;
    >-
    >- case KCS_CMD_WRITE_END:
    >- if (kcs_bmc->phase != KCS_PHASE_WRITE_DATA) {
    >- kcs_force_abort(kcs_bmc);
    >- break;
    >- }
    >-
    >- kcs_bmc->phase = KCS_PHASE_WRITE_END_CMD;
    >- break;
    >-
    >- case KCS_CMD_GET_STATUS_ABORT:
    >- if (kcs_bmc->error == KCS_NO_ERROR)
    >- kcs_bmc->error = KCS_ABORTED_BY_COMMAND;
    >-
    >- kcs_bmc->phase = KCS_PHASE_ABORT_ERROR1;
    >- kcs_bmc->data_in_avail = false;
    >- kcs_bmc->data_in_idx = 0;
    >- break;
    >-
    >- default:
    >- kcs_force_abort(kcs_bmc);
    >- kcs_bmc->error = KCS_ILLEGAL_CONTROL_CODE;
    >- break;
    >- }
    >-}
    >-
    >+int kcs_bmc_ipmi_event(struct kcs_bmc *kcs_bmc);

    This declaration looks a bit out of place here; should it be in
    kcs_bmc.h instead?

    > int kcs_bmc_handle_event(struct kcs_bmc *kcs_bmc)
    > {
    >- unsigned long flags;
    >- int ret = -ENODATA;
    >- u8 status;
    >-
    >- spin_lock_irqsave(&kcs_bmc->lock, flags);
    >-
    >- status = kcs_bmc_read_status(kcs_bmc);
    >- if (status & KCS_STATUS_IBF) {
    >- if (!kcs_bmc->running)
    >- kcs_force_abort(kcs_bmc);
    >- else if (status & KCS_STATUS_CMD_DAT)
    >- kcs_bmc_handle_cmd(kcs_bmc);
    >- else
    >- kcs_bmc_handle_data(kcs_bmc);
    >-
    >- ret = 0;
    >- }
    >-
    >- spin_unlock_irqrestore(&kcs_bmc->lock, flags);
    >-
    >- return ret;
    >+ return kcs_bmc_ipmi_event(kcs_bmc);
    > }
    > EXPORT_SYMBOL(kcs_bmc_handle_event);
    >
    >-static inline struct kcs_bmc *to_kcs_bmc(struct file *filp)
    >-{
    >- return container_of(filp->private_data, struct kcs_bmc, miscdev);
    >-}
    >-
    >-static int kcs_bmc_open(struct inode *inode, struct file *filp)
    >-{
    >- struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp);
    >- int ret = 0;
    >-
    >- spin_lock_irq(&kcs_bmc->lock);
    >- if (!kcs_bmc->running)
    >- kcs_bmc->running = 1;
    >- else
    >- ret = -EBUSY;
    >- spin_unlock_irq(&kcs_bmc->lock);
    >-
    >- return ret;
    >-}
    >-
    >-static __poll_t kcs_bmc_poll(struct file *filp, poll_table *wait)
    >-{
    >- struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp);
    >- __poll_t mask = 0;
    >-
    >- poll_wait(filp, &kcs_bmc->queue, wait);
    >-
    >- spin_lock_irq(&kcs_bmc->lock);
    >- if (kcs_bmc->data_in_avail)
    >- mask |= EPOLLIN;
    >- spin_unlock_irq(&kcs_bmc->lock);
    >-
    >- return mask;
    >-}
    >-
    >-static ssize_t kcs_bmc_read(struct file *filp, char __user *buf,
    >- size_t count, loff_t *ppos)
    >-{
    >- struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp);
    >- bool data_avail;
    >- size_t data_len;
    >- ssize_t ret;
    >-
    >- if (!(filp->f_flags & O_NONBLOCK))
    >- wait_event_interruptible(kcs_bmc->queue,
    >- kcs_bmc->data_in_avail);
    >-
    >- mutex_lock(&kcs_bmc->mutex);
    >-
    >- spin_lock_irq(&kcs_bmc->lock);
    >- data_avail = kcs_bmc->data_in_avail;
    >- if (data_avail) {
    >- data_len = kcs_bmc->data_in_idx;
    >- memcpy(kcs_bmc->kbuffer, kcs_bmc->data_in, data_len);
    >- }
    >- spin_unlock_irq(&kcs_bmc->lock);
    >-
    >- if (!data_avail) {
    >- ret = -EAGAIN;
    >- goto out_unlock;
    >- }
    >-
    >- if (count < data_len) {
    >- pr_err("channel=%u with too large data : %zu\n",
    >- kcs_bmc->channel, data_len);
    >-
    >- spin_lock_irq(&kcs_bmc->lock);
    >- kcs_force_abort(kcs_bmc);
    >- spin_unlock_irq(&kcs_bmc->lock);
    >-
    >- ret = -EOVERFLOW;
    >- goto out_unlock;
    >- }
    >-
    >- if (copy_to_user(buf, kcs_bmc->kbuffer, data_len)) {
    >- ret = -EFAULT;
    >- goto out_unlock;
    >- }
    >-
    >- ret = data_len;
    >-
    >- spin_lock_irq(&kcs_bmc->lock);
    >- if (kcs_bmc->phase == KCS_PHASE_WRITE_DONE) {
    >- kcs_bmc->phase = KCS_PHASE_WAIT_READ;
    >- kcs_bmc->data_in_avail = false;
    >- kcs_bmc->data_in_idx = 0;
    >- } else {
    >- ret = -EAGAIN;
    >- }
    >- spin_unlock_irq(&kcs_bmc->lock);
    >-
    >-out_unlock:
    >- mutex_unlock(&kcs_bmc->mutex);
    >-
    >- return ret;
    >-}
    >-
    >-static ssize_t kcs_bmc_write(struct file *filp, const char __user *buf,
    >- size_t count, loff_t *ppos)
    >-{
    >- struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp);
    >- ssize_t ret;
    >-
    >- /* a minimum response size '3' : netfn + cmd + ccode */
    >- if (count < 3 || count > KCS_MSG_BUFSIZ)
    >- return -EINVAL;
    >-
    >- mutex_lock(&kcs_bmc->mutex);
    >-
    >- if (copy_from_user(kcs_bmc->kbuffer, buf, count)) {
    >- ret = -EFAULT;
    >- goto out_unlock;
    >- }
    >-
    >- spin_lock_irq(&kcs_bmc->lock);
    >- if (kcs_bmc->phase == KCS_PHASE_WAIT_READ) {
    >- kcs_bmc->phase = KCS_PHASE_READ;
    >- kcs_bmc->data_out_idx = 1;
    >- kcs_bmc->data_out_len = count;
    >- memcpy(kcs_bmc->data_out, kcs_bmc->kbuffer, count);
    >- kcs_bmc_write_data(kcs_bmc, kcs_bmc->data_out[0]);
    >- ret = count;
    >- } else {
    >- ret = -EINVAL;
    >- }
    >- spin_unlock_irq(&kcs_bmc->lock);
    >-
    >-out_unlock:
    >- mutex_unlock(&kcs_bmc->mutex);
    >-
    >- return ret;
    >-}
    >-
    >-static long kcs_bmc_ioctl(struct file *filp, unsigned int cmd,
    >- unsigned long arg)
    >-{
    >- struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp);
    >- long ret = 0;
    >-
    >- spin_lock_irq(&kcs_bmc->lock);
    >-
    >- switch (cmd) {
    >- case IPMI_BMC_IOCTL_SET_SMS_ATN:
    >- kcs_bmc_update_status(kcs_bmc, KCS_STATUS_SMS_ATN, KCS_STATUS_SMS_ATN);
    >- break;
    >-
    >- case IPMI_BMC_IOCTL_CLEAR_SMS_ATN:
    >- kcs_bmc_update_status(kcs_bmc, KCS_STATUS_SMS_ATN, 0);
    >- break;
    >-
    >- case IPMI_BMC_IOCTL_FORCE_ABORT:
    >- kcs_force_abort(kcs_bmc);
    >- break;
    >-
    >- default:
    >- ret = -EINVAL;
    >- break;
    >- }
    >-
    >- spin_unlock_irq(&kcs_bmc->lock);
    >-
    >- return ret;
    >-}
    >-
    >-static int kcs_bmc_release(struct inode *inode, struct file *filp)
    >-{
    >- struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp);
    >-
    >- spin_lock_irq(&kcs_bmc->lock);
    >- kcs_bmc->running = 0;
    >- kcs_force_abort(kcs_bmc);
    >- spin_unlock_irq(&kcs_bmc->lock);
    >-
    >- return 0;
    >-}
    >-
    >-static const struct file_operations kcs_bmc_fops = {
    >- .owner = THIS_MODULE,
    >- .open = kcs_bmc_open,
    >- .read = kcs_bmc_read,
    >- .write = kcs_bmc_write,
    >- .release = kcs_bmc_release,
    >- .poll = kcs_bmc_poll,
    >- .unlocked_ioctl = kcs_bmc_ioctl,
    >-};
    >-
    >+struct kcs_bmc *kcs_bmc_ipmi_alloc(struct device *dev, int sizeof_priv, u32 channel);

    Similarly here.

    > struct kcs_bmc *kcs_bmc_alloc(struct device *dev, int sizeof_priv, u32 channel)
    > {
    >- struct kcs_bmc *kcs_bmc;
    >-
    >- kcs_bmc = devm_kzalloc(dev, sizeof(*kcs_bmc) + sizeof_priv, GFP_KERNEL);
    >- if (!kcs_bmc)
    >- return NULL;
    >-
    >- spin_lock_init(&kcs_bmc->lock);
    >- kcs_bmc->channel = channel;
    >-
    >- mutex_init(&kcs_bmc->mutex);
    >- init_waitqueue_head(&kcs_bmc->queue);
    >-
    >- kcs_bmc->data_in = devm_kmalloc(dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
    >- kcs_bmc->data_out = devm_kmalloc(dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
    >- kcs_bmc->kbuffer = devm_kmalloc(dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
    >-
    >- kcs_bmc->miscdev.minor = MISC_DYNAMIC_MINOR;
    >- kcs_bmc->miscdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s%u",
    >- DEVICE_NAME, channel);
    >- if (!kcs_bmc->data_in || !kcs_bmc->data_out || !kcs_bmc->kbuffer ||
    >- !kcs_bmc->miscdev.name)
    >- return NULL;
    >- kcs_bmc->miscdev.fops = &kcs_bmc_fops;
    >-
    >- return kcs_bmc;
    >+ return kcs_bmc_ipmi_alloc(dev, sizeof_priv, channel);
    > }
    > EXPORT_SYMBOL(kcs_bmc_alloc);
    >
    >diff --git a/drivers/char/ipmi/kcs_bmc.h b/drivers/char/ipmi/kcs_bmc.h
    >index 970f53892f2d..febea0c8deb4 100644
    >--- a/drivers/char/ipmi/kcs_bmc.h
    >+++ b/drivers/char/ipmi/kcs_bmc.h
    >@@ -104,6 +104,12 @@ static inline void *kcs_bmc_priv(struct kcs_bmc *kcs_bmc)
    > }
    >
    > int kcs_bmc_handle_event(struct kcs_bmc *kcs_bmc);
    >-struct kcs_bmc *kcs_bmc_alloc(struct device *dev, int sizeof_priv,
    >- u32 channel);
    >+struct kcs_bmc *kcs_bmc_alloc(struct device *dev, int sizeof_priv, u32 channel);
    >+
    >+u8 kcs_bmc_read_data(struct kcs_bmc *kcs_bmc);
    >+void kcs_bmc_write_data(struct kcs_bmc *kcs_bmc, u8 data);
    >+u8 kcs_bmc_read_status(struct kcs_bmc *kcs_bmc);
    >+void kcs_bmc_write_status(struct kcs_bmc *kcs_bmc, u8 data);
    >+void kcs_bmc_update_status(struct kcs_bmc *kcs_bmc, u8 mask, u8 val);
    >+
    > #endif /* __KCS_BMC_H__ */
    >diff --git a/drivers/char/ipmi/kcs_bmc_cdev_ipmi.c b/drivers/char/ipmi/kcs_bmc_cdev_ipmi.c
    >new file mode 100644
    >index 000000000000..82c77994e481
    >--- /dev/null
    >+++ b/drivers/char/ipmi/kcs_bmc_cdev_ipmi.c
    >@@ -0,0 +1,428 @@
    >+// SPDX-License-Identifier: GPL-2.0
    >+/*
    >+ * Copyright (c) 2015-2018, Intel Corporation.
    >+ */
    >+
    >+#define pr_fmt(fmt) "kcs-bmc: " fmt
    >+
    >+#include <linux/errno.h>
    >+#include <linux/io.h>
    >+#include <linux/ipmi_bmc.h>
    >+#include <linux/module.h>
    >+#include <linux/platform_device.h>
    >+#include <linux/poll.h>
    >+#include <linux/sched.h>
    >+#include <linux/slab.h>
    >+
    >+#include "kcs_bmc.h"
    >+
    >+#define DEVICE_NAME "ipmi-kcs"
    >+
    >+#define KCS_MSG_BUFSIZ 1000
    >+
    >+#define KCS_ZERO_DATA 0
    >+
    >+
    >+/* IPMI 2.0 - Table 9-1, KCS Interface Status Register Bits */
    >+#define KCS_STATUS_STATE(state) (state << 6)
    >+#define KCS_STATUS_STATE_MASK GENMASK(7, 6)
    >+#define KCS_STATUS_CMD_DAT BIT(3)
    >+#define KCS_STATUS_SMS_ATN BIT(2)
    >+#define KCS_STATUS_IBF BIT(1)
    >+#define KCS_STATUS_OBF BIT(0)
    >+
    >+/* IPMI 2.0 - Table 9-2, KCS Interface State Bits */
    >+enum kcs_states {
    >+ IDLE_STATE = 0,
    >+ READ_STATE = 1,
    >+ WRITE_STATE = 2,
    >+ ERROR_STATE = 3,
    >+};
    >+
    >+/* IPMI 2.0 - Table 9-3, KCS Interface Control Codes */
    >+#define KCS_CMD_GET_STATUS_ABORT 0x60
    >+#define KCS_CMD_WRITE_START 0x61
    >+#define KCS_CMD_WRITE_END 0x62
    >+#define KCS_CMD_READ_BYTE 0x68
    >+
    >+static inline void set_state(struct kcs_bmc *kcs_bmc, u8 state)
    >+{
    >+ kcs_bmc_update_status(kcs_bmc, KCS_STATUS_STATE_MASK,
    >+ KCS_STATUS_STATE(state));
    >+}
    >+
    >+static void kcs_bmc_ipmi_force_abort(struct kcs_bmc *kcs_bmc)
    >+{
    >+ set_state(kcs_bmc, ERROR_STATE);
    >+ kcs_bmc_read_data(kcs_bmc);
    >+ kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA);
    >+
    >+ kcs_bmc->phase = KCS_PHASE_ERROR;
    >+ kcs_bmc->data_in_avail = false;
    >+ kcs_bmc->data_in_idx = 0;
    >+}
    >+
    >+static void kcs_bmc_ipmi_handle_data(struct kcs_bmc *kcs_bmc)
    >+{
    >+ u8 data;
    >+
    >+ switch (kcs_bmc->phase) {
    >+ case KCS_PHASE_WRITE_START:
    >+ kcs_bmc->phase = KCS_PHASE_WRITE_DATA;
    >+ fallthrough;
    >+
    >+ case KCS_PHASE_WRITE_DATA:
    >+ if (kcs_bmc->data_in_idx < KCS_MSG_BUFSIZ) {
    >+ set_state(kcs_bmc, WRITE_STATE);
    >+ kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA);
    >+ kcs_bmc->data_in[kcs_bmc->data_in_idx++] =
    >+ kcs_bmc_read_data(kcs_bmc);
    >+ } else {
    >+ kcs_bmc_ipmi_force_abort(kcs_bmc);
    >+ kcs_bmc->error = KCS_LENGTH_ERROR;
    >+ }
    >+ break;
    >+
    >+ case KCS_PHASE_WRITE_END_CMD:
    >+ if (kcs_bmc->data_in_idx < KCS_MSG_BUFSIZ) {
    >+ set_state(kcs_bmc, READ_STATE);
    >+ kcs_bmc->data_in[kcs_bmc->data_in_idx++] =
    >+ kcs_bmc_read_data(kcs_bmc);
    >+ kcs_bmc->phase = KCS_PHASE_WRITE_DONE;
    >+ kcs_bmc->data_in_avail = true;
    >+ wake_up_interruptible(&kcs_bmc->queue);
    >+ } else {
    >+ kcs_bmc_ipmi_force_abort(kcs_bmc);
    >+ kcs_bmc->error = KCS_LENGTH_ERROR;
    >+ }
    >+ break;
    >+
    >+ case KCS_PHASE_READ:
    >+ if (kcs_bmc->data_out_idx == kcs_bmc->data_out_len)
    >+ set_state(kcs_bmc, IDLE_STATE);
    >+
    >+ data = kcs_bmc_read_data(kcs_bmc);
    >+ if (data != KCS_CMD_READ_BYTE) {
    >+ set_state(kcs_bmc, ERROR_STATE);
    >+ kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA);
    >+ break;
    >+ }
    >+
    >+ if (kcs_bmc->data_out_idx == kcs_bmc->data_out_len) {
    >+ kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA);
    >+ kcs_bmc->phase = KCS_PHASE_IDLE;
    >+ break;
    >+ }
    >+
    >+ kcs_bmc_write_data(kcs_bmc,
    >+ kcs_bmc->data_out[kcs_bmc->data_out_idx++]);
    >+ break;
    >+
    >+ case KCS_PHASE_ABORT_ERROR1:
    >+ set_state(kcs_bmc, READ_STATE);
    >+ kcs_bmc_read_data(kcs_bmc);
    >+ kcs_bmc_write_data(kcs_bmc, kcs_bmc->error);
    >+ kcs_bmc->phase = KCS_PHASE_ABORT_ERROR2;
    >+ break;
    >+
    >+ case KCS_PHASE_ABORT_ERROR2:
    >+ set_state(kcs_bmc, IDLE_STATE);
    >+ kcs_bmc_read_data(kcs_bmc);
    >+ kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA);
    >+ kcs_bmc->phase = KCS_PHASE_IDLE;
    >+ break;
    >+
    >+ default:
    >+ kcs_bmc_ipmi_force_abort(kcs_bmc);
    >+ break;
    >+ }
    >+}
    >+
    >+static void kcs_bmc_ipmi_handle_cmd(struct kcs_bmc *kcs_bmc)
    >+{
    >+ u8 cmd;
    >+
    >+ set_state(kcs_bmc, WRITE_STATE);
    >+ kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA);
    >+
    >+ cmd = kcs_bmc_read_data(kcs_bmc);
    >+ switch (cmd) {
    >+ case KCS_CMD_WRITE_START:
    >+ kcs_bmc->phase = KCS_PHASE_WRITE_START;
    >+ kcs_bmc->error = KCS_NO_ERROR;
    >+ kcs_bmc->data_in_avail = false;
    >+ kcs_bmc->data_in_idx = 0;
    >+ break;
    >+
    >+ case KCS_CMD_WRITE_END:
    >+ if (kcs_bmc->phase != KCS_PHASE_WRITE_DATA) {
    >+ kcs_bmc_ipmi_force_abort(kcs_bmc);
    >+ break;
    >+ }
    >+
    >+ kcs_bmc->phase = KCS_PHASE_WRITE_END_CMD;
    >+ break;
    >+
    >+ case KCS_CMD_GET_STATUS_ABORT:
    >+ if (kcs_bmc->error == KCS_NO_ERROR)
    >+ kcs_bmc->error = KCS_ABORTED_BY_COMMAND;
    >+
    >+ kcs_bmc->phase = KCS_PHASE_ABORT_ERROR1;
    >+ kcs_bmc->data_in_avail = false;
    >+ kcs_bmc->data_in_idx = 0;
    >+ break;
    >+
    >+ default:
    >+ kcs_bmc_ipmi_force_abort(kcs_bmc);
    >+ kcs_bmc->error = KCS_ILLEGAL_CONTROL_CODE;
    >+ break;
    >+ }
    >+}
    >+
    >+int kcs_bmc_ipmi_event(struct kcs_bmc *kcs_bmc);

    Another instance...

    >+int kcs_bmc_ipmi_event(struct kcs_bmc *kcs_bmc)
    >+{
    >+ unsigned long flags;
    >+ int ret = -ENODATA;
    >+ u8 status;
    >+
    >+ spin_lock_irqsave(&kcs_bmc->lock, flags);
    >+
    >+ status = kcs_bmc_read_status(kcs_bmc);
    >+ if (status & KCS_STATUS_IBF) {
    >+ if (!kcs_bmc->running)
    >+ kcs_bmc_ipmi_force_abort(kcs_bmc);
    >+ else if (status & KCS_STATUS_CMD_DAT)
    >+ kcs_bmc_ipmi_handle_cmd(kcs_bmc);
    >+ else
    >+ kcs_bmc_ipmi_handle_data(kcs_bmc);
    >+
    >+ ret = 0;
    >+ }
    >+
    >+ spin_unlock_irqrestore(&kcs_bmc->lock, flags);
    >+
    >+ return ret;
    >+}
    >+EXPORT_SYMBOL(kcs_bmc_ipmi_event);
    >+
    >+static inline struct kcs_bmc *to_kcs_bmc(struct file *filp)
    >+{
    >+ return container_of(filp->private_data, struct kcs_bmc, miscdev);
    >+}
    >+
    >+static int kcs_bmc_ipmi_open(struct inode *inode, struct file *filp)
    >+{
    >+ struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp);
    >+ int ret = 0;
    >+
    >+ spin_lock_irq(&kcs_bmc->lock);
    >+ if (!kcs_bmc->running)
    >+ kcs_bmc->running = 1;
    >+ else
    >+ ret = -EBUSY;
    >+ spin_unlock_irq(&kcs_bmc->lock);
    >+
    >+ return ret;
    >+}
    >+
    >+static __poll_t kcs_bmc_ipmi_poll(struct file *filp, poll_table *wait)
    >+{
    >+ struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp);
    >+ __poll_t mask = 0;
    >+
    >+ poll_wait(filp, &kcs_bmc->queue, wait);
    >+
    >+ spin_lock_irq(&kcs_bmc->lock);
    >+ if (kcs_bmc->data_in_avail)
    >+ mask |= EPOLLIN;
    >+ spin_unlock_irq(&kcs_bmc->lock);
    >+
    >+ return mask;
    >+}
    >+
    >+static ssize_t kcs_bmc_ipmi_read(struct file *filp, char __user *buf,
    >+ size_t count, loff_t *ppos)
    >+{
    >+ struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp);
    >+ bool data_avail;
    >+ size_t data_len;
    >+ ssize_t ret;
    >+
    >+ if (!(filp->f_flags & O_NONBLOCK))
    >+ wait_event_interruptible(kcs_bmc->queue,
    >+ kcs_bmc->data_in_avail);
    >+
    >+ mutex_lock(&kcs_bmc->mutex);
    >+
    >+ spin_lock_irq(&kcs_bmc->lock);
    >+ data_avail = kcs_bmc->data_in_avail;
    >+ if (data_avail) {
    >+ data_len = kcs_bmc->data_in_idx;
    >+ memcpy(kcs_bmc->kbuffer, kcs_bmc->data_in, data_len);
    >+ }
    >+ spin_unlock_irq(&kcs_bmc->lock);
    >+
    >+ if (!data_avail) {
    >+ ret = -EAGAIN;
    >+ goto out_unlock;
    >+ }
    >+
    >+ if (count < data_len) {
    >+ pr_err("channel=%u with too large data : %zu\n",
    >+ kcs_bmc->channel, data_len);
    >+
    >+ spin_lock_irq(&kcs_bmc->lock);
    >+ kcs_bmc_ipmi_force_abort(kcs_bmc);
    >+ spin_unlock_irq(&kcs_bmc->lock);
    >+
    >+ ret = -EOVERFLOW;
    >+ goto out_unlock;
    >+ }
    >+
    >+ if (copy_to_user(buf, kcs_bmc->kbuffer, data_len)) {
    >+ ret = -EFAULT;
    >+ goto out_unlock;
    >+ }
    >+
    >+ ret = data_len;
    >+
    >+ spin_lock_irq(&kcs_bmc->lock);
    >+ if (kcs_bmc->phase == KCS_PHASE_WRITE_DONE) {
    >+ kcs_bmc->phase = KCS_PHASE_WAIT_READ;
    >+ kcs_bmc->data_in_avail = false;
    >+ kcs_bmc->data_in_idx = 0;
    >+ } else {
    >+ ret = -EAGAIN;
    >+ }
    >+ spin_unlock_irq(&kcs_bmc->lock);
    >+
    >+out_unlock:
    >+ mutex_unlock(&kcs_bmc->mutex);
    >+
    >+ return ret;
    >+}
    >+
    >+static ssize_t kcs_bmc_ipmi_write(struct file *filp, const char __user *buf,
    >+ size_t count, loff_t *ppos)
    >+{
    >+ struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp);
    >+ ssize_t ret;
    >+
    >+ /* a minimum response size '3' : netfn + cmd + ccode */
    >+ if (count < 3 || count > KCS_MSG_BUFSIZ)
    >+ return -EINVAL;
    >+
    >+ mutex_lock(&kcs_bmc->mutex);
    >+
    >+ if (copy_from_user(kcs_bmc->kbuffer, buf, count)) {
    >+ ret = -EFAULT;
    >+ goto out_unlock;
    >+ }
    >+
    >+ spin_lock_irq(&kcs_bmc->lock);
    >+ if (kcs_bmc->phase == KCS_PHASE_WAIT_READ) {
    >+ kcs_bmc->phase = KCS_PHASE_READ;
    >+ kcs_bmc->data_out_idx = 1;
    >+ kcs_bmc->data_out_len = count;
    >+ memcpy(kcs_bmc->data_out, kcs_bmc->kbuffer, count);
    >+ kcs_bmc_write_data(kcs_bmc, kcs_bmc->data_out[0]);
    >+ ret = count;
    >+ } else {
    >+ ret = -EINVAL;
    >+ }
    >+ spin_unlock_irq(&kcs_bmc->lock);
    >+
    >+out_unlock:
    >+ mutex_unlock(&kcs_bmc->mutex);
    >+
    >+ return ret;
    >+}
    >+
    >+static long kcs_bmc_ipmi_ioctl(struct file *filp, unsigned int cmd,
    >+ unsigned long arg)
    >+{
    >+ struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp);
    >+ long ret = 0;
    >+
    >+ spin_lock_irq(&kcs_bmc->lock);
    >+
    >+ switch (cmd) {
    >+ case IPMI_BMC_IOCTL_SET_SMS_ATN:
    >+ kcs_bmc_update_status(kcs_bmc, KCS_STATUS_SMS_ATN, KCS_STATUS_SMS_ATN);
    >+ break;
    >+
    >+ case IPMI_BMC_IOCTL_CLEAR_SMS_ATN:
    >+ kcs_bmc_update_status(kcs_bmc, KCS_STATUS_SMS_ATN, 0);
    >+ break;
    >+
    >+ case IPMI_BMC_IOCTL_FORCE_ABORT:
    >+ kcs_bmc_ipmi_force_abort(kcs_bmc);
    >+ break;
    >+
    >+ default:
    >+ ret = -EINVAL;
    >+ break;
    >+ }
    >+
    >+ spin_unlock_irq(&kcs_bmc->lock);
    >+
    >+ return ret;
    >+}
    >+
    >+static int kcs_bmc_ipmi_release(struct inode *inode, struct file *filp)
    >+{
    >+ struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp);
    >+
    >+ spin_lock_irq(&kcs_bmc->lock);
    >+ kcs_bmc->running = 0;
    >+ kcs_bmc_ipmi_force_abort(kcs_bmc);
    >+ spin_unlock_irq(&kcs_bmc->lock);
    >+
    >+ return 0;
    >+}
    >+
    >+static const struct file_operations kcs_bmc_fops = {
    >+ .owner = THIS_MODULE,
    >+ .open = kcs_bmc_ipmi_open,
    >+ .read = kcs_bmc_ipmi_read,
    >+ .write = kcs_bmc_ipmi_write,
    >+ .release = kcs_bmc_ipmi_release,
    >+ .poll = kcs_bmc_ipmi_poll,
    >+ .unlocked_ioctl = kcs_bmc_ipmi_ioctl,
    >+};
    >+
    >+struct kcs_bmc *kcs_bmc_ipmi_alloc(struct device *dev, int sizeof_priv, u32 channel);

    Here too.

    >+struct kcs_bmc *kcs_bmc_ipmi_alloc(struct device *dev, int sizeof_priv, u32 channel)
    >+{
    >+ struct kcs_bmc *kcs_bmc;
    >+
    >+ kcs_bmc = devm_kzalloc(dev, sizeof(*kcs_bmc) + sizeof_priv, GFP_KERNEL);
    >+ if (!kcs_bmc)
    >+ return NULL;
    >+
    >+ spin_lock_init(&kcs_bmc->lock);
    >+ kcs_bmc->channel = channel;
    >+
    >+ mutex_init(&kcs_bmc->mutex);
    >+ init_waitqueue_head(&kcs_bmc->queue);
    >+
    >+ kcs_bmc->data_in = devm_kmalloc(dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
    >+ kcs_bmc->data_out = devm_kmalloc(dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
    >+ kcs_bmc->kbuffer = devm_kmalloc(dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
    >+
    >+ kcs_bmc->miscdev.minor = MISC_DYNAMIC_MINOR;
    >+ kcs_bmc->miscdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s%u",
    >+ DEVICE_NAME, channel);
    >+ if (!kcs_bmc->data_in || !kcs_bmc->data_out || !kcs_bmc->kbuffer ||
    >+ !kcs_bmc->miscdev.name)
    >+ return NULL;
    >+ kcs_bmc->miscdev.fops = &kcs_bmc_fops;
    >+
    >+ return kcs_bmc;
    >+}
    >+EXPORT_SYMBOL(kcs_bmc_ipmi_alloc);
    >+
    >+MODULE_LICENSE("GPL v2");
    >+MODULE_AUTHOR("Haiyue Wang <haiyue.wang@linux.intel.com>");
    >+MODULE_DESCRIPTION("KCS BMC to handle the IPMI request from system software");
    >--
    >2.27.0
    >
    \
     
     \ /
      Last update: 2021-04-09 05:59    [W:3.467 / U:0.056 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site