xref: /openbmc/linux/drivers/platform/chrome/cros_ec_lightbar.c (revision 812f77b749a8ae11f58dacf0d3ed65e7ede47458)
1 /*
2  * cros_ec_lightbar - expose the Chromebook Pixel lightbar to userspace
3  *
4  * Copyright (C) 2014 Google, Inc.
5  *
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.
10  *
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.
15  *
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/>.
18  */
19 
20 #define pr_fmt(fmt) "cros_ec_lightbar: " fmt
21 
22 #include <linux/ctype.h>
23 #include <linux/delay.h>
24 #include <linux/device.h>
25 #include <linux/fs.h>
26 #include <linux/kobject.h>
27 #include <linux/mfd/cros_ec.h>
28 #include <linux/mfd/cros_ec_commands.h>
29 #include <linux/module.h>
30 #include <linux/platform_device.h>
31 #include <linux/sched.h>
32 #include <linux/types.h>
33 #include <linux/uaccess.h>
34 #include <linux/slab.h>
35 
36 #include "cros_ec_dev.h"
37 
38 /* Rate-limit the lightbar interface to prevent DoS. */
39 static unsigned long lb_interval_jiffies = 50 * HZ / 1000;
40 
41 /*
42  * Whether or not we have given userspace control of the lightbar.
43  * If this is true, we won't do anything during suspend/resume.
44  */
45 static bool userspace_control;
46 static struct cros_ec_dev *ec_with_lightbar;
47 
48 static ssize_t interval_msec_show(struct device *dev,
49 				  struct device_attribute *attr, char *buf)
50 {
51 	unsigned long msec = lb_interval_jiffies * 1000 / HZ;
52 
53 	return scnprintf(buf, PAGE_SIZE, "%lu\n", msec);
54 }
55 
56 static ssize_t interval_msec_store(struct device *dev,
57 				   struct device_attribute *attr,
58 				   const char *buf, size_t count)
59 {
60 	unsigned long msec;
61 
62 	if (kstrtoul(buf, 0, &msec))
63 		return -EINVAL;
64 
65 	lb_interval_jiffies = msec * HZ / 1000;
66 
67 	return count;
68 }
69 
70 static DEFINE_MUTEX(lb_mutex);
71 /* Return 0 if able to throttle correctly, error otherwise */
72 static int lb_throttle(void)
73 {
74 	static unsigned long last_access;
75 	unsigned long now, next_timeslot;
76 	long delay;
77 	int ret = 0;
78 
79 	mutex_lock(&lb_mutex);
80 
81 	now = jiffies;
82 	next_timeslot = last_access + lb_interval_jiffies;
83 
84 	if (time_before(now, next_timeslot)) {
85 		delay = (long)(next_timeslot) - (long)now;
86 		set_current_state(TASK_INTERRUPTIBLE);
87 		if (schedule_timeout(delay) > 0) {
88 			/* interrupted - just abort */
89 			ret = -EINTR;
90 			goto out;
91 		}
92 		now = jiffies;
93 	}
94 
95 	last_access = now;
96 out:
97 	mutex_unlock(&lb_mutex);
98 
99 	return ret;
100 }
101 
102 static struct cros_ec_command *alloc_lightbar_cmd_msg(struct cros_ec_dev *ec)
103 {
104 	struct cros_ec_command *msg;
105 	int len;
106 
107 	len = max(sizeof(struct ec_params_lightbar),
108 		  sizeof(struct ec_response_lightbar));
109 
110 	msg = kmalloc(sizeof(*msg) + len, GFP_KERNEL);
111 	if (!msg)
112 		return NULL;
113 
114 	msg->version = 0;
115 	msg->command = EC_CMD_LIGHTBAR_CMD + ec->cmd_offset;
116 	msg->outsize = sizeof(struct ec_params_lightbar);
117 	msg->insize = sizeof(struct ec_response_lightbar);
118 
119 	return msg;
120 }
121 
122 static int get_lightbar_version(struct cros_ec_dev *ec,
123 				uint32_t *ver_ptr, uint32_t *flg_ptr)
124 {
125 	struct ec_params_lightbar *param;
126 	struct ec_response_lightbar *resp;
127 	struct cros_ec_command *msg;
128 	int ret;
129 
130 	msg = alloc_lightbar_cmd_msg(ec);
131 	if (!msg)
132 		return 0;
133 
134 	param = (struct ec_params_lightbar *)msg->data;
135 	param->cmd = LIGHTBAR_CMD_VERSION;
136 	ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
137 	if (ret < 0) {
138 		ret = 0;
139 		goto exit;
140 	}
141 
142 	switch (msg->result) {
143 	case EC_RES_INVALID_PARAM:
144 		/* Pixel had no version command. */
145 		if (ver_ptr)
146 			*ver_ptr = 0;
147 		if (flg_ptr)
148 			*flg_ptr = 0;
149 		ret = 1;
150 		goto exit;
151 
152 	case EC_RES_SUCCESS:
153 		resp = (struct ec_response_lightbar *)msg->data;
154 
155 		/* Future devices w/lightbars should implement this command */
156 		if (ver_ptr)
157 			*ver_ptr = resp->version.num;
158 		if (flg_ptr)
159 			*flg_ptr = resp->version.flags;
160 		ret = 1;
161 		goto exit;
162 	}
163 
164 	/* Anything else (ie, EC_RES_INVALID_COMMAND) - no lightbar */
165 	ret = 0;
166 exit:
167 	kfree(msg);
168 	return ret;
169 }
170 
171 static ssize_t version_show(struct device *dev,
172 			    struct device_attribute *attr, char *buf)
173 {
174 	uint32_t version = 0, flags = 0;
175 	struct cros_ec_dev *ec = container_of(dev,
176 					      struct cros_ec_dev, class_dev);
177 	int ret;
178 
179 	ret = lb_throttle();
180 	if (ret)
181 		return ret;
182 
183 	/* This should always succeed, because we check during init. */
184 	if (!get_lightbar_version(ec, &version, &flags))
185 		return -EIO;
186 
187 	return scnprintf(buf, PAGE_SIZE, "%d %d\n", version, flags);
188 }
189 
190 static ssize_t brightness_store(struct device *dev,
191 				struct device_attribute *attr,
192 				const char *buf, size_t count)
193 {
194 	struct ec_params_lightbar *param;
195 	struct cros_ec_command *msg;
196 	int ret;
197 	unsigned int val;
198 	struct cros_ec_dev *ec = container_of(dev,
199 					      struct cros_ec_dev, class_dev);
200 
201 	if (kstrtouint(buf, 0, &val))
202 		return -EINVAL;
203 
204 	msg = alloc_lightbar_cmd_msg(ec);
205 	if (!msg)
206 		return -ENOMEM;
207 
208 	param = (struct ec_params_lightbar *)msg->data;
209 	param->cmd = LIGHTBAR_CMD_SET_BRIGHTNESS;
210 	param->set_brightness.num = val;
211 	ret = lb_throttle();
212 	if (ret)
213 		goto exit;
214 
215 	ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
216 	if (ret < 0)
217 		goto exit;
218 
219 	if (msg->result != EC_RES_SUCCESS) {
220 		ret = -EINVAL;
221 		goto exit;
222 	}
223 
224 	ret = count;
225 exit:
226 	kfree(msg);
227 	return ret;
228 }
229 
230 
231 /*
232  * We expect numbers, and we'll keep reading until we find them, skipping over
233  * any whitespace (sysfs guarantees that the input is null-terminated). Every
234  * four numbers are sent to the lightbar as <LED,R,G,B>. We fail at the first
235  * parsing error, if we don't parse any numbers, or if we have numbers left
236  * over.
237  */
238 static ssize_t led_rgb_store(struct device *dev, struct device_attribute *attr,
239 			     const char *buf, size_t count)
240 {
241 	struct ec_params_lightbar *param;
242 	struct cros_ec_command *msg;
243 	struct cros_ec_dev *ec = container_of(dev,
244 					      struct cros_ec_dev, class_dev);
245 	unsigned int val[4];
246 	int ret, i = 0, j = 0, ok = 0;
247 
248 	msg = alloc_lightbar_cmd_msg(ec);
249 	if (!msg)
250 		return -ENOMEM;
251 
252 	do {
253 		/* Skip any whitespace */
254 		while (*buf && isspace(*buf))
255 			buf++;
256 
257 		if (!*buf)
258 			break;
259 
260 		ret = sscanf(buf, "%i", &val[i++]);
261 		if (ret == 0)
262 			goto exit;
263 
264 		if (i == 4) {
265 			param = (struct ec_params_lightbar *)msg->data;
266 			param->cmd = LIGHTBAR_CMD_SET_RGB;
267 			param->set_rgb.led = val[0];
268 			param->set_rgb.red = val[1];
269 			param->set_rgb.green = val[2];
270 			param->set_rgb.blue = val[3];
271 			/*
272 			 * Throttle only the first of every four transactions,
273 			 * so that the user can update all four LEDs at once.
274 			 */
275 			if ((j++ % 4) == 0) {
276 				ret = lb_throttle();
277 				if (ret)
278 					goto exit;
279 			}
280 
281 			ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
282 			if (ret < 0)
283 				goto exit;
284 
285 			if (msg->result != EC_RES_SUCCESS)
286 				goto exit;
287 
288 			i = 0;
289 			ok = 1;
290 		}
291 
292 		/* Skip over the number we just read */
293 		while (*buf && !isspace(*buf))
294 			buf++;
295 
296 	} while (*buf);
297 
298 exit:
299 	kfree(msg);
300 	return (ok && i == 0) ? count : -EINVAL;
301 }
302 
303 static char const *seqname[] = {
304 	"ERROR", "S5", "S3", "S0", "S5S3", "S3S0",
305 	"S0S3", "S3S5", "STOP", "RUN", "KONAMI",
306 	"TAP", "PROGRAM",
307 };
308 
309 static ssize_t sequence_show(struct device *dev,
310 			     struct device_attribute *attr, char *buf)
311 {
312 	struct ec_params_lightbar *param;
313 	struct ec_response_lightbar *resp;
314 	struct cros_ec_command *msg;
315 	int ret;
316 	struct cros_ec_dev *ec = container_of(dev,
317 					      struct cros_ec_dev, class_dev);
318 
319 	msg = alloc_lightbar_cmd_msg(ec);
320 	if (!msg)
321 		return -ENOMEM;
322 
323 	param = (struct ec_params_lightbar *)msg->data;
324 	param->cmd = LIGHTBAR_CMD_GET_SEQ;
325 	ret = lb_throttle();
326 	if (ret)
327 		goto exit;
328 
329 	ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
330 	if (ret < 0)
331 		goto exit;
332 
333 	if (msg->result != EC_RES_SUCCESS) {
334 		ret = scnprintf(buf, PAGE_SIZE,
335 				"ERROR: EC returned %d\n", msg->result);
336 		goto exit;
337 	}
338 
339 	resp = (struct ec_response_lightbar *)msg->data;
340 	if (resp->get_seq.num >= ARRAY_SIZE(seqname))
341 		ret = scnprintf(buf, PAGE_SIZE, "%d\n", resp->get_seq.num);
342 	else
343 		ret = scnprintf(buf, PAGE_SIZE, "%s\n",
344 				seqname[resp->get_seq.num]);
345 
346 exit:
347 	kfree(msg);
348 	return ret;
349 }
350 
351 static int lb_send_empty_cmd(struct cros_ec_dev *ec, uint8_t cmd)
352 {
353 	struct ec_params_lightbar *param;
354 	struct cros_ec_command *msg;
355 	int ret;
356 
357 	msg = alloc_lightbar_cmd_msg(ec);
358 	if (!msg)
359 		return -ENOMEM;
360 
361 	param = (struct ec_params_lightbar *)msg->data;
362 	param->cmd = cmd;
363 
364 	ret = lb_throttle();
365 	if (ret)
366 		goto error;
367 
368 	ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
369 	if (ret < 0)
370 		goto error;
371 	if (msg->result != EC_RES_SUCCESS) {
372 		ret = -EINVAL;
373 		goto error;
374 	}
375 	ret = 0;
376 error:
377 	kfree(msg);
378 
379 	return ret;
380 }
381 
382 int lb_manual_suspend_ctrl(struct cros_ec_dev *ec, uint8_t enable)
383 {
384 	struct ec_params_lightbar *param;
385 	struct cros_ec_command *msg;
386 	int ret;
387 
388 	if (ec != ec_with_lightbar)
389 		return 0;
390 
391 	msg = alloc_lightbar_cmd_msg(ec);
392 	if (!msg)
393 		return -ENOMEM;
394 
395 	param = (struct ec_params_lightbar *)msg->data;
396 
397 	param->cmd = LIGHTBAR_CMD_MANUAL_SUSPEND_CTRL;
398 	param->manual_suspend_ctrl.enable = enable;
399 
400 	ret = lb_throttle();
401 	if (ret)
402 		goto error;
403 
404 	ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
405 	if (ret < 0)
406 		goto error;
407 	if (msg->result != EC_RES_SUCCESS) {
408 		ret = -EINVAL;
409 		goto error;
410 	}
411 	ret = 0;
412 error:
413 	kfree(msg);
414 
415 	return ret;
416 }
417 
418 int lb_suspend(struct cros_ec_dev *ec)
419 {
420 	if (userspace_control || ec != ec_with_lightbar)
421 		return 0;
422 
423 	return lb_send_empty_cmd(ec, LIGHTBAR_CMD_SUSPEND);
424 }
425 
426 int lb_resume(struct cros_ec_dev *ec)
427 {
428 	if (userspace_control || ec != ec_with_lightbar)
429 		return 0;
430 
431 	return lb_send_empty_cmd(ec, LIGHTBAR_CMD_RESUME);
432 }
433 
434 static ssize_t sequence_store(struct device *dev, struct device_attribute *attr,
435 			      const char *buf, size_t count)
436 {
437 	struct ec_params_lightbar *param;
438 	struct cros_ec_command *msg;
439 	unsigned int num;
440 	int ret, len;
441 	struct cros_ec_dev *ec = container_of(dev,
442 					      struct cros_ec_dev, class_dev);
443 
444 	for (len = 0; len < count; len++)
445 		if (!isalnum(buf[len]))
446 			break;
447 
448 	for (num = 0; num < ARRAY_SIZE(seqname); num++)
449 		if (!strncasecmp(seqname[num], buf, len))
450 			break;
451 
452 	if (num >= ARRAY_SIZE(seqname)) {
453 		ret = kstrtouint(buf, 0, &num);
454 		if (ret)
455 			return ret;
456 	}
457 
458 	msg = alloc_lightbar_cmd_msg(ec);
459 	if (!msg)
460 		return -ENOMEM;
461 
462 	param = (struct ec_params_lightbar *)msg->data;
463 	param->cmd = LIGHTBAR_CMD_SEQ;
464 	param->seq.num = num;
465 	ret = lb_throttle();
466 	if (ret)
467 		goto exit;
468 
469 	ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
470 	if (ret < 0)
471 		goto exit;
472 
473 	if (msg->result != EC_RES_SUCCESS) {
474 		ret = -EINVAL;
475 		goto exit;
476 	}
477 
478 	ret = count;
479 exit:
480 	kfree(msg);
481 	return ret;
482 }
483 
484 static ssize_t program_store(struct device *dev, struct device_attribute *attr,
485 			     const char *buf, size_t count)
486 {
487 	int extra_bytes, max_size, ret;
488 	struct ec_params_lightbar *param;
489 	struct cros_ec_command *msg;
490 	struct cros_ec_dev *ec = container_of(dev, struct cros_ec_dev,
491 					      class_dev);
492 
493 	/*
494 	 * We might need to reject the program for size reasons. The EC
495 	 * enforces a maximum program size, but we also don't want to try
496 	 * and send a program that is too big for the protocol. In order
497 	 * to ensure the latter, we also need to ensure we have extra bytes
498 	 * to represent the rest of the packet.
499 	 */
500 	extra_bytes = sizeof(*param) - sizeof(param->set_program.data);
501 	max_size = min(EC_LB_PROG_LEN, ec->ec_dev->max_request - extra_bytes);
502 	if (count > max_size) {
503 		dev_err(dev, "Program is %u bytes, too long to send (max: %u)",
504 			(unsigned int)count, max_size);
505 
506 		return -EINVAL;
507 	}
508 
509 	msg = alloc_lightbar_cmd_msg(ec);
510 	if (!msg)
511 		return -ENOMEM;
512 
513 	ret = lb_throttle();
514 	if (ret)
515 		goto exit;
516 
517 	dev_info(dev, "Copying %zu byte program to EC", count);
518 
519 	param = (struct ec_params_lightbar *)msg->data;
520 	param->cmd = LIGHTBAR_CMD_SET_PROGRAM;
521 
522 	param->set_program.size = count;
523 	memcpy(param->set_program.data, buf, count);
524 
525 	/*
526 	 * We need to set the message size manually or else it will use
527 	 * EC_LB_PROG_LEN. This might be too long, and the program
528 	 * is unlikely to use all of the space.
529 	 */
530 	msg->outsize = count + extra_bytes;
531 
532 	ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
533 	if (ret < 0)
534 		goto exit;
535 	if (msg->result != EC_RES_SUCCESS) {
536 		ret = -EINVAL;
537 		goto exit;
538 	}
539 
540 	ret = count;
541 exit:
542 	kfree(msg);
543 
544 	return ret;
545 }
546 
547 static ssize_t userspace_control_show(struct device *dev,
548 				      struct device_attribute *attr,
549 				      char *buf)
550 {
551 	return scnprintf(buf, PAGE_SIZE, "%d\n", userspace_control);
552 }
553 
554 static ssize_t userspace_control_store(struct device *dev,
555 				       struct device_attribute *attr,
556 				       const char *buf,
557 				       size_t count)
558 {
559 	bool enable;
560 	int ret;
561 
562 	ret = strtobool(buf, &enable);
563 	if (ret < 0)
564 		return ret;
565 
566 	userspace_control = enable;
567 
568 	return count;
569 }
570 
571 /* Module initialization */
572 
573 static DEVICE_ATTR_RW(interval_msec);
574 static DEVICE_ATTR_RO(version);
575 static DEVICE_ATTR_WO(brightness);
576 static DEVICE_ATTR_WO(led_rgb);
577 static DEVICE_ATTR_RW(sequence);
578 static DEVICE_ATTR_WO(program);
579 static DEVICE_ATTR_RW(userspace_control);
580 
581 static struct attribute *__lb_cmds_attrs[] = {
582 	&dev_attr_interval_msec.attr,
583 	&dev_attr_version.attr,
584 	&dev_attr_brightness.attr,
585 	&dev_attr_led_rgb.attr,
586 	&dev_attr_sequence.attr,
587 	&dev_attr_program.attr,
588 	&dev_attr_userspace_control.attr,
589 	NULL,
590 };
591 
592 bool ec_has_lightbar(struct cros_ec_dev *ec)
593 {
594 	return !!get_lightbar_version(ec, NULL, NULL);
595 }
596 
597 static umode_t cros_ec_lightbar_attrs_are_visible(struct kobject *kobj,
598 						  struct attribute *a, int n)
599 {
600 	struct device *dev = container_of(kobj, struct device, kobj);
601 	struct cros_ec_dev *ec = container_of(dev,
602 					      struct cros_ec_dev, class_dev);
603 	struct platform_device *pdev = to_platform_device(ec->dev);
604 	struct cros_ec_platform *pdata = pdev->dev.platform_data;
605 	int is_cros_ec;
606 
607 	is_cros_ec = strcmp(pdata->ec_name, CROS_EC_DEV_NAME);
608 
609 	if (is_cros_ec != 0)
610 		return 0;
611 
612 	/* Only instantiate this stuff if the EC has a lightbar */
613 	if (ec_has_lightbar(ec)) {
614 		ec_with_lightbar = ec;
615 		return a->mode;
616 	}
617 	return 0;
618 }
619 
620 struct attribute_group cros_ec_lightbar_attr_group = {
621 	.name = "lightbar",
622 	.attrs = __lb_cmds_attrs,
623 	.is_visible = cros_ec_lightbar_attrs_are_visible,
624 };
625