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/slab.h>
24 #include <linux/uaccess.h>
26 #include "cros_ec_dev.h"
28 /* Device variables */
29 #define CROS_MAX_DEV 128
32 static const struct attribute_group
*cros_ec_groups
[] = {
34 &cros_ec_lightbar_attr_group
,
35 &cros_ec_vbc_attr_group
,
39 static struct class cros_class
= {
42 .dev_groups
= cros_ec_groups
,
45 /* Basic communication */
46 static int ec_get_version(struct cros_ec_dev
*ec
, char *str
, int maxlen
)
48 struct ec_response_get_version
*resp
;
49 static const char * const current_image_name
[] = {
50 "unknown", "read-only", "read-write", "invalid",
52 struct cros_ec_command
*msg
;
55 msg
= kmalloc(sizeof(*msg
) + sizeof(*resp
), GFP_KERNEL
);
60 msg
->command
= EC_CMD_GET_VERSION
+ ec
->cmd_offset
;
61 msg
->insize
= sizeof(*resp
);
64 ret
= cros_ec_cmd_xfer(ec
->ec_dev
, msg
);
68 if (msg
->result
!= EC_RES_SUCCESS
) {
70 "%s\nUnknown EC version: EC returned %d\n",
71 CROS_EC_DEV_VERSION
, msg
->result
);
76 resp
= (struct ec_response_get_version
*)msg
->data
;
77 if (resp
->current_image
>= ARRAY_SIZE(current_image_name
))
78 resp
->current_image
= 3; /* invalid */
80 snprintf(str
, maxlen
, "%s\n%s\n%s\n%s\n", CROS_EC_DEV_VERSION
,
81 resp
->version_string_ro
, resp
->version_string_rw
,
82 current_image_name
[resp
->current_image
]);
91 static int ec_device_open(struct inode
*inode
, struct file
*filp
)
93 struct cros_ec_dev
*ec
= container_of(inode
->i_cdev
,
94 struct cros_ec_dev
, cdev
);
95 filp
->private_data
= ec
;
96 nonseekable_open(inode
, filp
);
100 static int ec_device_release(struct inode
*inode
, struct file
*filp
)
105 static ssize_t
ec_device_read(struct file
*filp
, char __user
*buffer
,
106 size_t length
, loff_t
*offset
)
108 struct cros_ec_dev
*ec
= filp
->private_data
;
109 char msg
[sizeof(struct ec_response_get_version
) +
110 sizeof(CROS_EC_DEV_VERSION
)];
117 ret
= ec_get_version(ec
, msg
, sizeof(msg
));
121 count
= min(length
, strlen(msg
));
123 if (copy_to_user(buffer
, msg
, count
))
131 static long ec_device_ioctl_xcmd(struct cros_ec_dev
*ec
, void __user
*arg
)
134 struct cros_ec_command u_cmd
;
135 struct cros_ec_command
*s_cmd
;
137 if (copy_from_user(&u_cmd
, arg
, sizeof(u_cmd
)))
140 s_cmd
= kmalloc(sizeof(*s_cmd
) + max(u_cmd
.outsize
, u_cmd
.insize
),
145 if (copy_from_user(s_cmd
, arg
, sizeof(*s_cmd
) + u_cmd
.outsize
)) {
150 if (u_cmd
.outsize
!= s_cmd
->outsize
||
151 u_cmd
.insize
!= s_cmd
->insize
) {
156 s_cmd
->command
+= ec
->cmd_offset
;
157 ret
= cros_ec_cmd_xfer(ec
->ec_dev
, s_cmd
);
158 /* Only copy data to userland if data was received. */
162 if (copy_to_user(arg
, s_cmd
, sizeof(*s_cmd
) + s_cmd
->insize
))
169 static long ec_device_ioctl_readmem(struct cros_ec_dev
*ec
, void __user
*arg
)
171 struct cros_ec_device
*ec_dev
= ec
->ec_dev
;
172 struct cros_ec_readmem s_mem
= { };
175 /* Not every platform supports direct reads */
176 if (!ec_dev
->cmd_readmem
)
179 if (copy_from_user(&s_mem
, arg
, sizeof(s_mem
)))
182 num
= ec_dev
->cmd_readmem(ec_dev
, s_mem
.offset
, s_mem
.bytes
,
187 if (copy_to_user((void __user
*)arg
, &s_mem
, sizeof(s_mem
)))
193 static long ec_device_ioctl(struct file
*filp
, unsigned int cmd
,
196 struct cros_ec_dev
*ec
= filp
->private_data
;
198 if (_IOC_TYPE(cmd
) != CROS_EC_DEV_IOC
)
202 case CROS_EC_DEV_IOCXCMD
:
203 return ec_device_ioctl_xcmd(ec
, (void __user
*)arg
);
204 case CROS_EC_DEV_IOCRDMEM
:
205 return ec_device_ioctl_readmem(ec
, (void __user
*)arg
);
211 /* Module initialization */
212 static const struct file_operations fops
= {
213 .open
= ec_device_open
,
214 .release
= ec_device_release
,
215 .read
= ec_device_read
,
216 .unlocked_ioctl
= ec_device_ioctl
,
219 static void __remove(struct device
*dev
)
221 struct cros_ec_dev
*ec
= container_of(dev
, struct cros_ec_dev
,
226 static int ec_device_probe(struct platform_device
*pdev
)
228 int retval
= -ENOMEM
;
229 struct device
*dev
= &pdev
->dev
;
230 struct cros_ec_platform
*ec_platform
= dev_get_platdata(dev
);
231 dev_t devno
= MKDEV(ec_major
, pdev
->id
);
232 struct cros_ec_dev
*ec
= kzalloc(sizeof(*ec
), GFP_KERNEL
);
237 dev_set_drvdata(dev
, ec
);
238 ec
->ec_dev
= dev_get_drvdata(dev
->parent
);
240 ec
->cmd_offset
= ec_platform
->cmd_offset
;
241 device_initialize(&ec
->class_dev
);
242 cdev_init(&ec
->cdev
, &fops
);
245 * Add the character device
246 * Link cdev to the class device to be sure device is not used
247 * before unbinding it.
249 ec
->cdev
.kobj
.parent
= &ec
->class_dev
.kobj
;
250 retval
= cdev_add(&ec
->cdev
, devno
, 1);
252 dev_err(dev
, ": failed to add character device\n");
253 goto cdev_add_failed
;
257 * Add the class device
258 * Link to the character device for creating the /dev entry
261 ec
->class_dev
.devt
= ec
->cdev
.dev
;
262 ec
->class_dev
.class = &cros_class
;
263 ec
->class_dev
.parent
= dev
;
264 ec
->class_dev
.release
= __remove
;
266 retval
= dev_set_name(&ec
->class_dev
, "%s", ec_platform
->ec_name
);
268 dev_err(dev
, "dev_set_name failed => %d\n", retval
);
269 goto set_named_failed
;
272 retval
= device_add(&ec
->class_dev
);
274 dev_err(dev
, "device_register failed => %d\n", retval
);
282 dev_set_drvdata(dev
, NULL
);
289 static int ec_device_remove(struct platform_device
*pdev
)
291 struct cros_ec_dev
*ec
= dev_get_drvdata(&pdev
->dev
);
293 device_unregister(&ec
->class_dev
);
297 static const struct platform_device_id cros_ec_id
[] = {
298 { "cros-ec-ctl", 0 },
301 MODULE_DEVICE_TABLE(platform
, cros_ec_id
);
303 static struct platform_driver cros_ec_dev_driver
= {
305 .name
= "cros-ec-ctl",
307 .probe
= ec_device_probe
,
308 .remove
= ec_device_remove
,
311 static int __init
cros_ec_dev_init(void)
316 ret
= class_register(&cros_class
);
318 pr_err(CROS_EC_DEV_NAME
": failed to register device class\n");
322 /* Get a range of minor numbers (starting with 0) to work with */
323 ret
= alloc_chrdev_region(&dev
, 0, CROS_MAX_DEV
, CROS_EC_DEV_NAME
);
325 pr_err(CROS_EC_DEV_NAME
": alloc_chrdev_region() failed\n");
326 goto failed_chrdevreg
;
328 ec_major
= MAJOR(dev
);
330 /* Register the driver */
331 ret
= platform_driver_register(&cros_ec_dev_driver
);
333 pr_warn(CROS_EC_DEV_NAME
": can't register driver: %d\n", ret
);
339 unregister_chrdev_region(MKDEV(ec_major
, 0), CROS_MAX_DEV
);
341 class_unregister(&cros_class
);
345 static void __exit
cros_ec_dev_exit(void)
347 platform_driver_unregister(&cros_ec_dev_driver
);
348 unregister_chrdev(ec_major
, CROS_EC_DEV_NAME
);
349 class_unregister(&cros_class
);
352 module_init(cros_ec_dev_init
);
353 module_exit(cros_ec_dev_exit
);
355 MODULE_AUTHOR("Bill Richardson <wfrichar@chromium.org>");
356 MODULE_DESCRIPTION("Userspace interface to the Chrome OS Embedded Controller");
357 MODULE_VERSION("1.0");
358 MODULE_LICENSE("GPL");