drivers/platform/chrome/cros_ec_chardev.c

Source file repositories/reference/linux-study-clean/drivers/platform/chrome/cros_ec_chardev.c

File Facts

System
Linux kernel
Corpus path
drivers/platform/chrome/cros_ec_chardev.c
Extension
.c
Size
12445 bytes
Lines
507
Domain
Driver Families
Bucket
drivers/platform
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 chardev_fops = {
	.open		= cros_ec_chardev_open,
	.poll		= cros_ec_chardev_poll,
	.read		= cros_ec_chardev_read,
	.release	= cros_ec_chardev_release,
	.unlocked_ioctl	= cros_ec_chardev_ioctl,
#ifdef CONFIG_COMPAT
	.compat_ioctl	= cros_ec_chardev_ioctl,
#endif
};

static int cros_ec_chardev_probe(struct platform_device *pdev)
{
	struct cros_ec_dev *ec = dev_get_drvdata(pdev->dev.parent);
	struct cros_ec_platform *ec_platform = dev_get_platdata(ec->dev);
	struct chardev_pdata *pdata;
	int ret;

	pdata = kzalloc_obj(*pdata);
	if (!pdata)
		return -ENOMEM;

	platform_set_drvdata(pdev, pdata);
	kref_init(&pdata->kref);
	init_rwsem(&pdata->ec_dev_sem);
	pdata->ec_dev = ec->ec_dev;
	pdata->cmd_offset = ec->cmd_offset;
	BLOCKING_INIT_NOTIFIER_HEAD(&pdata->subscribers);
	pdata->relay.notifier_call = cros_ec_chardev_relay_event;
	ret = blocking_notifier_chain_register(&pdata->ec_dev->event_notifier,
					       &pdata->relay);
	if (ret) {
		dev_err(&pdev->dev, "failed to register event notifier\n");
		goto err_put_pdata;
	}

	pdata->misc.minor = MISC_DYNAMIC_MINOR;
	pdata->misc.fops = &chardev_fops;
	pdata->misc.name = ec_platform->ec_name;
	pdata->misc.parent = pdev->dev.parent;

	ret = misc_register(&pdata->misc);
	if (ret) {
		dev_err(&pdev->dev, "failed to register misc device\n");
		goto err_unregister_notifier;
	}

	return 0;
err_unregister_notifier:
	blocking_notifier_chain_unregister(&pdata->ec_dev->event_notifier,
					   &pdata->relay);
err_put_pdata:
	kref_put(&pdata->kref, chardev_pdata_release);
	return ret;
}

static void cros_ec_chardev_remove(struct platform_device *pdev)
{
	struct chardev_pdata *pdata = platform_get_drvdata(pdev);
	struct cros_ec_device *ec_dev = pdata->ec_dev;

	/* stop new fops from being created */
	misc_deregister(&pdata->misc);
	/* stop existing fops from running */
	scoped_guard(rwsem_write, &pdata->ec_dev_sem)
		pdata->ec_dev = NULL;

	blocking_notifier_chain_unregister(&ec_dev->event_notifier,
					   &pdata->relay);
	kref_put(&pdata->kref, chardev_pdata_release);
}

static const struct platform_device_id cros_ec_chardev_id[] = {
	{ DRV_NAME, 0 },
	{}
};
MODULE_DEVICE_TABLE(platform, cros_ec_chardev_id);

static struct platform_driver cros_ec_chardev_driver = {
	.driver = {
		.name = DRV_NAME,
	},
	.probe = cros_ec_chardev_probe,
	.remove = cros_ec_chardev_remove,
	.id_table = cros_ec_chardev_id,
};

module_platform_driver(cros_ec_chardev_driver);

MODULE_AUTHOR("Enric Balletbo i Serra <enric.balletbo@collabora.com>");

Annotation

Implementation Notes