xref: /linux/drivers/platform/chrome/cros_ec_lightbar.c (revision df34ecc52526480a1a4bb78bc75bfb009c6076a4)
1 // SPDX-License-Identifier: GPL-2.0+
2 // Expose the Chromebook Pixel lightbar to userspace
3 //
4 // Copyright (C) 2014 Google, Inc.
5 
6 #include <linux/ctype.h>
7 #include <linux/delay.h>
8 #include <linux/device.h>
9 #include <linux/fs.h>
10 #include <linux/kobject.h>
11 #include <linux/kstrtox.h>
12 #include <linux/module.h>
13 #include <linux/platform_data/cros_ec_commands.h>
14 #include <linux/platform_data/cros_ec_proto.h>
15 #include <linux/platform_device.h>
16 #include <linux/sched.h>
17 #include <linux/types.h>
18 #include <linux/uaccess.h>
19 #include <linux/slab.h>
20 
21 #define DRV_NAME "cros-ec-lightbar"
22 
23 /* Rate-limit the lightbar interface to prevent DoS. */
24 static unsigned long lb_interval_jiffies = 50 * HZ / 1000;
25 
26 /*
27  * Whether or not we have given userspace control of the lightbar.
28  * If this is true, we won't do anything during suspend/resume.
29  */
30 static bool userspace_control;
31 
32 /*
33  * Whether or not the lightbar supports the manual suspend commands.
34  * The Pixel 2013 (Link) does not while all other devices with a
35  * lightbar do.
36  */
37 static bool has_manual_suspend;
38 
39 /*
40  * Lightbar version
41  */
42 static int lb_version;
43 
44 static ssize_t interval_msec_show(struct device *dev,
45 				  struct device_attribute *attr, char *buf)
46 {
47 	unsigned long msec = lb_interval_jiffies * 1000 / HZ;
48 
49 	return sysfs_emit(buf, "%lu\n", msec);
50 }
51 
52 static ssize_t interval_msec_store(struct device *dev,
53 				   struct device_attribute *attr,
54 				   const char *buf, size_t count)
55 {
56 	unsigned long msec;
57 
58 	if (kstrtoul(buf, 0, &msec))
59 		return -EINVAL;
60 
61 	lb_interval_jiffies = msec * HZ / 1000;
62 
63 	return count;
64 }
65 
66 static DEFINE_MUTEX(lb_mutex);
67 /* Return 0 if able to throttle correctly, error otherwise */
68 static int lb_throttle(void)
69 {
70 	static unsigned long last_access;
71 	unsigned long now, next_timeslot;
72 	long delay;
73 	int ret = 0;
74 
75 	mutex_lock(&lb_mutex);
76 
77 	now = jiffies;
78 	next_timeslot = last_access + lb_interval_jiffies;
79 
80 	if (time_before(now, next_timeslot)) {
81 		delay = (long)(next_timeslot) - (long)now;
82 		set_current_state(TASK_INTERRUPTIBLE);
83 		if (schedule_timeout(delay) > 0) {
84 			/* interrupted - just abort */
85 			ret = -EINTR;
86 			goto out;
87 		}
88 		now = jiffies;
89 	}
90 
91 	last_access = now;
92 out:
93 	mutex_unlock(&lb_mutex);
94 
95 	return ret;
96 }
97 
98 static struct cros_ec_command *alloc_lightbar_cmd_msg(struct cros_ec_dev *ec)
99 {
100 	int len = max(ec->ec_dev->max_response, ec->ec_dev->max_request);
101 	struct cros_ec_command *msg;
102 
103 	msg = kmalloc(sizeof(*msg) + len, GFP_KERNEL);
104 	if (!msg)
105 		return NULL;
106 
107 	msg->version = 0;
108 	msg->command = EC_CMD_LIGHTBAR_CMD + ec->cmd_offset;
109 	/*
110 	 * Default sizes for regular commands.
111 	 * Can be set smaller to optimize transfer,
112 	 * larger when sending large light sequences.
113 	 */
114 	msg->outsize = sizeof(struct ec_params_lightbar);
115 	msg->insize = sizeof(struct ec_response_lightbar);
116 
117 	return msg;
118 }
119 
120 static int get_lightbar_version(struct cros_ec_dev *ec,
121 				uint32_t *ver_ptr, uint32_t *flg_ptr)
122 {
123 	struct ec_params_lightbar *param;
124 	struct ec_response_lightbar *resp;
125 	struct cros_ec_command *msg;
126 	int ret;
127 
128 	msg = alloc_lightbar_cmd_msg(ec);
129 	if (!msg)
130 		return 0;
131 
132 	param = (struct ec_params_lightbar *)msg->data;
133 	param->cmd = LIGHTBAR_CMD_VERSION;
134 	msg->outsize = sizeof(param->cmd);
135 	msg->insize = sizeof(resp->version);
136 	ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
137 	if (ret < 0 && ret != -EINVAL) {
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 = to_cros_ec_dev(dev);
176 	int ret;
177 
178 	ret = lb_throttle();
179 	if (ret)
180 		return ret;
181 
182 	/* This should always succeed, because we check during init. */
183 	if (!get_lightbar_version(ec, &version, &flags))
184 		return -EIO;
185 
186 	return sysfs_emit(buf, "%d %d\n", version, flags);
187 }
188 
189 static ssize_t num_segments_show(struct device *dev,
190 				 struct device_attribute *attr, char *buf)
191 {
192 	struct ec_params_lightbar *param;
193 	struct ec_response_lightbar *resp;
194 	struct cros_ec_command *msg;
195 	struct cros_ec_dev *ec = to_cros_ec_dev(dev);
196 	uint32_t num = 0;
197 	int ret;
198 
199 	ret = lb_throttle();
200 	if (ret)
201 		return ret;
202 
203 	msg = alloc_lightbar_cmd_msg(ec);
204 	if (!msg)
205 		return -ENOMEM;
206 
207 	param = (struct ec_params_lightbar *)msg->data;
208 	param->cmd = LIGHTBAR_CMD_GET_PARAMS_V3;
209 	msg->outsize = sizeof(param->cmd);
210 	msg->insize = sizeof(resp->get_params_v3);
211 	ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
212 	if (ret < 0 && ret != -EINVAL)
213 		goto exit;
214 
215 	if (msg->result == EC_RES_SUCCESS) {
216 		resp = (struct ec_response_lightbar *)msg->data;
217 		num = resp->get_params_v3.reported_led_num;
218 	}
219 
220 	/*
221 	 * Anything else (ie, EC_RES_INVALID_COMMAND) - no direct control over
222 	 * LEDs, return that no leds are supported.
223 	 */
224 	ret = sysfs_emit(buf, "%u\n", num);
225 exit:
226 	kfree(msg);
227 	return ret;
228 }
229 
230 static ssize_t brightness_store(struct device *dev,
231 				struct device_attribute *attr,
232 				const char *buf, size_t count)
233 {
234 	struct ec_params_lightbar *param;
235 	struct cros_ec_command *msg;
236 	int ret;
237 	unsigned int val;
238 	struct cros_ec_dev *ec = to_cros_ec_dev(dev);
239 
240 	if (kstrtouint(buf, 0, &val))
241 		return -EINVAL;
242 
243 	msg = alloc_lightbar_cmd_msg(ec);
244 	if (!msg)
245 		return -ENOMEM;
246 
247 	param = (struct ec_params_lightbar *)msg->data;
248 	param->cmd = LIGHTBAR_CMD_SET_BRIGHTNESS;
249 	param->set_brightness.num = val;
250 	ret = lb_throttle();
251 	if (ret)
252 		goto exit;
253 
254 	ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
255 	if (ret < 0)
256 		goto exit;
257 
258 	ret = count;
259 exit:
260 	kfree(msg);
261 	return ret;
262 }
263 
264 
265 /*
266  * We expect numbers, and we'll keep reading until we find them, skipping over
267  * any whitespace (sysfs guarantees that the input is null-terminated). Every
268  * four numbers are sent to the lightbar as <LED,R,G,B>. We fail at the first
269  * parsing error, if we don't parse any numbers, or if we have numbers left
270  * over.
271  */
272 static ssize_t led_rgb_store(struct device *dev, struct device_attribute *attr,
273 			     const char *buf, size_t count)
274 {
275 	struct ec_params_lightbar *param;
276 	struct cros_ec_command *msg;
277 	struct cros_ec_dev *ec = to_cros_ec_dev(dev);
278 	unsigned int val[4];
279 	int ret, i = 0, j = 0, ok = 0;
280 
281 	msg = alloc_lightbar_cmd_msg(ec);
282 	if (!msg)
283 		return -ENOMEM;
284 
285 	do {
286 		/* Skip any whitespace */
287 		while (*buf && isspace(*buf))
288 			buf++;
289 
290 		if (!*buf)
291 			break;
292 
293 		ret = sscanf(buf, "%i", &val[i++]);
294 		if (ret == 0)
295 			goto exit;
296 
297 		if (i == 4) {
298 			param = (struct ec_params_lightbar *)msg->data;
299 			param->cmd = LIGHTBAR_CMD_SET_RGB;
300 			param->set_rgb.led = val[0];
301 			param->set_rgb.red = val[1];
302 			param->set_rgb.green = val[2];
303 			param->set_rgb.blue = val[3];
304 			/*
305 			 * Throttle only the first of every four transactions,
306 			 * so that the user can update all four LEDs at once.
307 			 */
308 			if ((j++ % 4) == 0) {
309 				ret = lb_throttle();
310 				if (ret)
311 					goto exit;
312 			}
313 
314 			ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
315 			if (ret < 0)
316 				goto exit;
317 
318 			i = 0;
319 			ok = 1;
320 		}
321 
322 		/* Skip over the number we just read */
323 		while (*buf && !isspace(*buf))
324 			buf++;
325 
326 	} while (*buf);
327 
328 exit:
329 	kfree(msg);
330 	return (ok && i == 0) ? count : -EINVAL;
331 }
332 
333 static char const *seqname[] = {
334 	"ERROR", "S5", "S3", "S0", "S5S3", "S3S0",
335 	"S0S3", "S3S5", "STOP", "RUN", "KONAMI",
336 	"TAP", "PROGRAM",
337 };
338 
339 static ssize_t sequence_show(struct device *dev,
340 			     struct device_attribute *attr, char *buf)
341 {
342 	struct ec_params_lightbar *param;
343 	struct ec_response_lightbar *resp;
344 	struct cros_ec_command *msg;
345 	int ret;
346 	struct cros_ec_dev *ec = to_cros_ec_dev(dev);
347 
348 	msg = alloc_lightbar_cmd_msg(ec);
349 	if (!msg)
350 		return -ENOMEM;
351 
352 	param = (struct ec_params_lightbar *)msg->data;
353 	param->cmd = LIGHTBAR_CMD_GET_SEQ;
354 	ret = lb_throttle();
355 	if (ret)
356 		goto exit;
357 
358 	ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
359 	if (ret < 0) {
360 		ret = sysfs_emit(buf, "XFER / EC ERROR %d / %d\n", ret, msg->result);
361 		goto exit;
362 	}
363 
364 	resp = (struct ec_response_lightbar *)msg->data;
365 	if (resp->get_seq.num >= ARRAY_SIZE(seqname))
366 		ret = sysfs_emit(buf, "%d\n", resp->get_seq.num);
367 	else
368 		ret = sysfs_emit(buf, "%s\n", seqname[resp->get_seq.num]);
369 
370 exit:
371 	kfree(msg);
372 	return ret;
373 }
374 
375 static int lb_send_empty_cmd(struct cros_ec_dev *ec, uint8_t cmd)
376 {
377 	struct ec_params_lightbar *param;
378 	struct cros_ec_command *msg;
379 	int ret;
380 
381 	msg = alloc_lightbar_cmd_msg(ec);
382 	if (!msg)
383 		return -ENOMEM;
384 
385 	param = (struct ec_params_lightbar *)msg->data;
386 	param->cmd = cmd;
387 
388 	ret = lb_throttle();
389 	if (ret)
390 		goto error;
391 
392 	ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
393 	if (ret < 0)
394 		goto error;
395 
396 	ret = 0;
397 error:
398 	kfree(msg);
399 
400 	return ret;
401 }
402 
403 static int lb_manual_suspend_ctrl(struct cros_ec_dev *ec, uint8_t enable)
404 {
405 	struct ec_params_lightbar *param;
406 	struct cros_ec_command *msg;
407 	int ret;
408 
409 	msg = alloc_lightbar_cmd_msg(ec);
410 	if (!msg)
411 		return -ENOMEM;
412 
413 	param = (struct ec_params_lightbar *)msg->data;
414 
415 	param->cmd = LIGHTBAR_CMD_MANUAL_SUSPEND_CTRL;
416 	param->manual_suspend_ctrl.enable = enable;
417 
418 	ret = lb_throttle();
419 	if (ret)
420 		goto error;
421 
422 	ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
423 	if (ret < 0)
424 		goto error;
425 
426 	ret = 0;
427 error:
428 	kfree(msg);
429 
430 	return ret;
431 }
432 
433 static ssize_t sequence_store(struct device *dev, struct device_attribute *attr,
434 			      const char *buf, size_t count)
435 {
436 	struct ec_params_lightbar *param;
437 	struct cros_ec_command *msg;
438 	unsigned int num;
439 	int ret, len;
440 	struct cros_ec_dev *ec = to_cros_ec_dev(dev);
441 
442 	for (len = 0; len < count; len++)
443 		if (!isalnum(buf[len]))
444 			break;
445 
446 	for (num = 0; num < ARRAY_SIZE(seqname); num++)
447 		if (!strncasecmp(seqname[num], buf, len))
448 			break;
449 
450 	if (num >= ARRAY_SIZE(seqname)) {
451 		ret = kstrtouint(buf, 0, &num);
452 		if (ret)
453 			return ret;
454 	}
455 
456 	msg = alloc_lightbar_cmd_msg(ec);
457 	if (!msg)
458 		return -ENOMEM;
459 
460 	param = (struct ec_params_lightbar *)msg->data;
461 	param->cmd = LIGHTBAR_CMD_SEQ;
462 	param->seq.num = num;
463 	msg->outsize = offsetof(typeof(*param), seq) + sizeof(param->seq);
464 	msg->insize = 0;
465 	ret = lb_throttle();
466 	if (ret)
467 		goto exit;
468 
469 	ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
470 	if (ret < 0)
471 		goto exit;
472 
473 	ret = count;
474 exit:
475 	kfree(msg);
476 	return ret;
477 }
478 
479 static ssize_t program_store(struct device *dev, struct device_attribute *attr,
480 			     const char *buf, size_t count)
481 {
482 	size_t extra_bytes, max_size;
483 	struct ec_params_lightbar *param;
484 	struct cros_ec_command *msg;
485 	struct cros_ec_dev *ec = to_cros_ec_dev(dev);
486 	int ret;
487 
488 	/*
489 	 * We might need to reject the program for size reasons. The EC
490 	 * enforces a maximum program size, but we also don't want to try
491 	 * and send a program that is too big for the protocol. In order
492 	 * to ensure the latter, we also need to ensure we have extra bytes
493 	 * to represent the rest of the packet.
494 	 * With V3, larger program can be sent, limited only by the EC.
495 	 * Only the protocol limit the payload size.
496 	 */
497 	if (lb_version < 3) {
498 		extra_bytes = sizeof(*param) - sizeof(param->set_program.data);
499 		max_size = min(EC_LB_PROG_LEN, ec->ec_dev->max_request - extra_bytes);
500 		if (count > max_size) {
501 			dev_err(dev, "Program is %zu bytes, too long to send (max: %zu)",
502 				count, max_size);
503 
504 			return -EINVAL;
505 		}
506 	} else {
507 		extra_bytes = offsetof(typeof(*param), set_program_ex) +
508 			sizeof(param->set_program_ex);
509 		max_size = ec->ec_dev->max_request - extra_bytes;
510 	}
511 
512 	msg = alloc_lightbar_cmd_msg(ec);
513 	if (!msg)
514 		return -ENOMEM;
515 
516 	ret = lb_throttle();
517 	if (ret)
518 		goto exit;
519 	param = (struct ec_params_lightbar *)msg->data;
520 	msg->insize = 0;
521 
522 	if (lb_version < 3) {
523 		dev_info(dev, "Copying %zu byte program to EC", count);
524 
525 		param->cmd = LIGHTBAR_CMD_SET_PROGRAM;
526 
527 		param->set_program.size = count;
528 		memcpy(param->set_program.data, buf, count);
529 
530 		/*
531 		 * We need to set the message size manually or else it will use
532 		 * EC_LB_PROG_LEN. This might be too long, and the program
533 		 * is unlikely to use all of the space.
534 		 */
535 		msg->outsize = count + extra_bytes;
536 
537 		ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
538 		if (ret < 0)
539 			goto exit;
540 	} else {
541 		size_t offset = 0;
542 		size_t payload = 0;
543 
544 		param->cmd = LIGHTBAR_CMD_SET_PROGRAM_EX;
545 		while (offset < count) {
546 			payload = min(max_size, count - offset);
547 			param->set_program_ex.offset = offset;
548 			param->set_program_ex.size = payload;
549 			memcpy(param->set_program_ex.data, &buf[offset], payload);
550 			msg->outsize = payload + extra_bytes;
551 
552 			ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
553 			if (ret < 0)
554 				goto exit;
555 			offset += payload;
556 		}
557 	}
558 	ret = count;
559 exit:
560 	kfree(msg);
561 
562 	return ret;
563 }
564 
565 static ssize_t userspace_control_show(struct device *dev,
566 				      struct device_attribute *attr,
567 				      char *buf)
568 {
569 	return sysfs_emit(buf, "%d\n", userspace_control);
570 }
571 
572 static ssize_t userspace_control_store(struct device *dev,
573 				       struct device_attribute *attr,
574 				       const char *buf,
575 				       size_t count)
576 {
577 	bool enable;
578 	int ret;
579 
580 	ret = kstrtobool(buf, &enable);
581 	if (ret < 0)
582 		return ret;
583 
584 	userspace_control = enable;
585 
586 	return count;
587 }
588 
589 /* Module initialization */
590 
591 static DEVICE_ATTR_RW(interval_msec);
592 static DEVICE_ATTR_RO(num_segments);
593 static DEVICE_ATTR_RO(version);
594 static DEVICE_ATTR_WO(brightness);
595 static DEVICE_ATTR_WO(led_rgb);
596 static DEVICE_ATTR_RW(sequence);
597 static DEVICE_ATTR_WO(program);
598 static DEVICE_ATTR_RW(userspace_control);
599 
600 static struct attribute *__lb_cmds_attrs[] = {
601 	&dev_attr_interval_msec.attr,
602 	&dev_attr_num_segments.attr,
603 	&dev_attr_version.attr,
604 	&dev_attr_brightness.attr,
605 	&dev_attr_led_rgb.attr,
606 	&dev_attr_sequence.attr,
607 	&dev_attr_program.attr,
608 	&dev_attr_userspace_control.attr,
609 	NULL,
610 };
611 
612 static const struct attribute_group cros_ec_lightbar_attr_group = {
613 	.name = "lightbar",
614 	.attrs = __lb_cmds_attrs,
615 };
616 
617 static int cros_ec_lightbar_probe(struct platform_device *pd)
618 {
619 	struct cros_ec_dev *ec_dev = dev_get_drvdata(pd->dev.parent);
620 	struct cros_ec_platform *pdata = dev_get_platdata(ec_dev->dev);
621 	struct device *dev = &pd->dev;
622 	int ret;
623 
624 	/*
625 	 * Only instantiate the lightbar if the EC name is 'cros_ec'. Other EC
626 	 * devices like 'cros_pd' doesn't have a lightbar.
627 	 */
628 	if (strcmp(pdata->ec_name, CROS_EC_DEV_NAME) != 0)
629 		return -ENODEV;
630 
631 	/*
632 	 * Ask then for the lightbar version, if it's 0 then the 'cros_ec'
633 	 * doesn't have a lightbar.
634 	 */
635 	if (!get_lightbar_version(ec_dev, &lb_version, NULL))
636 		return -ENODEV;
637 
638 	/* Take control of the lightbar from the EC. */
639 	has_manual_suspend = (lb_manual_suspend_ctrl(ec_dev, 1) != -EINVAL);
640 
641 	ret = sysfs_create_group(&ec_dev->class_dev.kobj,
642 				 &cros_ec_lightbar_attr_group);
643 	if (ret < 0)
644 		dev_err(dev, "failed to create %s attributes. err=%d\n",
645 			cros_ec_lightbar_attr_group.name, ret);
646 
647 	return ret;
648 }
649 
650 static void cros_ec_lightbar_remove(struct platform_device *pd)
651 {
652 	struct cros_ec_dev *ec_dev = dev_get_drvdata(pd->dev.parent);
653 
654 	sysfs_remove_group(&ec_dev->class_dev.kobj,
655 			   &cros_ec_lightbar_attr_group);
656 
657 	/* Let the EC take over the lightbar again. */
658 	if (has_manual_suspend)
659 		lb_manual_suspend_ctrl(ec_dev, 0);
660 }
661 
662 static int __maybe_unused cros_ec_lightbar_resume(struct device *dev)
663 {
664 	struct cros_ec_dev *ec_dev = dev_get_drvdata(dev->parent);
665 
666 	if (userspace_control || !has_manual_suspend)
667 		return 0;
668 
669 	return lb_send_empty_cmd(ec_dev, LIGHTBAR_CMD_RESUME);
670 }
671 
672 static int __maybe_unused cros_ec_lightbar_suspend(struct device *dev)
673 {
674 	struct cros_ec_dev *ec_dev = dev_get_drvdata(dev->parent);
675 
676 	if (userspace_control || !has_manual_suspend)
677 		return 0;
678 
679 	return lb_send_empty_cmd(ec_dev, LIGHTBAR_CMD_SUSPEND);
680 }
681 
682 static SIMPLE_DEV_PM_OPS(cros_ec_lightbar_pm_ops,
683 			 cros_ec_lightbar_suspend, cros_ec_lightbar_resume);
684 
685 static const struct platform_device_id cros_ec_lightbar_id[] = {
686 	{ DRV_NAME, 0 },
687 	{}
688 };
689 MODULE_DEVICE_TABLE(platform, cros_ec_lightbar_id);
690 
691 static struct platform_driver cros_ec_lightbar_driver = {
692 	.driver = {
693 		.name = DRV_NAME,
694 		.pm = &cros_ec_lightbar_pm_ops,
695 		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
696 	},
697 	.probe = cros_ec_lightbar_probe,
698 	.remove = cros_ec_lightbar_remove,
699 	.id_table = cros_ec_lightbar_id,
700 };
701 
702 module_platform_driver(cros_ec_lightbar_driver);
703 
704 MODULE_LICENSE("GPL");
705 MODULE_DESCRIPTION("Expose the Chromebook Pixel's lightbar to userspace");
706