cros_ec_lightbar.c 13.8 KB
Newer Older
1 2 3 4
// SPDX-License-Identifier: GPL-2.0+
// Expose the Chromebook Pixel lightbar to userspace
//
// Copyright (C) 2014 Google, Inc.
5 6 7 8 9 10

#include <linux/ctype.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/kobject.h>
11
#include <linux/kstrtox.h>
12
#include <linux/mod_devicetable.h>
13
#include <linux/module.h>
14 15
#include <linux/platform_data/cros_ec_commands.h>
#include <linux/platform_data/cros_ec_proto.h>
16 17 18 19
#include <linux/platform_device.h>
#include <linux/sched.h>
#include <linux/types.h>
#include <linux/uaccess.h>
20
#include <linux/slab.h>
21

22 23
#define DRV_NAME "cros-ec-lightbar"

24 25 26
/* Rate-limit the lightbar interface to prevent DoS. */
static unsigned long lb_interval_jiffies = 50 * HZ / 1000;

27 28 29 30 31 32
/*
 * Whether or not we have given userspace control of the lightbar.
 * If this is true, we won't do anything during suspend/resume.
 */
static bool userspace_control;

33 34 35 36 37
static ssize_t interval_msec_show(struct device *dev,
				  struct device_attribute *attr, char *buf)
{
	unsigned long msec = lb_interval_jiffies * 1000 / HZ;

38
	return sysfs_emit(buf, "%lu\n", msec);
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
}

static ssize_t interval_msec_store(struct device *dev,
				   struct device_attribute *attr,
				   const char *buf, size_t count)
{
	unsigned long msec;

	if (kstrtoul(buf, 0, &msec))
		return -EINVAL;

	lb_interval_jiffies = msec * HZ / 1000;

	return count;
}

static DEFINE_MUTEX(lb_mutex);
/* Return 0 if able to throttle correctly, error otherwise */
static int lb_throttle(void)
{
	static unsigned long last_access;
	unsigned long now, next_timeslot;
	long delay;
	int ret = 0;

	mutex_lock(&lb_mutex);

	now = jiffies;
	next_timeslot = last_access + lb_interval_jiffies;

	if (time_before(now, next_timeslot)) {
		delay = (long)(next_timeslot) - (long)now;
		set_current_state(TASK_INTERRUPTIBLE);
		if (schedule_timeout(delay) > 0) {
			/* interrupted - just abort */
			ret = -EINTR;
			goto out;
		}
		now = jiffies;
	}

	last_access = now;
out:
	mutex_unlock(&lb_mutex);

	return ret;
}

87
static struct cros_ec_command *alloc_lightbar_cmd_msg(struct cros_ec_dev *ec)
88 89 90 91 92 93 94 95 96 97 98 99
{
	struct cros_ec_command *msg;
	int len;

	len = max(sizeof(struct ec_params_lightbar),
		  sizeof(struct ec_response_lightbar));

	msg = kmalloc(sizeof(*msg) + len, GFP_KERNEL);
	if (!msg)
		return NULL;

	msg->version = 0;
100
	msg->command = EC_CMD_LIGHTBAR_CMD + ec->cmd_offset;
101 102 103 104 105
	msg->outsize = sizeof(struct ec_params_lightbar);
	msg->insize = sizeof(struct ec_response_lightbar);

	return msg;
}
106

