cros_ec_lightbar.c 14 KB

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