Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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