root/drivers/platform/chrome/cros_ec_chardev.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. ec_get_version
  2. cros_ec_chardev_mkbp_event
  3. cros_ec_chardev_fetch_event
  4. cros_ec_chardev_open
  5. cros_ec_chardev_poll
  6. cros_ec_chardev_read
  7. cros_ec_chardev_release
  8. cros_ec_chardev_ioctl_xcmd
  9. cros_ec_chardev_ioctl_readmem
  10. cros_ec_chardev_ioctl
  11. cros_ec_chardev_probe
  12. cros_ec_chardev_remove

   1 // SPDX-License-Identifier: GPL-2.0
   2 /*
   3  * Miscellaneous character driver for ChromeOS Embedded Controller
   4  *
   5  * Copyright 2014 Google, Inc.
   6  * Copyright 2019 Google LLC
   7  *
   8  * This file is a rework and part of the code is ported from
   9  * drivers/mfd/cros_ec_dev.c that was originally written by
  10  * Bill Richardson.
  11  */
  12 
  13 #include <linux/init.h>
  14 #include <linux/device.h>
  15 #include <linux/fs.h>
  16 #include <linux/mfd/cros_ec.h>
  17 #include <linux/miscdevice.h>
  18 #include <linux/module.h>
  19 #include <linux/notifier.h>
  20 #include <linux/platform_data/cros_ec_chardev.h>
  21 #include <linux/platform_data/cros_ec_commands.h>
  22 #include <linux/platform_data/cros_ec_proto.h>
  23 #include <linux/platform_device.h>
  24 #include <linux/poll.h>
  25 #include <linux/slab.h>
  26 #include <linux/types.h>
  27 #include <linux/uaccess.h>
  28 
  29 #define DRV_NAME                "cros-ec-chardev"
  30 
  31 /* Arbitrary bounded size for the event queue */
  32 #define CROS_MAX_EVENT_LEN      PAGE_SIZE
  33 
  34 struct chardev_data {
  35         struct cros_ec_dev *ec_dev;
  36         struct miscdevice misc;
  37 };
  38 
  39 struct chardev_priv {
  40         struct cros_ec_dev *ec_dev;
  41         struct notifier_block notifier;
  42         wait_queue_head_t wait_event;
  43         unsigned long event_mask;
  44         struct list_head events;
  45         size_t event_len;
  46 };
  47 
  48 struct ec_event {
  49         struct list_head node;
  50         size_t size;
  51         u8 event_type;
  52         u8 data[0];
  53 };
  54 
  55 static int ec_get_version(struct cros_ec_dev *ec, char *str, int maxlen)
  56 {
  57         static const char * const current_image_name[] = {
  58                 "unknown", "read-only", "read-write", "invalid",
  59         };
  60         struct ec_response_get_version *resp;
  61         struct cros_ec_command *msg;
  62         int ret;
  63 
  64         msg = kzalloc(sizeof(*msg) + sizeof(*resp), GFP_KERNEL);
  65         if (!msg)
  66                 return -ENOMEM;
  67 
  68         msg->command = EC_CMD_GET_VERSION + ec->cmd_offset;
  69         msg->insize = sizeof(*resp);
  70 
  71         ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
  72         if (ret < 0) {
  73                 snprintf(str, maxlen,
  74                          "Unknown EC version, returned error: %d\n",
  75                          msg->result);
  76                 goto exit;
  77         }
  78 
  79         resp = (struct ec_response_get_version *)msg->data;
  80         if (resp->current_image >= ARRAY_SIZE(current_image_name))
  81                 resp->current_image = 3; /* invalid */
  82 
  83         snprintf(str, maxlen, "%s\n%s\n%s\n%s\n", CROS_EC_DEV_VERSION,
  84                  resp->version_string_ro, resp->version_string_rw,
  85                  current_image_name[resp->current_image]);
  86 
  87         ret = 0;
  88 exit:
  89         kfree(msg);
  90         return ret;
  91 }
  92 
  93 static int cros_ec_chardev_mkbp_event(struct notifier_block *nb,
  94                                       unsigned long queued_during_suspend,
  95                                       void *_notify)
  96 {
  97         struct chardev_priv *priv = container_of(nb, struct chardev_priv,
  98                                                  notifier);
  99         struct cros_ec_device *ec_dev = priv->ec_dev->ec_dev;
 100         struct ec_event *event;
 101         unsigned long event_bit = 1 << ec_dev->event_data.event_type;
 102         int total_size = sizeof(*event) + ec_dev->event_size;
 103 
 104         if (!(event_bit & priv->event_mask) ||
 105             (priv->event_len + total_size) > CROS_MAX_EVENT_LEN)
 106                 return NOTIFY_DONE;
 107 
 108         event = kzalloc(total_size, GFP_KERNEL);
 109         if (!event)
 110                 return NOTIFY_DONE;
 111 
 112         event->size = ec_dev->event_size;
 113         event->event_type = ec_dev->event_data.event_type;
 114         memcpy(event->data, &ec_dev->event_data.data, ec_dev->event_size);
 115 
 116         spin_lock(&priv->wait_event.lock);
 117         list_add_tail(&event->node, &priv->events);
 118         priv->event_len += total_size;
 119         wake_up_locked(&priv->wait_event);
 120         spin_unlock(&priv->wait_event.lock);
 121 
 122         return NOTIFY_OK;
 123 }
 124 
 125 static struct ec_event *cros_ec_chardev_fetch_event(struct chardev_priv *priv,
 126                                                     bool fetch, bool block)
 127 {
 128         struct ec_event *event;
 129         int err;
 130 
 131         spin_lock(&priv->wait_event.lock);
 132         if (!block && list_empty(&priv->events)) {
 133                 event = ERR_PTR(-EWOULDBLOCK);
 134                 goto out;
 135         }
 136 
 137         if (!fetch) {
 138                 event = NULL;
 139                 goto out;
 140         }
 141 
 142         err = wait_event_interruptible_locked(priv->wait_event,
 143                                               !list_empty(&priv->events));
 144         if (err) {
 145                 event = ERR_PTR(err);
 146                 goto out;
 147         }
 148 
 149         event = list_first_entry(&priv->events, struct ec_event, node);
 150         list_del(&event->node);
 151         priv->event_len -= sizeof(*event) + event->size;
 152 
 153 out:
 154         spin_unlock(&priv->wait_event.lock);
 155         return event;
 156 }
 157 
 158 /*
 159  * Device file ops
 160  */
 161 static int cros_ec_chardev_open(struct inode *inode, struct file *filp)
 162 {
 163         struct miscdevice *mdev = filp->private_data;
 164         struct cros_ec_dev *ec_dev = dev_get_drvdata(mdev->parent);
 165         struct chardev_priv *priv;
 166         int ret;
 167 
 168         priv = kzalloc(sizeof(*priv), GFP_KERNEL);
 169         if (!priv)
 170                 return -ENOMEM;
 171 
 172         priv->ec_dev = ec_dev;
 173         filp->private_data = priv;
 174         INIT_LIST_HEAD(&priv->events);
 175         init_waitqueue_head(&priv->wait_event);
 176         nonseekable_open(inode, filp);
 177 
 178         priv->notifier.notifier_call = cros_ec_chardev_mkbp_event;
 179         ret = blocking_notifier_chain_register(&ec_dev->ec_dev->event_notifier,
 180                                                &priv->notifier);
 181         if (ret) {
 182                 dev_err(ec_dev->dev, "failed to register event notifier\n");
 183                 kfree(priv);
 184         }
 185 
 186         return ret;
 187 }
 188 
 189 static __poll_t cros_ec_chardev_poll(struct file *filp, poll_table *wait)
 190 {
 191         struct chardev_priv *priv = filp->private_data;
 192 
 193         poll_wait(filp, &priv->wait_event, wait);
 194 
 195         if (list_empty(&priv->events))
 196                 return 0;
 197 
 198         return EPOLLIN | EPOLLRDNORM;
 199 }
 200 
 201 static ssize_t cros_ec_chardev_read(struct file *filp, char __user *buffer,
 202                                      size_t length, loff_t *offset)
 203 {
 204         char msg[sizeof(struct ec_response_get_version) +
 205                  sizeof(CROS_EC_DEV_VERSION)];
 206         struct chardev_priv *priv = filp->private_data;
 207         struct cros_ec_dev *ec_dev = priv->ec_dev;
 208         size_t count;
 209         int ret;
 210 
 211         if (priv->event_mask) { /* queued MKBP event */
 212                 struct ec_event *event;
 213 
 214                 event = cros_ec_chardev_fetch_event(priv, length != 0,
 215                                                 !(filp->f_flags & O_NONBLOCK));
 216                 if (IS_ERR(event))
 217                         return PTR_ERR(event);
 218                 /*
 219                  * length == 0 is special - no IO is done but we check
 220                  * for error conditions.
 221                  */
 222                 if (length == 0)
 223                         return 0;
 224 
 225                 /* The event is 1 byte of type plus the payload */
 226                 count = min(length, event->size + 1);
 227                 ret = copy_to_user(buffer, &event->event_type, count);
 228                 kfree(event);
 229                 if (ret) /* the copy failed */
 230                         return -EFAULT;
 231                 *offset = count;
 232                 return count;
 233         }
 234 
 235         /*
 236          * Legacy behavior if no event mask is defined
 237          */
 238         if (*offset != 0)
 239                 return 0;
 240 
 241         ret = ec_get_version(ec_dev, msg, sizeof(msg));
 242         if (ret)
 243                 return ret;
 244 
 245         count = min(length, strlen(msg));
 246 
 247         if (copy_to_user(buffer, msg, count))
 248                 return -EFAULT;
 249 
 250         *offset = count;
 251         return count;
 252 }
 253 
 254 static int cros_ec_chardev_release(struct inode *inode, struct file *filp)
 255 {
 256         struct chardev_priv *priv = filp->private_data;
 257         struct cros_ec_dev *ec_dev = priv->ec_dev;
 258         struct ec_event *event, *e;
 259 
 260         blocking_notifier_chain_unregister(&ec_dev->ec_dev->event_notifier,
 261                                            &priv->notifier);
 262 
 263         list_for_each_entry_safe(event, e, &priv->events, node) {
 264                 list_del(&event->node);
 265                 kfree(event);
 266         }
 267         kfree(priv);
 268 
 269         return 0;
 270 }
 271 
 272 /*
 273  * Ioctls
 274  */
 275 static long cros_ec_chardev_ioctl_xcmd(struct cros_ec_dev *ec, void __user *arg)
 276 {
 277         struct cros_ec_command *s_cmd;
 278         struct cros_ec_command u_cmd;
 279         long ret;
 280 
 281         if (copy_from_user(&u_cmd, arg, sizeof(u_cmd)))
 282                 return -EFAULT;
 283 
 284         if (u_cmd.outsize > EC_MAX_MSG_BYTES ||
 285             u_cmd.insize > EC_MAX_MSG_BYTES)
 286                 return -EINVAL;
 287 
 288         s_cmd = kmalloc(sizeof(*s_cmd) + max(u_cmd.outsize, u_cmd.insize),
 289                         GFP_KERNEL);
 290         if (!s_cmd)
 291                 return -ENOMEM;
 292 
 293         if (copy_from_user(s_cmd, arg, sizeof(*s_cmd) + u_cmd.outsize)) {
 294                 ret = -EFAULT;
 295                 goto exit;
 296         }
 297 
 298         if (u_cmd.outsize != s_cmd->outsize ||
 299             u_cmd.insize != s_cmd->insize) {
 300                 ret = -EINVAL;
 301                 goto exit;
 302         }
 303 
 304         s_cmd->command += ec->cmd_offset;
 305         ret = cros_ec_cmd_xfer(ec->ec_dev, s_cmd);
 306         /* Only copy data to userland if data was received. */
 307         if (ret < 0)
 308                 goto exit;
 309 
 310         if (copy_to_user(arg, s_cmd, sizeof(*s_cmd) + s_cmd->insize))
 311                 ret = -EFAULT;
 312 exit:
 313         kfree(s_cmd);
 314         return ret;
 315 }
 316 
 317 static long cros_ec_chardev_ioctl_readmem(struct cros_ec_dev *ec,
 318                                            void __user *arg)
 319 {
 320         struct cros_ec_device *ec_dev = ec->ec_dev;
 321         struct cros_ec_readmem s_mem = { };
 322         long num;
 323 
 324         /* Not every platform supports direct reads */
 325         if (!ec_dev->cmd_readmem)
 326                 return -ENOTTY;
 327 
 328         if (copy_from_user(&s_mem, arg, sizeof(s_mem)))
 329                 return -EFAULT;
 330 
 331         num = ec_dev->cmd_readmem(ec_dev, s_mem.offset, s_mem.bytes,
 332                                   s_mem.buffer);
 333         if (num <= 0)
 334                 return num;
 335 
 336         if (copy_to_user((void __user *)arg, &s_mem, sizeof(s_mem)))
 337                 return -EFAULT;
 338 
 339         return num;
 340 }
 341 
 342 static long cros_ec_chardev_ioctl(struct file *filp, unsigned int cmd,
 343                                    unsigned long arg)
 344 {
 345         struct chardev_priv *priv = filp->private_data;
 346         struct cros_ec_dev *ec = priv->ec_dev;
 347 
 348         if (_IOC_TYPE(cmd) != CROS_EC_DEV_IOC)
 349                 return -ENOTTY;
 350 
 351         switch (cmd) {
 352         case CROS_EC_DEV_IOCXCMD:
 353                 return cros_ec_chardev_ioctl_xcmd(ec, (void __user *)arg);
 354         case CROS_EC_DEV_IOCRDMEM:
 355                 return cros_ec_chardev_ioctl_readmem(ec, (void __user *)arg);
 356         case CROS_EC_DEV_IOCEVENTMASK:
 357                 priv->event_mask = arg;
 358                 return 0;
 359         }
 360 
 361         return -ENOTTY;
 362 }
 363 
 364 static const struct file_operations chardev_fops = {
 365         .open           = cros_ec_chardev_open,
 366         .poll           = cros_ec_chardev_poll,
 367         .read           = cros_ec_chardev_read,
 368         .release        = cros_ec_chardev_release,
 369         .unlocked_ioctl = cros_ec_chardev_ioctl,
 370 #ifdef CONFIG_COMPAT
 371         .compat_ioctl   = cros_ec_chardev_ioctl,
 372 #endif
 373 };
 374 
 375 static int cros_ec_chardev_probe(struct platform_device *pdev)
 376 {
 377         struct cros_ec_dev *ec_dev = dev_get_drvdata(pdev->dev.parent);
 378         struct cros_ec_platform *ec_platform = dev_get_platdata(ec_dev->dev);
 379         struct chardev_data *data;
 380 
 381         /* Create a char device: we want to create it anew */
 382         data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
 383         if (!data)
 384                 return -ENOMEM;
 385 
 386         data->ec_dev = ec_dev;
 387         data->misc.minor = MISC_DYNAMIC_MINOR;
 388         data->misc.fops = &chardev_fops;
 389         data->misc.name = ec_platform->ec_name;
 390         data->misc.parent = pdev->dev.parent;
 391 
 392         dev_set_drvdata(&pdev->dev, data);
 393 
 394         return misc_register(&data->misc);
 395 }
 396 
 397 static int cros_ec_chardev_remove(struct platform_device *pdev)
 398 {
 399         struct chardev_data *data = dev_get_drvdata(&pdev->dev);
 400 
 401         misc_deregister(&data->misc);
 402 
 403         return 0;
 404 }
 405 
 406 static struct platform_driver cros_ec_chardev_driver = {
 407         .driver = {
 408                 .name = DRV_NAME,
 409         },
 410         .probe = cros_ec_chardev_probe,
 411         .remove = cros_ec_chardev_remove,
 412 };
 413 
 414 module_platform_driver(cros_ec_chardev_driver);
 415 
 416 MODULE_ALIAS("platform:" DRV_NAME);
 417 MODULE_AUTHOR("Enric Balletbo i Serra <enric.balletbo@collabora.com>");
 418 MODULE_DESCRIPTION("ChromeOS EC Miscellaneous Character Driver");
 419 MODULE_LICENSE("GPL");

/* [<][>][^][v][top][bottom][index][help] */