2 * cros_ec_dev - expose the Chrome OS Embedded Controller to user-space
4 * Copyright (C) 2014 Google, Inc.
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 #include <linux/module.h>
22 #include <linux/platform_device.h>
23 #include <linux/uaccess.h>
25 #include "cros_ec_dev.h"
27 /* Device variables */
28 #define CROS_MAX_DEV 128
29 static struct class *cros_class
;
32 /* Basic communication */
33 static int ec_get_version(struct cros_ec_device
*ec
, char *str
, int maxlen
)
35 struct ec_response_get_version
*resp
;
36 static const char * const current_image_name
[] = {
37 "unknown", "read-only", "read-write", "invalid",
39 struct cros_ec_command msg
= {
41 .command
= EC_CMD_GET_VERSION
,
45 .insize
= sizeof(*resp
),
49 ret
= cros_ec_cmd_xfer(ec
, &msg
);
53 if (msg
.result
!= EC_RES_SUCCESS
) {
55 "%s\nUnknown EC version: EC returned %d\n",
56 CROS_EC_DEV_VERSION
, msg
.result
);
60 resp
= (struct ec_response_get_version
*)msg
.indata
;
61 if (resp
->current_image
>= ARRAY_SIZE(current_image_name
))
62 resp
->current_image
= 3; /* invalid */
64 snprintf(str
, maxlen
, "%s\n%s\n%s\n%s\n", CROS_EC_DEV_VERSION
,
65 resp
->version_string_ro
, resp
->version_string_rw
,
66 current_image_name
[resp
->current_image
]);
72 static int ec_device_open(struct inode
*inode
, struct file
*filp
)
74 filp
->private_data
= container_of(inode
->i_cdev
,
75 struct cros_ec_device
, cdev
);
79 static int ec_device_release(struct inode
*inode
, struct file
*filp
)
84 static ssize_t
ec_device_read(struct file
*filp
, char __user
*buffer
,
85 size_t length
, loff_t
*offset
)
87 struct cros_ec_device
*ec
= filp
->private_data
;
88 char msg
[sizeof(struct ec_response_get_version
) +
89 sizeof(CROS_EC_DEV_VERSION
)];
96 ret
= ec_get_version(ec
, msg
, sizeof(msg
));
100 count
= min(length
, strlen(msg
));
102 if (copy_to_user(buffer
, msg
, count
))
110 static long ec_device_ioctl_xcmd(struct cros_ec_device
*ec
, void __user
*arg
)
113 struct cros_ec_command s_cmd
= { };
115 if (copy_from_user(&s_cmd
, arg
, sizeof(s_cmd
)))
118 ret
= cros_ec_cmd_xfer(ec
, &s_cmd
);
119 /* Only copy data to userland if data was received. */
123 if (copy_to_user(arg
, &s_cmd
, sizeof(s_cmd
)))
129 static long ec_device_ioctl_readmem(struct cros_ec_device
*ec
, void __user
*arg
)
131 struct cros_ec_readmem s_mem
= { };
134 /* Not every platform supports direct reads */
135 if (!ec
->cmd_readmem
)
138 if (copy_from_user(&s_mem
, arg
, sizeof(s_mem
)))
141 num
= ec
->cmd_readmem(ec
, s_mem
.offset
, s_mem
.bytes
, s_mem
.buffer
);
145 if (copy_to_user((void __user
*)arg
, &s_mem
, sizeof(s_mem
)))
151 static long ec_device_ioctl(struct file
*filp
, unsigned int cmd
,
154 struct cros_ec_device
*ec
= filp
->private_data
;
156 if (_IOC_TYPE(cmd
) != CROS_EC_DEV_IOC
)
160 case CROS_EC_DEV_IOCXCMD
:
161 return ec_device_ioctl_xcmd(ec
, (void __user
*)arg
);
162 case CROS_EC_DEV_IOCRDMEM
:
163 return ec_device_ioctl_readmem(ec
, (void __user
*)arg
);
169 /* Module initialization */
170 static const struct file_operations fops
= {
171 .open
= ec_device_open
,
172 .release
= ec_device_release
,
173 .read
= ec_device_read
,
174 .unlocked_ioctl
= ec_device_ioctl
,
177 static int ec_device_probe(struct platform_device
*pdev
)
179 struct cros_ec_device
*ec
= dev_get_drvdata(pdev
->dev
.parent
);
180 int retval
= -ENOTTY
;
181 dev_t devno
= MKDEV(ec_major
, 0);
183 /* Instantiate it (and remember the EC) */
184 cdev_init(&ec
->cdev
, &fops
);
186 retval
= cdev_add(&ec
->cdev
, devno
, 1);
188 dev_err(&pdev
->dev
, ": failed to add character device\n");
192 ec
->vdev
= device_create(cros_class
, NULL
, devno
, ec
,
194 if (IS_ERR(ec
->vdev
)) {
195 retval
= PTR_ERR(ec
->vdev
);
196 dev_err(&pdev
->dev
, ": failed to create device\n");
201 /* Initialize extra interfaces */
202 ec_dev_sysfs_init(ec
);
203 ec_dev_lightbar_init(ec
);
208 static int ec_device_remove(struct platform_device
*pdev
)
210 struct cros_ec_device
*ec
= dev_get_drvdata(pdev
->dev
.parent
);
212 ec_dev_lightbar_remove(ec
);
213 ec_dev_sysfs_remove(ec
);
214 device_destroy(cros_class
, MKDEV(ec_major
, 0));
219 static struct platform_driver cros_ec_dev_driver
= {
221 .name
= "cros-ec-ctl",
223 .probe
= ec_device_probe
,
224 .remove
= ec_device_remove
,
227 static int __init
cros_ec_dev_init(void)
232 cros_class
= class_create(THIS_MODULE
, "chromeos");
233 if (IS_ERR(cros_class
)) {
234 pr_err(CROS_EC_DEV_NAME
": failed to register device class\n");
235 return PTR_ERR(cros_class
);
238 /* Get a range of minor numbers (starting with 0) to work with */
239 ret
= alloc_chrdev_region(&dev
, 0, CROS_MAX_DEV
, CROS_EC_DEV_NAME
);
241 pr_err(CROS_EC_DEV_NAME
": alloc_chrdev_region() failed\n");
242 goto failed_chrdevreg
;
244 ec_major
= MAJOR(dev
);
246 /* Register the driver */
247 ret
= platform_driver_register(&cros_ec_dev_driver
);
249 pr_warn(CROS_EC_DEV_NAME
": can't register driver: %d\n", ret
);
255 unregister_chrdev_region(MKDEV(ec_major
, 0), CROS_MAX_DEV
);
257 class_destroy(cros_class
);
261 static void __exit
cros_ec_dev_exit(void)
263 platform_driver_unregister(&cros_ec_dev_driver
);
264 unregister_chrdev(ec_major
, CROS_EC_DEV_NAME
);
265 class_destroy(cros_class
);
268 module_init(cros_ec_dev_init
);
269 module_exit(cros_ec_dev_exit
);
271 MODULE_AUTHOR("Bill Richardson <wfrichar@chromium.org>");
272 MODULE_DESCRIPTION("Userspace interface to the Chrome OS Embedded Controller");
273 MODULE_VERSION("1.0");
274 MODULE_LICENSE("GPL");