Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Miscellaneous character driver for ChromeOS Embedded Controller
0004  *
0005  * Copyright 2014 Google, Inc.
0006  * Copyright 2019 Google LLC
0007  *
0008  * This file is a rework and part of the code is ported from
0009  * drivers/mfd/cros_ec_dev.c that was originally written by
0010  * Bill Richardson.
0011  */
0012 
0013 #include <linux/init.h>
0014 #include <linux/device.h>
0015 #include <linux/fs.h>
0016 #include <linux/miscdevice.h>
0017 #include <linux/module.h>
0018 #include <linux/notifier.h>
0019 #include <linux/platform_data/cros_ec_chardev.h>
0020 #include <linux/platform_data/cros_ec_commands.h>
0021 #include <linux/platform_data/cros_ec_proto.h>
0022 #include <linux/platform_device.h>
0023 #include <linux/poll.h>
0024 #include <linux/slab.h>
0025 #include <linux/types.h>
0026 #include <linux/uaccess.h>
0027 
0028 #define DRV_NAME        "cros-ec-chardev"
0029 
0030 /* Arbitrary bounded size for the event queue */
0031 #define CROS_MAX_EVENT_LEN  PAGE_SIZE
0032 
0033 struct chardev_data {
0034     struct cros_ec_dev *ec_dev;
0035     struct miscdevice misc;
0036 };
0037 
0038 struct chardev_priv {
0039     struct cros_ec_dev *ec_dev;
0040     struct notifier_block notifier;
0041     wait_queue_head_t wait_event;
0042     unsigned long event_mask;
0043     struct list_head events;
0044     size_t event_len;
0045 };
0046 
0047 struct ec_event {
0048     struct list_head node;
0049     size_t size;
0050     u8 event_type;
0051     u8 data[];
0052 };
0053 
0054 static int ec_get_version(struct cros_ec_dev *ec, char *str, int maxlen)
0055 {
0056     static const char * const current_image_name[] = {
0057         "unknown", "read-only", "read-write", "invalid",
0058     };
0059     struct ec_response_get_version *resp;
0060     struct cros_ec_command *msg;
0061     int ret;
0062 
0063     msg = kzalloc(sizeof(*msg) + sizeof(*resp), GFP_KERNEL);
0064     if (!msg)
0065         return -ENOMEM;
0066 
0067     msg->command = EC_CMD_GET_VERSION + ec->cmd_offset;
0068     msg->insize = sizeof(*resp);
0069 
0070     ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
0071     if (ret < 0) {
0072         snprintf(str, maxlen,
0073              "Unknown EC version, returned error: %d\n",
0074              msg->result);
0075         goto exit;
0076     }
0077 
0078     resp = (struct ec_response_get_version *)msg->data;
0079     if (resp->current_image >= ARRAY_SIZE(current_image_name))
0080         resp->current_image = 3; /* invalid */
0081 
0082     snprintf(str, maxlen, "%s\n%s\n%s\n%s\n", CROS_EC_DEV_VERSION,
0083          resp->version_string_ro, resp->version_string_rw,
0084          current_image_name[resp->current_image]);
0085 
0086     ret = 0;
0087 exit:
0088     kfree(msg);
0089     return ret;
0090 }
0091 
0092 static int cros_ec_chardev_mkbp_event(struct notifier_block *nb,
0093                       unsigned long queued_during_suspend,
0094                       void *_notify)
0095 {
0096     struct chardev_priv *priv = container_of(nb, struct chardev_priv,
0097                          notifier);
0098     struct cros_ec_device *ec_dev = priv->ec_dev->ec_dev;
0099     struct ec_event *event;
0100     unsigned long event_bit = 1 << ec_dev->event_data.event_type;
0101     int total_size = sizeof(*event) + ec_dev->event_size;
0102 
0103     if (!(event_bit & priv->event_mask) ||
0104         (priv->event_len + total_size) > CROS_MAX_EVENT_LEN)
0105         return NOTIFY_DONE;
0106 
0107     event = kzalloc(total_size, GFP_KERNEL);
0108     if (!event)
0109         return NOTIFY_DONE;
0110 
0111     event->size = ec_dev->event_size;
0112     event->event_type = ec_dev->event_data.event_type;
0113     memcpy(event->data, &ec_dev->event_data.data, ec_dev->event_size);
0114 
0115     spin_lock(&priv->wait_event.lock);
0116     list_add_tail(&event->node, &priv->events);
0117     priv->event_len += total_size;
0118     wake_up_locked(&priv->wait_event);
0119     spin_unlock(&priv->wait_event.lock);
0120 
0121     return NOTIFY_OK;
0122 }
0123 
0124 static struct ec_event *cros_ec_chardev_fetch_event(struct chardev_priv *priv,
0125                             bool fetch, bool block)
0126 {
0127     struct ec_event *event;
0128     int err;
0129 
0130     spin_lock(&priv->wait_event.lock);
0131     if (!block && list_empty(&priv->events)) {
0132         event = ERR_PTR(-EWOULDBLOCK);
0133         goto out;
0134     }
0135 
0136     if (!fetch) {
0137         event = NULL;
0138         goto out;
0139     }
0140 
0141     err = wait_event_interruptible_locked(priv->wait_event,
0142                           !list_empty(&priv->events));
0143     if (err) {
0144         event = ERR_PTR(err);
0145         goto out;
0146     }
0147 
0148     event = list_first_entry(&priv->events, struct ec_event, node);
0149     list_del(&event->node);
0150     priv->event_len -= sizeof(*event) + event->size;
0151 
0152 out:
0153     spin_unlock(&priv->wait_event.lock);
0154     return event;
0155 }
0156 
0157 /*
0158  * Device file ops
0159  */
0160 static int cros_ec_chardev_open(struct inode *inode, struct file *filp)
0161 {
0162     struct miscdevice *mdev = filp->private_data;
0163     struct cros_ec_dev *ec_dev = dev_get_drvdata(mdev->parent);
0164     struct chardev_priv *priv;
0165     int ret;
0166 
0167     priv = kzalloc(sizeof(*priv), GFP_KERNEL);
0168     if (!priv)
0169         return -ENOMEM;
0170 
0171     priv->ec_dev = ec_dev;
0172     filp->private_data = priv;
0173     INIT_LIST_HEAD(&priv->events);
0174     init_waitqueue_head(&priv->wait_event);
0175     nonseekable_open(inode, filp);
0176 
0177     priv->notifier.notifier_call = cros_ec_chardev_mkbp_event;
0178     ret = blocking_notifier_chain_register(&ec_dev->ec_dev->event_notifier,
0179                            &priv->notifier);
0180     if (ret) {
0181         dev_err(ec_dev->dev, "failed to register event notifier\n");
0182         kfree(priv);
0183     }
0184 
0185     return ret;
0186 }
0187 
0188 static __poll_t cros_ec_chardev_poll(struct file *filp, poll_table *wait)
0189 {
0190     struct chardev_priv *priv = filp->private_data;
0191 
0192     poll_wait(filp, &priv->wait_event, wait);
0193 
0194     if (list_empty(&priv->events))
0195         return 0;
0196 
0197     return EPOLLIN | EPOLLRDNORM;
0198 }
0199 
0200 static ssize_t cros_ec_chardev_read(struct file *filp, char __user *buffer,
0201                      size_t length, loff_t *offset)
0202 {
0203     char msg[sizeof(struct ec_response_get_version) +
0204          sizeof(CROS_EC_DEV_VERSION)];
0205     struct chardev_priv *priv = filp->private_data;
0206     struct cros_ec_dev *ec_dev = priv->ec_dev;
0207     size_t count;
0208     int ret;
0209 
0210     if (priv->event_mask) { /* queued MKBP event */
0211         struct ec_event *event;
0212 
0213         event = cros_ec_chardev_fetch_event(priv, length != 0,
0214                         !(filp->f_flags & O_NONBLOCK));
0215         if (IS_ERR(event))
0216             return PTR_ERR(event);
0217         /*
0218          * length == 0 is special - no IO is done but we check
0219          * for error conditions.
0220          */
0221         if (length == 0)
0222             return 0;
0223 
0224         /* The event is 1 byte of type plus the payload */
0225         count = min(length, event->size + 1);
0226         ret = copy_to_user(buffer, &event->event_type, count);
0227         kfree(event);
0228         if (ret) /* the copy failed */
0229             return -EFAULT;
0230         *offset = count;
0231         return count;
0232     }
0233 
0234     /*
0235      * Legacy behavior if no event mask is defined
0236      */
0237     if (*offset != 0)
0238         return 0;
0239 
0240     ret = ec_get_version(ec_dev, msg, sizeof(msg));
0241     if (ret)
0242         return ret;
0243 
0244     count = min(length, strlen(msg));
0245 
0246     if (copy_to_user(buffer, msg, count))
0247         return -EFAULT;
0248 
0249     *offset = count;
0250     return count;
0251 }
0252 
0253 static int cros_ec_chardev_release(struct inode *inode, struct file *filp)
0254 {
0255     struct chardev_priv *priv = filp->private_data;
0256     struct cros_ec_dev *ec_dev = priv->ec_dev;
0257     struct ec_event *event, *e;
0258 
0259     blocking_notifier_chain_unregister(&ec_dev->ec_dev->event_notifier,
0260                        &priv->notifier);
0261 
0262     list_for_each_entry_safe(event, e, &priv->events, node) {
0263         list_del(&event->node);
0264         kfree(event);
0265     }
0266     kfree(priv);
0267 
0268     return 0;
0269 }
0270 
0271 /*
0272  * Ioctls
0273  */
0274 static long cros_ec_chardev_ioctl_xcmd(struct cros_ec_dev *ec, void __user *arg)
0275 {
0276     struct cros_ec_command *s_cmd;
0277     struct cros_ec_command u_cmd;
0278     long ret;
0279 
0280     if (copy_from_user(&u_cmd, arg, sizeof(u_cmd)))
0281         return -EFAULT;
0282 
0283     if (u_cmd.outsize > EC_MAX_MSG_BYTES ||
0284         u_cmd.insize > EC_MAX_MSG_BYTES)
0285         return -EINVAL;
0286 
0287     s_cmd = kmalloc(sizeof(*s_cmd) + max(u_cmd.outsize, u_cmd.insize),
0288             GFP_KERNEL);
0289     if (!s_cmd)
0290         return -ENOMEM;
0291 
0292     if (copy_from_user(s_cmd, arg, sizeof(*s_cmd) + u_cmd.outsize)) {
0293         ret = -EFAULT;
0294         goto exit;
0295     }
0296 
0297     if (u_cmd.outsize != s_cmd->outsize ||
0298         u_cmd.insize != s_cmd->insize) {
0299         ret = -EINVAL;
0300         goto exit;
0301     }
0302 
0303     s_cmd->command += ec->cmd_offset;
0304     ret = cros_ec_cmd_xfer(ec->ec_dev, s_cmd);
0305     /* Only copy data to userland if data was received. */
0306     if (ret < 0)
0307         goto exit;
0308 
0309     if (copy_to_user(arg, s_cmd, sizeof(*s_cmd) + s_cmd->insize))
0310         ret = -EFAULT;
0311 exit:
0312     kfree(s_cmd);
0313     return ret;
0314 }
0315 
0316 static long cros_ec_chardev_ioctl_readmem(struct cros_ec_dev *ec,
0317                        void __user *arg)
0318 {
0319     struct cros_ec_device *ec_dev = ec->ec_dev;
0320     struct cros_ec_readmem s_mem = { };
0321     long num;
0322 
0323     /* Not every platform supports direct reads */
0324     if (!ec_dev->cmd_readmem)
0325         return -ENOTTY;
0326 
0327     if (copy_from_user(&s_mem, arg, sizeof(s_mem)))
0328         return -EFAULT;
0329 
0330     num = ec_dev->cmd_readmem(ec_dev, s_mem.offset, s_mem.bytes,
0331                   s_mem.buffer);
0332     if (num <= 0)
0333         return num;
0334 
0335     if (copy_to_user((void __user *)arg, &s_mem, sizeof(s_mem)))
0336         return -EFAULT;
0337 
0338     return num;
0339 }
0340 
0341 static long cros_ec_chardev_ioctl(struct file *filp, unsigned int cmd,
0342                    unsigned long arg)
0343 {
0344     struct chardev_priv *priv = filp->private_data;
0345     struct cros_ec_dev *ec = priv->ec_dev;
0346 
0347     if (_IOC_TYPE(cmd) != CROS_EC_DEV_IOC)
0348         return -ENOTTY;
0349 
0350     switch (cmd) {
0351     case CROS_EC_DEV_IOCXCMD:
0352         return cros_ec_chardev_ioctl_xcmd(ec, (void __user *)arg);
0353     case CROS_EC_DEV_IOCRDMEM:
0354         return cros_ec_chardev_ioctl_readmem(ec, (void __user *)arg);
0355     case CROS_EC_DEV_IOCEVENTMASK:
0356         priv->event_mask = arg;
0357         return 0;
0358     }
0359 
0360     return -ENOTTY;
0361 }
0362 
0363 static const struct file_operations chardev_fops = {
0364     .open       = cros_ec_chardev_open,
0365     .poll       = cros_ec_chardev_poll,
0366     .read       = cros_ec_chardev_read,
0367     .release    = cros_ec_chardev_release,
0368     .unlocked_ioctl = cros_ec_chardev_ioctl,
0369 #ifdef CONFIG_COMPAT
0370     .compat_ioctl   = cros_ec_chardev_ioctl,
0371 #endif
0372 };
0373 
0374 static int cros_ec_chardev_probe(struct platform_device *pdev)
0375 {
0376     struct cros_ec_dev *ec_dev = dev_get_drvdata(pdev->dev.parent);
0377     struct cros_ec_platform *ec_platform = dev_get_platdata(ec_dev->dev);
0378     struct chardev_data *data;
0379 
0380     /* Create a char device: we want to create it anew */
0381     data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
0382     if (!data)
0383         return -ENOMEM;
0384 
0385     data->ec_dev = ec_dev;
0386     data->misc.minor = MISC_DYNAMIC_MINOR;
0387     data->misc.fops = &chardev_fops;
0388     data->misc.name = ec_platform->ec_name;
0389     data->misc.parent = pdev->dev.parent;
0390 
0391     dev_set_drvdata(&pdev->dev, data);
0392 
0393     return misc_register(&data->misc);
0394 }
0395 
0396 static int cros_ec_chardev_remove(struct platform_device *pdev)
0397 {
0398     struct chardev_data *data = dev_get_drvdata(&pdev->dev);
0399 
0400     misc_deregister(&data->misc);
0401 
0402     return 0;
0403 }
0404 
0405 static struct platform_driver cros_ec_chardev_driver = {
0406     .driver = {
0407         .name = DRV_NAME,
0408     },
0409     .probe = cros_ec_chardev_probe,
0410     .remove = cros_ec_chardev_remove,
0411 };
0412 
0413 module_platform_driver(cros_ec_chardev_driver);
0414 
0415 MODULE_ALIAS("platform:" DRV_NAME);
0416 MODULE_AUTHOR("Enric Balletbo i Serra <enric.balletbo@collabora.com>");
0417 MODULE_DESCRIPTION("ChromeOS EC Miscellaneous Character Driver");
0418 MODULE_LICENSE("GPL");