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