107
static int get_lightbar_version(struct cros_ec_dev *ec,
108 109 110 111
				uint32_t *ver_ptr, uint32_t *flg_ptr)
{
	struct ec_params_lightbar *param;
	struct ec_response_lightbar *resp;
112
	struct cros_ec_command *msg;
113 114
	int ret;

115
	msg = alloc_lightbar_cmd_msg(ec);
116
	if (!msg)
117 118
		return 0;

119 120
	param = (struct ec_params_lightbar *)msg->data;
	param->cmd = LIGHTBAR_CMD_VERSION;
121 122
	msg->outsize = sizeof(param->cmd);
	msg->result = sizeof(resp->version);
123
	ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
124
	if (ret < 0 && ret != -EINVAL) {
125 126 127 128 129
		ret = 0;
		goto exit;
	}

	switch (msg->result) {
130 131 132 133 134 135
	case EC_RES_INVALID_PARAM:
		/* Pixel had no version command. */
		if (ver_ptr)
			*ver_ptr = 0;
		if (flg_ptr)
			*flg_ptr = 0;
136 137
		ret = 1;
		goto exit;
138 139

	case EC_RES_SUCCESS:
140
		resp = (struct ec_response_lightbar *)msg->data;
141 142 143 144 145 146

		/* Future devices w/lightbars should implement this command */
		if (ver_ptr)
			*ver_ptr = resp->version.num;
		if (flg_ptr)
			*flg_ptr = resp->version.flags;
147 148
		ret = 1;
		goto exit;
149 150 151
	}

	/* Anything else (ie, EC_RES_INVALID_COMMAND) - no lightbar */
152 153 154 155
	ret = 0;
exit:
	kfree(msg);
	return ret;
156 157 158 159 160
}

static ssize_t version_show(struct device *dev,
			    struct device_attribute *attr, char *buf)
{
161
	uint32_t version = 0, flags = 0;
162
	struct cros_ec_dev *ec = to_cros_ec_dev(dev);
163 164 165 166 167 168 169 170 171 172
	int ret;

	ret = lb_throttle();
	if (ret)
		return ret;

	/* This should always succeed, because we check during init. */
	if (!get_lightbar_version(ec, &version, &flags))
		return -EIO;

173
	return sysfs_emit(buf, "%d %d\n", version, flags);
174 175 176 177 178 179 180
}

static ssize_t brightness_store(struct device *dev,
				struct device_attribute *attr,
				const char *buf, size_t count)
{
	struct ec_params_lightbar *param;
181
	struct cros_ec_command *msg;
182 183
	int ret;
	unsigned int val;
184
	struct cros_ec_dev *ec = to_cros_ec_dev(dev);
185 186 187 188

	if (kstrtouint(buf, 0, &val))
		return -EINVAL;

189
	msg = alloc_lightbar_cmd_msg(ec);
190 191 192 193
	if (!msg)
		return -ENOMEM;

	param = (struct ec_params_lightbar *)msg->data;
194 195
	param->cmd = LIGHTBAR_CMD_SET_BRIGHTNESS;
	param->set_brightness.num = val;
196 197
	ret = lb_throttle();
	if (ret)
198
		goto exit;
199

200
	ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
201
	if (ret < 0)
202
		goto exit;
203

204 205 206 207
	ret = count;
exit:
	kfree(msg);
	return ret;
208 209 210 211 212 213 214 215 216 217 218 219 220 221
}


/*
 * We expect numbers, and we'll keep reading until we find them, skipping over
 * any whitespace (sysfs guarantees that the input is null-terminated). Every
 * four numbers are sent to the lightbar as <LED,R,G,B>. We fail at the first
 * parsing error, if we don't parse any numbers, or if we have numbers left
 * over.
 */
static ssize_t led_rgb_store(struct device *dev, struct device_attribute *attr,
			     const char *buf, size_t count)
{
	struct ec_params_lightbar *param;
222
	struct cros_ec_command *msg;
223
	struct cros_ec_dev *ec = to_cros_ec_dev(dev);
224 225 226
	unsigned int val[4];
	int ret, i = 0, j = 0, ok = 0;

227
	msg = alloc_lightbar_cmd_msg(ec);
228 229 230
	if (!msg)
		return -ENOMEM;

231 232 233 234 235 236 237 238 239 240
	do {
		/* Skip any whitespace */
		while (*buf && isspace(*buf))
			buf++;

		if (!*buf)
			break;

		ret = sscanf(buf, "%i", &val[i++]);
		if (ret == 0)
241
			goto exit;
242 243

		if (i == 4) {
244
			param = (struct ec_params_lightbar *)msg->data;
245 246 247 248 249
			param->cmd = LIGHTBAR_CMD_SET_RGB;
			param->set_rgb.led = val[0];
			param->set_rgb.red = val[1];
			param->set_rgb.green = val[2];
			param->set_rgb.blue = val[3];
250 251 252 253 254 255 256
			/*
			 * Throttle only the first of every four transactions,
			 * so that the user can update all four LEDs at once.
			 */
			if ((j++ % 4) == 0) {
				ret = lb_throttle();
				if (ret)
257
					goto exit;
258 259
			}

260
			ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
261
			if (ret < 0)
262
				goto exit;
263 264 265 266 267 268 269 270 271 272 273

			i = 0;
			ok = 1;
		}

		/* Skip over the number we just read */
		while (*buf && !isspace(*buf))
			buf++;

	} while (*buf);

274 275
exit:
	kfree(msg);
276 277 278
	return (ok && i == 0) ? count : -EINVAL;
}

