root/drivers/platform/chrome/cros_ec_lightbar.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. interval_msec_show
  2. interval_msec_store
  3. lb_throttle
  4. alloc_lightbar_cmd_msg
  5. get_lightbar_version
  6. version_show
  7. brightness_store
  8. led_rgb_store
  9. sequence_show
  10. lb_send_empty_cmd
  11. lb_manual_suspend_ctrl
  12. sequence_store
  13. program_store
  14. userspace_control_show
  15. userspace_control_store
  16. cros_ec_lightbar_probe
  17. cros_ec_lightbar_remove
  18. cros_ec_lightbar_resume
  19. cros_ec_lightbar_suspend

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

/* [<][>][^][v][top][bottom][index][help] */