]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
platform/chrome: cros_ec_chardev: Move data to chardev_pdata
authorTzung-Bi Shih <tzungbi@kernel.org>
Mon, 25 May 2026 05:26:52 +0000 (05:26 +0000)
committerTzung-Bi Shih <tzungbi@kernel.org>
Thu, 28 May 2026 02:17:30 +0000 (02:17 +0000)
Move `ec_dev` and `cmd_offset` from `chardev_priv` to `chardev_pdata` as
they are per-device properties but not per-open-file properties.

Hold a reference to `chardev_pdata` for each open file to ensure the
data remains valid even if the underlying platform device is removed.

Reviewed-by: Jason Gunthorpe <jgg@nvidia.com>
Link: https://lore.kernel.org/r/20260525052654.4076429-3-tzungbi@kernel.org
Signed-off-by: Tzung-Bi Shih <tzungbi@kernel.org>
drivers/platform/chrome/cros_ec_chardev.c

index e7012e44a006ac9cf8d27a85eda11ca9df468139..352d61a2f3c6e2b4d1e09b44f14fef02c100dae8 100644 (file)
@@ -38,6 +38,8 @@
 struct chardev_pdata {
        struct miscdevice misc;
        struct kref kref;
+       struct cros_ec_device *ec_dev;
+       u16 cmd_offset;
 };
 
 static void chardev_pdata_release(struct kref *kref)
@@ -48,13 +50,12 @@ static void chardev_pdata_release(struct kref *kref)
 }
 
 struct chardev_priv {
-       struct cros_ec_device *ec_dev;
        struct notifier_block notifier;
        wait_queue_head_t wait_event;
        unsigned long event_mask;
        struct list_head events;
        size_t event_len;
-       u16 cmd_offset;
+       struct chardev_pdata *pdata;
 };
 
 struct ec_event {
@@ -77,10 +78,10 @@ static int ec_get_version(struct chardev_priv *priv, char *str, int maxlen)
        if (!msg)
                return -ENOMEM;
 
-       msg->command = EC_CMD_GET_VERSION + priv->cmd_offset;
+       msg->command = EC_CMD_GET_VERSION + priv->pdata->cmd_offset;
        msg->insize = sizeof(*resp);
 
-       ret = cros_ec_cmd_xfer_status(priv->ec_dev, msg);
+       ret = cros_ec_cmd_xfer_status(priv->pdata->ec_dev, msg);
        if (ret < 0) {
                snprintf(str, maxlen,
                         "Unknown EC version, returned error: %d\n",
@@ -108,7 +109,7 @@ static int cros_ec_chardev_mkbp_event(struct notifier_block *nb,
 {
        struct chardev_priv *priv = container_of(nb, struct chardev_priv,
                                                 notifier);
-       struct cros_ec_device *ec_dev = priv->ec_dev;
+       struct cros_ec_device *ec_dev = priv->pdata->ec_dev;
        struct ec_event *event;
        unsigned long event_bit = 1 << ec_dev->event_data.event_type;
        int total_size = sizeof(*event) + ec_dev->event_size;
@@ -173,8 +174,7 @@ out:
 static int cros_ec_chardev_open(struct inode *inode, struct file *filp)
 {
        struct miscdevice *mdev = filp->private_data;
-       struct cros_ec_dev *ec = dev_get_drvdata(mdev->parent);
-       struct cros_ec_device *ec_dev = ec->ec_dev;
+       struct chardev_pdata *pdata = container_of(mdev, typeof(*pdata), misc);
        struct chardev_priv *priv;
        int ret;
 
@@ -182,18 +182,20 @@ static int cros_ec_chardev_open(struct inode *inode, struct file *filp)
        if (!priv)
                return -ENOMEM;
 
-       priv->ec_dev = ec_dev;
-       priv->cmd_offset = ec->cmd_offset;
+       priv->pdata = pdata;
+       kref_get(&pdata->kref);
        filp->private_data = priv;
        INIT_LIST_HEAD(&priv->events);
        init_waitqueue_head(&priv->wait_event);
        nonseekable_open(inode, filp);
 
        priv->notifier.notifier_call = cros_ec_chardev_mkbp_event;
-       ret = blocking_notifier_chain_register(&ec_dev->event_notifier,
+       ret = blocking_notifier_chain_register(&pdata->ec_dev->event_notifier,
                                               &priv->notifier);
        if (ret) {
-               dev_err(ec_dev->dev, "failed to register event notifier\n");
+               dev_err(pdata->ec_dev->dev,
+                       "failed to register event notifier\n");
+               kref_put(&priv->pdata->kref, chardev_pdata_release);
                kfree(priv);
        }
 
@@ -267,11 +269,11 @@ static ssize_t cros_ec_chardev_read(struct file *filp, char __user *buffer,
 static int cros_ec_chardev_release(struct inode *inode, struct file *filp)
 {
        struct chardev_priv *priv = filp->private_data;
-       struct cros_ec_device *ec_dev = priv->ec_dev;
        struct ec_event *event, *e;
 
-       blocking_notifier_chain_unregister(&ec_dev->event_notifier,
+       blocking_notifier_chain_unregister(&priv->pdata->ec_dev->event_notifier,
                                           &priv->notifier);
+       kref_put(&priv->pdata->kref, chardev_pdata_release);
 
        list_for_each_entry_safe(event, e, &priv->events, node) {
                list_del(&event->node);
@@ -314,8 +316,8 @@ static long cros_ec_chardev_ioctl_xcmd(struct chardev_priv *priv, void __user *a
                goto exit;
        }
 
-       s_cmd->command += priv->cmd_offset;
-       ret = cros_ec_cmd_xfer(priv->ec_dev, s_cmd);
+       s_cmd->command += priv->pdata->cmd_offset;
+       ret = cros_ec_cmd_xfer(priv->pdata->ec_dev, s_cmd);
        /* Only copy data to userland if data was received. */
        if (ret < 0)
                goto exit;
@@ -329,7 +331,7 @@ exit:
 
 static long cros_ec_chardev_ioctl_readmem(struct chardev_priv *priv, void __user *arg)
 {
-       struct cros_ec_device *ec_dev = priv->ec_dev;
+       struct cros_ec_device *ec_dev = priv->pdata->ec_dev;
        struct cros_ec_readmem s_mem = { };
        long num;
 
@@ -399,6 +401,8 @@ static int cros_ec_chardev_probe(struct platform_device *pdev)
 
        platform_set_drvdata(pdev, pdata);
        kref_init(&pdata->kref);
+       pdata->ec_dev = ec->ec_dev;
+       pdata->cmd_offset = ec->cmd_offset;
 
        pdata->misc.minor = MISC_DYNAMIC_MINOR;
        pdata->misc.fops = &chardev_fops;