0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
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
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;
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
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) {
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
0219
0220
0221 if (length == 0)
0222 return 0;
0223
0224
0225 count = min(length, event->size + 1);
0226 ret = copy_to_user(buffer, &event->event_type, count);
0227 kfree(event);
0228 if (ret)
0229 return -EFAULT;
0230 *offset = count;
0231 return count;
0232 }
0233
0234
0235
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
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
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
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
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");