279
static char const *seqname[] = {
280
	"ERROR", "S5", "S3", "S0", "S5S3", "S3S0",
281 282
	"S0S3", "S3S5", "STOP", "RUN", "KONAMI",
	"TAP", "PROGRAM",
283 284 285 286 287 288 289
};

static ssize_t sequence_show(struct device *dev,
			     struct device_attribute *attr, char *buf)
{
	struct ec_params_lightbar *param;
	struct ec_response_lightbar *resp;
290
	struct cros_ec_command *msg;
291
	int ret;
292
	struct cros_ec_dev *ec = to_cros_ec_dev(dev);
293

294
	msg = alloc_lightbar_cmd_msg(ec);
295 296 297 298
	if (!msg)
		return -ENOMEM;

	param = (struct ec_params_lightbar *)msg->data;
299 300 301
	param->cmd = LIGHTBAR_CMD_GET_SEQ;
	ret = lb_throttle();
	if (ret)
302
		goto exit;
303

304
	ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
305
	if (ret < 0) {
306
		ret = sysfs_emit(buf, "XFER / EC ERROR %d / %d\n", ret, msg->result);
307
		goto exit;
308
	}
309

310
	resp = (struct ec_response_lightbar *)msg->data;
311
	if (resp->get_seq.num >= ARRAY_SIZE(seqname))
312
		ret = sysfs_emit(buf, "%d\n", resp->get_seq.num);
313
	else
314
		ret = sysfs_emit(buf, "%s\n", seqname[resp->get_seq.num]);
315 316 317 318

exit:
	kfree(msg);
	return ret;
319 320
}

321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337
static int lb_send_empty_cmd(struct cros_ec_dev *ec, uint8_t cmd)
{
	struct ec_params_lightbar *param;
	struct cros_ec_command *msg;
	int ret;

	msg = alloc_lightbar_cmd_msg(ec);
	if (!msg)
		return -ENOMEM;

	param = (struct ec_params_lightbar *)msg->data;
	param->cmd = cmd;

	ret = lb_throttle();
	if (ret)
		goto error;

338
	ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
339 340
	if (ret < 0)
		goto error;
341

342 343 344 345 346 347 348
	ret = 0;
error:
	kfree(msg);

	return ret;
}

349
static int lb_manual_suspend_ctrl(struct cros_ec_dev *ec, uint8_t enable)
350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367
{
	struct ec_params_lightbar *param;
	struct cros_ec_command *msg;
	int ret;

	msg = alloc_lightbar_cmd_msg(ec);
	if (!msg)
		return -ENOMEM;

	param = (struct ec_params_lightbar *)msg->data;

	param->cmd = LIGHTBAR_CMD_MANUAL_SUSPEND_CTRL;
	param->manual_suspend_ctrl.enable = enable;

	ret = lb_throttle();
	if (ret)
		goto error;

368
	ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
369 370
	if (ret < 0)
		goto error;
371

372 373 374 375 376 377 378
	ret = 0;
error:
	kfree(msg);

	return ret;
}

