drivers/char/ipmi/kcs_bmc_cdev_ipmi.c

Source file repositories/reference/linux-study-clean/drivers/char/ipmi/kcs_bmc_cdev_ipmi.c

File Facts

System
Linux kernel
Corpus path
drivers/char/ipmi/kcs_bmc_cdev_ipmi.c
Extension
.c
Size
13583 bytes
Lines
569
Domain
Driver Families
Bucket
drivers/char
Inferred role
Driver Families: operation-table or driver-model contract
Status
pattern implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

static const struct file_operations kcs_bmc_ipmi_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,
};

static DEFINE_SPINLOCK(kcs_bmc_ipmi_instances_lock);
static LIST_HEAD(kcs_bmc_ipmi_instances);

static int kcs_bmc_ipmi_add_device(struct kcs_bmc_device *kcs_bmc)
{
	struct kcs_bmc_ipmi *priv;
	int rc;

	priv = devm_kzalloc(kcs_bmc->dev, sizeof(*priv), GFP_KERNEL);
	if (!priv)
		return -ENOMEM;

	spin_lock_init(&priv->lock);
	mutex_init(&priv->mutex);

	init_waitqueue_head(&priv->queue);

	priv->client.dev = kcs_bmc;
	priv->client.ops = &kcs_bmc_ipmi_client_ops;
	priv->data_in = devm_kmalloc(kcs_bmc->dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
	priv->data_out = devm_kmalloc(kcs_bmc->dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
	priv->kbuffer = devm_kmalloc(kcs_bmc->dev, KCS_MSG_BUFSIZ, GFP_KERNEL);

	priv->miscdev.minor = MISC_DYNAMIC_MINOR;
	priv->miscdev.name = devm_kasprintf(kcs_bmc->dev, GFP_KERNEL, "%s%u", DEVICE_NAME,
					   kcs_bmc->channel);
	if (!priv->data_in || !priv->data_out || !priv->kbuffer || !priv->miscdev.name)
		return -EINVAL;

	priv->miscdev.fops = &kcs_bmc_ipmi_fops;

	rc = misc_register(&priv->miscdev);
	if (rc) {
		dev_err(kcs_bmc->dev, "Unable to register device: %d\n", rc);
		return rc;
	}

	spin_lock_irq(&kcs_bmc_ipmi_instances_lock);
	list_add(&priv->entry, &kcs_bmc_ipmi_instances);
	spin_unlock_irq(&kcs_bmc_ipmi_instances_lock);

	dev_info(kcs_bmc->dev, "Initialised IPMI client for channel %d", kcs_bmc->channel);

	return 0;
}

static int kcs_bmc_ipmi_remove_device(struct kcs_bmc_device *kcs_bmc)
{
	struct kcs_bmc_ipmi *priv = NULL, *pos;

	spin_lock_irq(&kcs_bmc_ipmi_instances_lock);
	list_for_each_entry(pos, &kcs_bmc_ipmi_instances, entry) {
		if (pos->client.dev == kcs_bmc) {
			priv = pos;
			list_del(&pos->entry);
			break;
		}
	}
	spin_unlock_irq(&kcs_bmc_ipmi_instances_lock);

	if (!priv)
		return -ENODEV;

	misc_deregister(&priv->miscdev);
	kcs_bmc_disable_device(priv->client.dev, &priv->client);
	devm_kfree(kcs_bmc->dev, priv->kbuffer);
	devm_kfree(kcs_bmc->dev, priv->data_out);
	devm_kfree(kcs_bmc->dev, priv->data_in);
	devm_kfree(kcs_bmc->dev, priv);

	return 0;
}

static const struct kcs_bmc_driver_ops kcs_bmc_ipmi_driver_ops = {
	.add_device = kcs_bmc_ipmi_add_device,
	.remove_device = kcs_bmc_ipmi_remove_device,
};

static struct kcs_bmc_driver kcs_bmc_ipmi_driver = {
	.ops = &kcs_bmc_ipmi_driver_ops,

Annotation

Implementation Notes