diff options
Diffstat (limited to 'drivers/platform/chrome/cros_ec_dev.c')
-rw-r--r-- | drivers/platform/chrome/cros_ec_dev.c | 130 |
1 files changed, 91 insertions, 39 deletions
diff --git a/drivers/platform/chrome/cros_ec_dev.c b/drivers/platform/chrome/cros_ec_dev.c index e91ced1cb8ce..e8fcdc237029 100644 --- a/drivers/platform/chrome/cros_ec_dev.c +++ b/drivers/platform/chrome/cros_ec_dev.c @@ -27,11 +27,22 @@ /* Device variables */ #define CROS_MAX_DEV 128 -static struct class *cros_class; static int ec_major; +static const struct attribute_group *cros_ec_groups[] = { + &cros_ec_attr_group, + &cros_ec_lightbar_attr_group, + NULL, +}; + +static struct class cros_class = { + .owner = THIS_MODULE, + .name = "chromeos", + .dev_groups = cros_ec_groups, +}; + /* Basic communication */ -static int ec_get_version(struct cros_ec_device *ec, char *str, int maxlen) +static int ec_get_version(struct cros_ec_dev *ec, char *str, int maxlen) { struct ec_response_get_version *resp; static const char * const current_image_name[] = { @@ -45,11 +56,11 @@ static int ec_get_version(struct cros_ec_device *ec, char *str, int maxlen) return -ENOMEM; msg->version = 0; - msg->command = EC_CMD_GET_VERSION; + msg->command = EC_CMD_GET_VERSION + ec->cmd_offset; msg->insize = sizeof(*resp); msg->outsize = 0; - ret = cros_ec_cmd_xfer(ec, msg); + ret = cros_ec_cmd_xfer(ec->ec_dev, msg); if (ret < 0) goto exit; @@ -78,8 +89,10 @@ exit: /* Device file ops */ static int ec_device_open(struct inode *inode, struct file *filp) { - filp->private_data = container_of(inode->i_cdev, - struct cros_ec_device, cdev); + struct cros_ec_dev *ec = container_of(inode->i_cdev, + struct cros_ec_dev, cdev); + filp->private_data = ec; + nonseekable_open(inode, filp); return 0; } @@ -91,7 +104,7 @@ static int ec_device_release(struct inode *inode, struct file *filp) static ssize_t ec_device_read(struct file *filp, char __user *buffer, size_t length, loff_t *offset) { - struct cros_ec_device *ec = filp->private_data; + struct cros_ec_dev *ec = filp->private_data; char msg[sizeof(struct ec_response_get_version) + sizeof(CROS_EC_DEV_VERSION)]; size_t count; @@ -114,7 +127,7 @@ static ssize_t ec_device_read(struct file *filp, char __user *buffer, } /* Ioctls */ -static long ec_device_ioctl_xcmd(struct cros_ec_device *ec, void __user *arg) +static long ec_device_ioctl_xcmd(struct cros_ec_dev *ec, void __user *arg) { long ret; struct cros_ec_command u_cmd; @@ -133,7 +146,8 @@ static long ec_device_ioctl_xcmd(struct cros_ec_device *ec, void __user *arg) goto exit; } - ret = cros_ec_cmd_xfer(ec, s_cmd); + s_cmd->command += ec->cmd_offset; + ret = cros_ec_cmd_xfer(ec->ec_dev, s_cmd); /* Only copy data to userland if data was received. */ if (ret < 0) goto exit; @@ -145,19 +159,21 @@ exit: return ret; } -static long ec_device_ioctl_readmem(struct cros_ec_device *ec, void __user *arg) +static long ec_device_ioctl_readmem(struct cros_ec_dev *ec, void __user *arg) { + struct cros_ec_device *ec_dev = ec->ec_dev; struct cros_ec_readmem s_mem = { }; long num; /* Not every platform supports direct reads */ - if (!ec->cmd_readmem) + if (!ec_dev->cmd_readmem) return -ENOTTY; if (copy_from_user(&s_mem, arg, sizeof(s_mem))) return -EFAULT; - num = ec->cmd_readmem(ec, s_mem.offset, s_mem.bytes, s_mem.buffer); + num = ec_dev->cmd_readmem(ec_dev, s_mem.offset, s_mem.bytes, + s_mem.buffer); if (num <= 0) return num; @@ -170,7 +186,7 @@ static long ec_device_ioctl_readmem(struct cros_ec_device *ec, void __user *arg) static long ec_device_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { - struct cros_ec_device *ec = filp->private_data; + struct cros_ec_dev *ec = filp->private_data; if (_IOC_TYPE(cmd) != CROS_EC_DEV_IOC) return -ENOTTY; @@ -193,45 +209,81 @@ static const struct file_operations fops = { .unlocked_ioctl = ec_device_ioctl, }; +static void __remove(struct device *dev) +{ + struct cros_ec_dev *ec = container_of(dev, struct cros_ec_dev, + class_dev); + kfree(ec); +} + static int ec_device_probe(struct platform_device *pdev) { - struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent); - int retval = -ENOTTY; - dev_t devno = MKDEV(ec_major, 0); + int retval = -ENOMEM; + struct device *dev = &pdev->dev; + struct cros_ec_platform *ec_platform = dev_get_platdata(dev); + dev_t devno = MKDEV(ec_major, pdev->id); + struct cros_ec_dev *ec = kzalloc(sizeof(*ec), GFP_KERNEL); + + if (!ec) + return retval; - /* Instantiate it (and remember the EC) */ + dev_set_drvdata(dev, ec); + ec->ec_dev = dev_get_drvdata(dev->parent); + ec->dev = dev; + ec->cmd_offset = ec_platform->cmd_offset; + device_initialize(&ec->class_dev); cdev_init(&ec->cdev, &fops); + /* + * Add the character device + * Link cdev to the class device to be sure device is not used + * before unbinding it. + */ + ec->cdev.kobj.parent = &ec->class_dev.kobj; retval = cdev_add(&ec->cdev, devno, 1); if (retval) { - dev_err(&pdev->dev, ": failed to add character device\n"); - return retval; + dev_err(dev, ": failed to add character device\n"); + goto cdev_add_failed; } - ec->vdev = device_create(cros_class, NULL, devno, ec, - CROS_EC_DEV_NAME); - if (IS_ERR(ec->vdev)) { - retval = PTR_ERR(ec->vdev); - dev_err(&pdev->dev, ": failed to create device\n"); - cdev_del(&ec->cdev); - return retval; + /* + * Add the class device + * Link to the character device for creating the /dev entry + * in devtmpfs. + */ + ec->class_dev.devt = ec->cdev.dev; + ec->class_dev.class = &cros_class; + ec->class_dev.parent = dev; + ec->class_dev.release = __remove; + + retval = dev_set_name(&ec->class_dev, "%s", ec_platform->ec_name); + if (retval) { + dev_err(dev, "dev_set_name failed => %d\n", retval); + goto set_named_failed; } - /* Initialize extra interfaces */ - ec_dev_sysfs_init(ec); - ec_dev_lightbar_init(ec); + retval = device_add(&ec->class_dev); + if (retval) { + dev_err(dev, "device_register failed => %d\n", retval); + goto dev_reg_failed; + } return 0; + +dev_reg_failed: +set_named_failed: + dev_set_drvdata(dev, NULL); + cdev_del(&ec->cdev); +cdev_add_failed: + kfree(ec); + return retval; } static int ec_device_remove(struct platform_device *pdev) { - struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent); - - ec_dev_lightbar_remove(ec); - ec_dev_sysfs_remove(ec); - device_destroy(cros_class, MKDEV(ec_major, 0)); + struct cros_ec_dev *ec = dev_get_drvdata(&pdev->dev); cdev_del(&ec->cdev); + device_unregister(&ec->class_dev); return 0; } @@ -248,10 +300,10 @@ static int __init cros_ec_dev_init(void) int ret; dev_t dev = 0; - cros_class = class_create(THIS_MODULE, "chromeos"); - if (IS_ERR(cros_class)) { + ret = class_register(&cros_class); + if (ret) { pr_err(CROS_EC_DEV_NAME ": failed to register device class\n"); - return PTR_ERR(cros_class); + return ret; } /* Get a range of minor numbers (starting with 0) to work with */ @@ -273,7 +325,7 @@ static int __init cros_ec_dev_init(void) failed_devreg: unregister_chrdev_region(MKDEV(ec_major, 0), CROS_MAX_DEV); failed_chrdevreg: - class_destroy(cros_class); + class_unregister(&cros_class); return ret; } @@ -281,7 +333,7 @@ static void __exit cros_ec_dev_exit(void) { platform_driver_unregister(&cros_ec_dev_driver); unregister_chrdev(ec_major, CROS_EC_DEV_NAME); - class_destroy(cros_class); + class_unregister(&cros_class); } module_init(cros_ec_dev_init); |