|
@@ -27,11 +27,22 @@
|
|
|
|
|
|
/* Device variables */
|
|
/* Device variables */
|
|
#define CROS_MAX_DEV 128
|
|
#define CROS_MAX_DEV 128
|
|
-static struct class *cros_class;
|
|
|
|
static int ec_major;
|
|
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 */
|
|
/* 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;
|
|
struct ec_response_get_version *resp;
|
|
static const char * const current_image_name[] = {
|
|
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;
|
|
return -ENOMEM;
|
|
|
|
|
|
msg->version = 0;
|
|
msg->version = 0;
|
|
- msg->command = EC_CMD_GET_VERSION;
|
|
|
|
|
|
+ msg->command = EC_CMD_GET_VERSION + ec->cmd_offset;
|
|
msg->insize = sizeof(*resp);
|
|
msg->insize = sizeof(*resp);
|
|
msg->outsize = 0;
|
|
msg->outsize = 0;
|
|
|
|
|
|
- ret = cros_ec_cmd_xfer(ec, msg);
|
|
|
|
|
|
+ ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
|
|
if (ret < 0)
|
|
if (ret < 0)
|
|
goto exit;
|
|
goto exit;
|
|
|
|
|
|
@@ -78,8 +89,10 @@ exit:
|
|
/* Device file ops */
|
|
/* Device file ops */
|
|
static int ec_device_open(struct inode *inode, struct file *filp)
|
|
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;
|
|
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,
|
|
static ssize_t ec_device_read(struct file *filp, char __user *buffer,
|
|
size_t length, loff_t *offset)
|
|
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) +
|
|
char msg[sizeof(struct ec_response_get_version) +
|
|
sizeof(CROS_EC_DEV_VERSION)];
|
|
sizeof(CROS_EC_DEV_VERSION)];
|
|
size_t count;
|
|
size_t count;
|
|
@@ -114,7 +127,7 @@ static ssize_t ec_device_read(struct file *filp, char __user *buffer,
|
|
}
|
|
}
|
|
|
|
|
|
/* Ioctls */
|
|
/* 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;
|
|
long ret;
|
|
struct cros_ec_command u_cmd;
|
|
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;
|
|
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. */
|
|
/* Only copy data to userland if data was received. */
|
|
if (ret < 0)
|
|
if (ret < 0)
|
|
goto exit;
|
|
goto exit;
|
|
@@ -145,19 +159,21 @@ exit:
|
|
return ret;
|
|
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 = { };
|
|
struct cros_ec_readmem s_mem = { };
|
|
long num;
|
|
long num;
|
|
|
|
|
|
/* Not every platform supports direct reads */
|
|
/* Not every platform supports direct reads */
|
|
- if (!ec->cmd_readmem)
|
|
|
|
|
|
+ if (!ec_dev->cmd_readmem)
|
|
return -ENOTTY;
|
|
return -ENOTTY;
|
|
|
|
|
|
if (copy_from_user(&s_mem, arg, sizeof(s_mem)))
|
|
if (copy_from_user(&s_mem, arg, sizeof(s_mem)))
|
|
return -EFAULT;
|
|
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)
|
|
if (num <= 0)
|
|
return num;
|
|
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,
|
|
static long ec_device_ioctl(struct file *filp, unsigned int cmd,
|
|
unsigned long arg)
|
|
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)
|
|
if (_IOC_TYPE(cmd) != CROS_EC_DEV_IOC)
|
|
return -ENOTTY;
|
|
return -ENOTTY;
|
|
@@ -193,45 +209,81 @@ static const struct file_operations fops = {
|
|
.unlocked_ioctl = ec_device_ioctl,
|
|
.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)
|
|
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);
|
|
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);
|
|
retval = cdev_add(&ec->cdev, devno, 1);
|
|
if (retval) {
|
|
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;
|
|
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)
|
|
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);
|
|
cdev_del(&ec->cdev);
|
|
|
|
+ device_unregister(&ec->class_dev);
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -248,10 +300,10 @@ static int __init cros_ec_dev_init(void)
|
|
int ret;
|
|
int ret;
|
|
dev_t dev = 0;
|
|
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");
|
|
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 */
|
|
/* 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:
|
|
failed_devreg:
|
|
unregister_chrdev_region(MKDEV(ec_major, 0), CROS_MAX_DEV);
|
|
unregister_chrdev_region(MKDEV(ec_major, 0), CROS_MAX_DEV);
|
|
failed_chrdevreg:
|
|
failed_chrdevreg:
|
|
- class_destroy(cros_class);
|
|
|
|
|
|
+ class_unregister(&cros_class);
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -281,7 +333,7 @@ static void __exit cros_ec_dev_exit(void)
|
|
{
|
|
{
|
|
platform_driver_unregister(&cros_ec_dev_driver);
|
|
platform_driver_unregister(&cros_ec_dev_driver);
|
|
unregister_chrdev(ec_major, CROS_EC_DEV_NAME);
|
|
unregister_chrdev(ec_major, CROS_EC_DEV_NAME);
|
|
- class_destroy(cros_class);
|
|
|
|
|
|
+ class_unregister(&cros_class);
|
|
}
|
|
}
|
|
|
|
|
|
module_init(cros_ec_dev_init);
|
|
module_init(cros_ec_dev_init);
|