Blame view
drivers/platform/chrome/cros_ec_lightbar.c
13.9 KB
5414dd14a platform/chrome: ... |
1 2 3 4 |
// SPDX-License-Identifier: GPL-2.0+ // Expose the Chromebook Pixel lightbar to userspace // // Copyright (C) 2014 Google, Inc. |
f3f837e52 platform/chrome: ... |
5 |
|
f3f837e52 platform/chrome: ... |
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> |
f3f837e52 platform/chrome: ... |
11 |
#include <linux/module.h> |
840d9f131 mfd / platform: c... |
12 13 |
#include <linux/platform_data/cros_ec_commands.h> #include <linux/platform_data/cros_ec_proto.h> |
f3f837e52 platform/chrome: ... |
14 15 16 17 |
#include <linux/platform_device.h> #include <linux/sched.h> #include <linux/types.h> #include <linux/uaccess.h> |
a84117844 mfd: cros_ec: Use... |
18 |
#include <linux/slab.h> |
f3f837e52 platform/chrome: ... |
19 |
|
ecf8a6cd9 mfd / platform: c... |
20 |
#define DRV_NAME "cros-ec-lightbar" |
f3f837e52 platform/chrome: ... |
21 22 |
/* Rate-limit the lightbar interface to prevent DoS. */ static unsigned long lb_interval_jiffies = 50 * HZ / 1000; |
abbb054d5 platform/chrome: ... |
23 24 25 26 27 |
/* * 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; |
f3f837e52 platform/chrome: ... |
28 29 30 31 32 33 34 35 36 37 38 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 |
static ssize_t interval_msec_show(struct device *dev, struct device_attribute *attr, char *buf) { unsigned long msec = lb_interval_jiffies * 1000 / HZ; return scnprintf(buf, PAGE_SIZE, "%lu ", msec); } 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; } |
57b33ff07 mfd: cros_ec: Sup... |
82 |
static struct cros_ec_command *alloc_lightbar_cmd_msg(struct cros_ec_dev *ec) |
a84117844 mfd: cros_ec: Use... |
83 84 85 86 87 88 89 90 91 92 93 94 |
{ 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; |
57b33ff07 mfd: cros_ec: Sup... |
95 |
msg->command = EC_CMD_LIGHTBAR_CMD + ec->cmd_offset; |
a84117844 mfd: cros_ec: Use... |
96 97 98 99 100 |
msg->outsize = sizeof(struct ec_params_lightbar); msg->insize = sizeof(struct ec_response_lightbar); return msg; } |
f3f837e52 platform/chrome: ... |
101 |
|
57b33ff07 mfd: cros_ec: Sup... |
102 |
static int get_lightbar_version(struct cros_ec_dev *ec, |
f3f837e52 platform/chrome: ... |
103 104 105 106 |
uint32_t *ver_ptr, uint32_t *flg_ptr) { struct ec_params_lightbar *param; struct ec_response_lightbar *resp; |
a84117844 mfd: cros_ec: Use... |
107 |
struct cros_ec_command *msg; |
f3f837e52 platform/chrome: ... |
108 |
int ret; |
57b33ff07 mfd: cros_ec: Sup... |
109 |
msg = alloc_lightbar_cmd_msg(ec); |
a84117844 mfd: cros_ec: Use... |
110 |
if (!msg) |
f3f837e52 platform/chrome: ... |
111 |
return 0; |
a84117844 mfd: cros_ec: Use... |
112 113 |
param = (struct ec_params_lightbar *)msg->data; param->cmd = LIGHTBAR_CMD_VERSION; |
57b33ff07 mfd: cros_ec: Sup... |
114 |
ret = cros_ec_cmd_xfer(ec->ec_dev, msg); |
a84117844 mfd: cros_ec: Use... |
115 116 117 118 119 120 |
if (ret < 0) { ret = 0; goto exit; } switch (msg->result) { |
f3f837e52 platform/chrome: ... |
121 122 123 124 125 126 |
case EC_RES_INVALID_PARAM: /* Pixel had no version command. */ if (ver_ptr) *ver_ptr = 0; if (flg_ptr) *flg_ptr = 0; |
a84117844 mfd: cros_ec: Use... |
127 128 |
ret = 1; goto exit; |
f3f837e52 platform/chrome: ... |
129 130 |
case EC_RES_SUCCESS: |
a84117844 mfd: cros_ec: Use... |
131 |
resp = (struct ec_response_lightbar *)msg->data; |
f3f837e52 platform/chrome: ... |
132 133 134 135 136 137 |
/* Future devices w/lightbars should implement this command */ if (ver_ptr) *ver_ptr = resp->version.num; if (flg_ptr) *flg_ptr = resp->version.flags; |
a84117844 mfd: cros_ec: Use... |
138 139 |
ret = 1; goto exit; |
f3f837e52 platform/chrome: ... |
140 141 142 |
} /* Anything else (ie, EC_RES_INVALID_COMMAND) - no lightbar */ |
a84117844 mfd: cros_ec: Use... |
143 144 145 146 |
ret = 0; exit: kfree(msg); return ret; |
f3f837e52 platform/chrome: ... |
147 148 149 150 151 |
} static ssize_t version_show(struct device *dev, struct device_attribute *attr, char *buf) { |
a84117844 mfd: cros_ec: Use... |
152 |
uint32_t version = 0, flags = 0; |
79a3d6030 platform/chrome: ... |
153 |
struct cros_ec_dev *ec = to_cros_ec_dev(dev); |
f3f837e52 platform/chrome: ... |
154 155 156 157 158 159 160 161 162 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; return scnprintf(buf, PAGE_SIZE, "%d %d ", version, flags); } static ssize_t brightness_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct ec_params_lightbar *param; |
a84117844 mfd: cros_ec: Use... |
173 |
struct cros_ec_command *msg; |
f3f837e52 platform/chrome: ... |
174 175 |
int ret; unsigned int val; |
79a3d6030 platform/chrome: ... |
176 |
struct cros_ec_dev *ec = to_cros_ec_dev(dev); |
f3f837e52 platform/chrome: ... |
177 178 179 |
if (kstrtouint(buf, 0, &val)) return -EINVAL; |
57b33ff07 mfd: cros_ec: Sup... |
180 |
msg = alloc_lightbar_cmd_msg(ec); |
a84117844 mfd: cros_ec: Use... |
181 182 183 184 |
if (!msg) return -ENOMEM; param = (struct ec_params_lightbar *)msg->data; |
256ab950b mfd: cros_ec: rev... |
185 186 |
param->cmd = LIGHTBAR_CMD_SET_BRIGHTNESS; param->set_brightness.num = val; |
f3f837e52 platform/chrome: ... |
187 188 |
ret = lb_throttle(); if (ret) |
a84117844 mfd: cros_ec: Use... |
189 |
goto exit; |
f3f837e52 platform/chrome: ... |
190 |
|
57b33ff07 mfd: cros_ec: Sup... |
191 |
ret = cros_ec_cmd_xfer(ec->ec_dev, msg); |
f3f837e52 platform/chrome: ... |
192 |
if (ret < 0) |
a84117844 mfd: cros_ec: Use... |
193 |
goto exit; |
f3f837e52 platform/chrome: ... |
194 |
|
a84117844 mfd: cros_ec: Use... |
195 196 197 198 |
if (msg->result != EC_RES_SUCCESS) { ret = -EINVAL; goto exit; } |
f3f837e52 platform/chrome: ... |
199 |
|
a84117844 mfd: cros_ec: Use... |
200 201 202 203 |
ret = count; exit: kfree(msg); return ret; |
f3f837e52 platform/chrome: ... |
204 205 206 207 208 209 210 211 212 213 214 215 216 217 |
} /* * 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; |
a84117844 mfd: cros_ec: Use... |
218 |
struct cros_ec_command *msg; |
79a3d6030 platform/chrome: ... |
219 |
struct cros_ec_dev *ec = to_cros_ec_dev(dev); |
f3f837e52 platform/chrome: ... |
220 221 |
unsigned int val[4]; int ret, i = 0, j = 0, ok = 0; |
57b33ff07 mfd: cros_ec: Sup... |
222 |
msg = alloc_lightbar_cmd_msg(ec); |
a84117844 mfd: cros_ec: Use... |
223 224 |
if (!msg) return -ENOMEM; |
f3f837e52 platform/chrome: ... |
225 226 227 228 229 230 231 232 233 234 |
do { /* Skip any whitespace */ while (*buf && isspace(*buf)) buf++; if (!*buf) break; ret = sscanf(buf, "%i", &val[i++]); if (ret == 0) |
f14ae099b platform/chrome: ... |
235 |
goto exit; |
f3f837e52 platform/chrome: ... |
236 237 |
if (i == 4) { |
a84117844 mfd: cros_ec: Use... |
238 |
param = (struct ec_params_lightbar *)msg->data; |
256ab950b mfd: cros_ec: rev... |
239 240 241 242 243 |
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]; |
f3f837e52 platform/chrome: ... |
244 245 246 247 248 249 250 |
/* * 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) |
f14ae099b platform/chrome: ... |
251 |
goto exit; |
f3f837e52 platform/chrome: ... |
252 |
} |
57b33ff07 mfd: cros_ec: Sup... |
253 |
ret = cros_ec_cmd_xfer(ec->ec_dev, msg); |
f3f837e52 platform/chrome: ... |
254 |
if (ret < 0) |
a84117844 mfd: cros_ec: Use... |
255 |
goto exit; |
f3f837e52 platform/chrome: ... |
256 |
|
f14ae099b platform/chrome: ... |
257 |
if (msg->result != EC_RES_SUCCESS) |
a84117844 mfd: cros_ec: Use... |
258 |
goto exit; |
f3f837e52 platform/chrome: ... |
259 260 261 262 263 264 265 266 267 268 |
i = 0; ok = 1; } /* Skip over the number we just read */ while (*buf && !isspace(*buf)) buf++; } while (*buf); |
a84117844 mfd: cros_ec: Use... |
269 270 |
exit: kfree(msg); |
f3f837e52 platform/chrome: ... |
271 272 |
return (ok && i == 0) ? count : -EINVAL; } |
377415aba platform/chrome: ... |
273 |
static char const *seqname[] = { |
f3f837e52 platform/chrome: ... |
274 |
"ERROR", "S5", "S3", "S0", "S5S3", "S3S0", |
be3ebebf4 platform/chrome: ... |
275 276 |
"S0S3", "S3S5", "STOP", "RUN", "KONAMI", "TAP", "PROGRAM", |
f3f837e52 platform/chrome: ... |
277 278 279 280 281 282 283 |
}; static ssize_t sequence_show(struct device *dev, struct device_attribute *attr, char *buf) { struct ec_params_lightbar *param; struct ec_response_lightbar *resp; |
a84117844 mfd: cros_ec: Use... |
284 |
struct cros_ec_command *msg; |
f3f837e52 platform/chrome: ... |
285 |
int ret; |
79a3d6030 platform/chrome: ... |
286 |
struct cros_ec_dev *ec = to_cros_ec_dev(dev); |
f3f837e52 platform/chrome: ... |
287 |
|
57b33ff07 mfd: cros_ec: Sup... |
288 |
msg = alloc_lightbar_cmd_msg(ec); |
a84117844 mfd: cros_ec: Use... |
289 290 291 292 |
if (!msg) return -ENOMEM; param = (struct ec_params_lightbar *)msg->data; |
f3f837e52 platform/chrome: ... |
293 294 295 |
param->cmd = LIGHTBAR_CMD_GET_SEQ; ret = lb_throttle(); if (ret) |
a84117844 mfd: cros_ec: Use... |
296 |
goto exit; |
f3f837e52 platform/chrome: ... |
297 |
|
57b33ff07 mfd: cros_ec: Sup... |
298 |
ret = cros_ec_cmd_xfer(ec->ec_dev, msg); |
f3f837e52 platform/chrome: ... |
299 |
if (ret < 0) |
a84117844 mfd: cros_ec: Use... |
300 |
goto exit; |
f3f837e52 platform/chrome: ... |
301 |
|
a84117844 mfd: cros_ec: Use... |
302 303 304 305 306 307 |
if (msg->result != EC_RES_SUCCESS) { ret = scnprintf(buf, PAGE_SIZE, "ERROR: EC returned %d ", msg->result); goto exit; } |
f3f837e52 platform/chrome: ... |
308 |
|
a84117844 mfd: cros_ec: Use... |
309 |
resp = (struct ec_response_lightbar *)msg->data; |
f3f837e52 platform/chrome: ... |
310 |
if (resp->get_seq.num >= ARRAY_SIZE(seqname)) |
a84117844 mfd: cros_ec: Use... |
311 312 |
ret = scnprintf(buf, PAGE_SIZE, "%d ", resp->get_seq.num); |
f3f837e52 platform/chrome: ... |
313 |
else |
a84117844 mfd: cros_ec: Use... |
314 315 316 317 318 319 320 |
ret = scnprintf(buf, PAGE_SIZE, "%s ", seqname[resp->get_seq.num]); exit: kfree(msg); return ret; |
f3f837e52 platform/chrome: ... |
321 |
} |
405c84308 platform/chrome: ... |
322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 |
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; ret = cros_ec_cmd_xfer(ec->ec_dev, msg); if (ret < 0) goto error; if (msg->result != EC_RES_SUCCESS) { ret = -EINVAL; goto error; } ret = 0; error: kfree(msg); return ret; } |
ecf8a6cd9 mfd / platform: c... |
352 |
static int lb_manual_suspend_ctrl(struct cros_ec_dev *ec, uint8_t enable) |
405c84308 platform/chrome: ... |
353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 |
{ 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; ret = cros_ec_cmd_xfer(ec->ec_dev, msg); if (ret < 0) goto error; if (msg->result != EC_RES_SUCCESS) { ret = -EINVAL; goto error; } ret = 0; error: kfree(msg); return ret; } |
405c84308 platform/chrome: ... |
384 |
|
f3f837e52 platform/chrome: ... |
385 386 387 388 |
static ssize_t sequence_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct ec_params_lightbar *param; |
a84117844 mfd: cros_ec: Use... |
389 |
struct cros_ec_command *msg; |
f3f837e52 platform/chrome: ... |
390 391 |
unsigned int num; int ret, len; |
79a3d6030 platform/chrome: ... |
392 |
struct cros_ec_dev *ec = to_cros_ec_dev(dev); |
f3f837e52 platform/chrome: ... |
393 394 395 396 397 398 399 400 401 402 403 404 405 406 |
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; } |
88dfb8b43 platform/chrome: ... |
407 408 409 |
msg = alloc_lightbar_cmd_msg(ec); if (!msg) return -ENOMEM; |
a84117844 mfd: cros_ec: Use... |
410 |
param = (struct ec_params_lightbar *)msg->data; |
f3f837e52 platform/chrome: ... |
411 412 413 414 |
param->cmd = LIGHTBAR_CMD_SEQ; param->seq.num = num; ret = lb_throttle(); if (ret) |
88dfb8b43 platform/chrome: ... |
415 |
goto exit; |
f3f837e52 platform/chrome: ... |
416 |
|
57b33ff07 mfd: cros_ec: Sup... |
417 |
ret = cros_ec_cmd_xfer(ec->ec_dev, msg); |
f3f837e52 platform/chrome: ... |
418 |
if (ret < 0) |
88dfb8b43 platform/chrome: ... |
419 |
goto exit; |
f3f837e52 platform/chrome: ... |
420 |
|
88dfb8b43 platform/chrome: ... |
421 422 423 424 |
if (msg->result != EC_RES_SUCCESS) { ret = -EINVAL; goto exit; } |
f3f837e52 platform/chrome: ... |
425 |
|
88dfb8b43 platform/chrome: ... |
426 427 428 429 |
ret = count; exit: kfree(msg); return ret; |
f3f837e52 platform/chrome: ... |
430 |
} |
be3ebebf4 platform/chrome: ... |
431 432 433 434 435 436 |
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; |
79a3d6030 platform/chrome: ... |
437 |
struct cros_ec_dev *ec = to_cros_ec_dev(dev); |
be3ebebf4 platform/chrome: ... |
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 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 |
/* * 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; ret = cros_ec_cmd_xfer(ec->ec_dev, msg); if (ret < 0) goto exit; if (msg->result != EC_RES_SUCCESS) { ret = -EINVAL; goto exit; } ret = count; exit: kfree(msg); return ret; } |
abbb054d5 platform/chrome: ... |
492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 |
static ssize_t userspace_control_show(struct device *dev, struct device_attribute *attr, char *buf) { return scnprintf(buf, PAGE_SIZE, "%d ", userspace_control); } static ssize_t userspace_control_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { bool enable; int ret; ret = strtobool(buf, &enable); if (ret < 0) return ret; userspace_control = enable; return count; } |
f3f837e52 platform/chrome: ... |
516 517 518 519 520 521 522 |
/* 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); |
be3ebebf4 platform/chrome: ... |
523 |
static DEVICE_ATTR_WO(program); |
abbb054d5 platform/chrome: ... |
524 |
static DEVICE_ATTR_RW(userspace_control); |
be3ebebf4 platform/chrome: ... |
525 |
|
f3f837e52 platform/chrome: ... |
526 527 528 529 530 531 |
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, |
be3ebebf4 platform/chrome: ... |
532 |
&dev_attr_program.attr, |
abbb054d5 platform/chrome: ... |
533 |
&dev_attr_userspace_control.attr, |
f3f837e52 platform/chrome: ... |
534 535 |
NULL, }; |
f3f837e52 platform/chrome: ... |
536 |
|
81bc8c03e platform/chrome: ... |
537 |
static struct attribute_group cros_ec_lightbar_attr_group = { |
57b33ff07 mfd: cros_ec: Sup... |
538 539 |
.name = "lightbar", .attrs = __lb_cmds_attrs, |
57b33ff07 mfd: cros_ec: Sup... |
540 |
}; |
ecf8a6cd9 mfd / platform: c... |
541 542 543 544 |
static int cros_ec_lightbar_probe(struct platform_device *pd) { struct cros_ec_dev *ec_dev = dev_get_drvdata(pd->dev.parent); |
fd68bd0f5 platform/chrome: ... |
545 |
struct cros_ec_platform *pdata = dev_get_platdata(ec_dev->dev); |
ecf8a6cd9 mfd / platform: c... |
546 547 |
struct device *dev = &pd->dev; int ret; |
fd68bd0f5 platform/chrome: ... |
548 549 550 551 552 553 554 555 556 557 558 559 560 |
/* * 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; |
ecf8a6cd9 mfd / platform: c... |
561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 |
/* 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 ", cros_ec_lightbar_attr_group.name, ret); return ret; } static int cros_ec_lightbar_remove(struct platform_device *pd) { 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); return 0; } static int __maybe_unused cros_ec_lightbar_resume(struct device *dev) { |
c18e6ea10 platform/chrome: ... |
589 |
struct cros_ec_dev *ec_dev = dev_get_drvdata(dev->parent); |
ecf8a6cd9 mfd / platform: c... |
590 |
|
fd68bd0f5 platform/chrome: ... |
591 |
if (userspace_control) |
ecf8a6cd9 mfd / platform: c... |
592 593 594 595 596 597 598 |
return 0; return lb_send_empty_cmd(ec_dev, LIGHTBAR_CMD_RESUME); } static int __maybe_unused cros_ec_lightbar_suspend(struct device *dev) { |
c18e6ea10 platform/chrome: ... |
599 |
struct cros_ec_dev *ec_dev = dev_get_drvdata(dev->parent); |
ecf8a6cd9 mfd / platform: c... |
600 |
|
fd68bd0f5 platform/chrome: ... |
601 |
if (userspace_control) |
ecf8a6cd9 mfd / platform: c... |
602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 |
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); static struct platform_driver cros_ec_lightbar_driver = { .driver = { .name = DRV_NAME, .pm = &cros_ec_lightbar_pm_ops, }, .probe = cros_ec_lightbar_probe, .remove = cros_ec_lightbar_remove, }; module_platform_driver(cros_ec_lightbar_driver); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Expose the Chromebook Pixel's lightbar to userspace"); MODULE_ALIAS("platform:" DRV_NAME); |