379 380 381 382
static ssize_t sequence_store(struct device *dev, struct device_attribute *attr,
			      const char *buf, size_t count)
{
	struct ec_params_lightbar *param;
383
	struct cros_ec_command *msg;
384 385
	unsigned int num;
	int ret, len;
386
	struct cros_ec_dev *ec = to_cros_ec_dev(dev);
387 388 389 390 391 392 393 394 395 396 397 398 399 400 401

	for (len = 0; len < count; len++)
		if (!isalnum(buf[len]))
			break;

	for (num = 0; num < ARRAY_SIZE(seqname); num++)
		if (!strncasecmp(seqname[num], buf, len))
			break;

	if (num >= ARRAY_SIZE(seqname)) {
		ret = kstrtouint(buf, 0, &num);
		if (ret)
			return ret;
	}

402 403 404 405
	msg = alloc_lightbar_cmd_msg(ec);
	if (!msg)
		return -ENOMEM;

406
	param = (struct ec_params_lightbar *)msg->data;
407 408 409 410
	param->cmd = LIGHTBAR_CMD_SEQ;
	param->seq.num = num;
	ret = lb_throttle();
	if (ret)
411
		goto exit;
412

413
	ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
414
	if (ret < 0)
415
		goto exit;
416

417 418 419 420
	ret = count;
exit:
	kfree(msg);
	return ret;
421 422
}

423 424 425 426 427 428
static ssize_t program_store(struct device *dev, struct device_attribute *attr,
			     const char *buf, size_t count)
{
	int extra_bytes, max_size, ret;
	struct ec_params_lightbar *param;
	struct cros_ec_command *msg;
429
	struct cros_ec_dev *ec = to_cros_ec_dev(dev);
430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469

	/*
	 * We might need to reject the program for size reasons. The EC
	 * enforces a maximum program size, but we also don't want to try
	 * and send a program that is too big for the protocol. In order
	 * to ensure the latter, we also need to ensure we have extra bytes
	 * to represent the rest of the packet.
	 */
	extra_bytes = sizeof(*param) - sizeof(param->set_program.data);
	max_size = min(EC_LB_PROG_LEN, ec->ec_dev->max_request - extra_bytes);
	if (count > max_size) {
		dev_err(dev, "Program is %u bytes, too long to send (max: %u)",
			(unsigned int)count, max_size);

		return -EINVAL;
	}

	msg = alloc_lightbar_cmd_msg(ec);
	if (!msg)
		return -ENOMEM;

	ret = lb_throttle();
	if (ret)
		goto exit;

	dev_info(dev, "Copying %zu byte program to EC", count);

	param = (struct ec_params_lightbar *)msg->data;
	param->cmd = LIGHTBAR_CMD_SET_PROGRAM;

	param->set_program.size = count;
	memcpy(param->set_program.data, buf, count);

	/*
	 * We need to set the message size manually or else it will use
	 * EC_LB_PROG_LEN. This might be too long, and the program
	 * is unlikely to use all of the space.
	 */
	msg->outsize = count + extra_bytes;

470
	ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
471 472 473 474 475 476 477 478 479 480
	if (ret < 0)
		goto exit;

	ret = count;
exit:
	kfree(msg);

	return ret;
}

481 482 483 484
static ssize_t userspace_control_show(struct device *dev,
				      struct device_attribute *attr,
				      char *buf)
{
485
	return sysfs_emit(buf, "%d\n", userspace_control);
486 487 488 489 490 491 492 493 494 495
}

static ssize_t userspace_control_store(struct device *dev,
				       struct device_attribute *attr,
				       const char *buf,
				       size_t count)
{
	bool enable;
	int ret;

496
	ret = kstrtobool(buf, &enable);
497 498 499 500 501 502 503 504
	if (ret < 0)
		return ret;

	userspace_control = enable;

	return count;
}

505 506 507 508 509 510 511
/* Module initialization */

static DEVICE_ATTR_RW(interval_msec);
static DEVICE_ATTR_RO(version);
static DEVICE_ATTR_WO(brightness);
static DEVICE_ATTR_WO(led_rgb);
static DEVICE_ATTR_RW(sequence);
512
static DEVICE_ATTR_WO(program);
513
static DEVICE_ATTR_RW(userspace_control);
514

515 516 517 518 519 520
static struct attribute *__lb_cmds_attrs[] = {
	&dev_attr_interval_msec.attr,
	&dev_attr_version.attr,
	&dev_attr_brightness.attr,
	&dev_attr_led_rgb.attr,
	&dev_attr_sequence.attr,
521
	&dev_attr_program.attr,
522
	&dev_attr_userspace_control.attr,
523 524 525
	NULL,
};

526
static const struct attribute_group cros_ec_lightbar_attr_group = {
527 528 529
	.name = "lightbar",
	.attrs = __lb_cmds_attrs,
};
530 531 532 533

static int cros_ec_lightbar_probe(struct platform_device *pd)
{
	struct cros_ec_dev *ec_dev = dev_get_drvdata(pd->dev.parent);
534
	struct cros_ec_platform *pdata = dev_get_platdata(ec_dev->dev);
535 536 537
	struct device *dev = &pd->dev;
	int ret;

538 539 540 541 542 543 544 545 546 547 548 549 550 551
	/*
	 * Only instantiate the lightbar if the EC name is 'cros_ec'. Other EC
	 * devices like 'cros_pd' doesn't have a lightbar.
	 */
	if (strcmp(pdata->ec_name, CROS_EC_DEV_NAME) != 0)
		return -ENODEV;

	/*
	 * Ask then for the lightbar version, if it's 0 then the 'cros_ec'
	 * doesn't have a lightbar.
	 */
	if (!get_lightbar_version(ec_dev, NULL, NULL))
		return -ENODEV;

552 553 554 555 556 557 558 559 560 561 562 563
	/* Take control of the lightbar from the EC. */
	lb_manual_suspend_ctrl(ec_dev, 1);

	ret = sysfs_create_group(&ec_dev->class_dev.kobj,
				 &cros_ec_lightbar_attr_group);
	if (ret < 0)
		dev_err(dev, "failed to create %s attributes. err=%d\n",
			cros_ec_lightbar_attr_group.name, ret);

	return ret;
}

564
static void cros_ec_lightbar_remove(struct platform_device *pd)
565 566 567 568 569 570 571 572 573 574 575 576
{
	struct cros_ec_dev *ec_dev = dev_get_drvdata(pd->dev.parent);

	sysfs_remove_group(&ec_dev->class_dev.kobj,
			   &cros_ec_lightbar_attr_group);

	/* Let the EC take over the lightbar again. */
	lb_manual_suspend_ctrl(ec_dev, 0);
}

static int __maybe_unused cros_ec_lightbar_resume(struct device *dev)
{
577
	struct cros_ec_dev *ec_dev = dev_get_drvdata(dev->parent);
578

579
	if (userspace_control)
580 581 582 583 584 585 586
		return 0;

	return lb_send_empty_cmd(ec_dev, LIGHTBAR_CMD_RESUME);
}

static int __maybe_unused cros_ec_lightbar_suspend(struct device *dev)
{
587
	struct cros_ec_dev *ec_dev = dev_get_drvdata(dev->parent);
588

589
	if (userspace_control)
590 591 592 593 594 595 596 597
		return 0;

	return lb_send_empty_cmd(ec_dev, LIGHTBAR_CMD_SUSPEND);
}

static SIMPLE_DEV_PM_OPS(cros_ec_lightbar_pm_ops,
			 cros_ec_lightbar_suspend, cros_ec_lightbar_resume);

598 599 600 601 602 603
static const struct platform_device_id cros_ec_lightbar_id[] = {
	{ DRV_NAME, 0 },
	{}
};
MODULE_DEVICE_TABLE(platform, cros_ec_lightbar_id);

604 605 606 607
static struct platform_driver cros_ec_lightbar_driver = {
	.driver = {
		.name = DRV_NAME,
		.pm = &cros_ec_lightbar_pm_ops,
608
		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
609 610
	},
	.probe = cros_ec_lightbar_probe,
611
	.remove_new = cros_ec_lightbar_remove,
612
	.id_table = cros_ec_lightbar_id,
613 614 615 616 617 618
};

module_platform_driver(cros_ec_lightbar_driver);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Expose the Chromebook Pixel's lightbar to userspace");