GNU Linux-libre 4.9.328-gnu1
[releases.git] / drivers / platform / chrome / cros_ec_lightbar.c
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 #include "cros_ec_dev.h"
37
38 /* Rate-limit the lightbar interface to prevent DoS. */
39 static unsigned long lb_interval_jiffies = 50 * HZ / 1000;
40
41 static ssize_t interval_msec_show(struct device *dev,
42                                   struct device_attribute *attr, char *buf)
43 {
44         unsigned long msec = lb_interval_jiffies * 1000 / HZ;
45
46         return scnprintf(buf, PAGE_SIZE, "%lu\n", msec);
47 }
48
49 static ssize_t interval_msec_store(struct device *dev,
50                                    struct device_attribute *attr,
51                                    const char *buf, size_t count)
52 {
53         unsigned long msec;
54
55         if (kstrtoul(buf, 0, &msec))
56                 return -EINVAL;
57
58         lb_interval_jiffies = msec * HZ / 1000;
59
60         return count;
61 }
62
63 static DEFINE_MUTEX(lb_mutex);
64 /* Return 0 if able to throttle correctly, error otherwise */
65 static int lb_throttle(void)
66 {
67         static unsigned long last_access;
68         unsigned long now, next_timeslot;
69         long delay;
70         int ret = 0;
71
72         mutex_lock(&lb_mutex);
73
74         now = jiffies;
75         next_timeslot = last_access + lb_interval_jiffies;
76
77         if (time_before(now, next_timeslot)) {
78                 delay = (long)(next_timeslot) - (long)now;
79                 set_current_state(TASK_INTERRUPTIBLE);
80                 if (schedule_timeout(delay) > 0) {
81                         /* interrupted - just abort */
82                         ret = -EINTR;
83                         goto out;
84                 }
85                 now = jiffies;
86         }
87
88         last_access = now;
89 out:
90         mutex_unlock(&lb_mutex);
91
92         return ret;
93 }
94
95 static struct cros_ec_command *alloc_lightbar_cmd_msg(struct cros_ec_dev *ec)
96 {
97         struct cros_ec_command *msg;
98         int len;
99
100         len = max(sizeof(struct ec_params_lightbar),
101                   sizeof(struct ec_response_lightbar));
102
103         msg = kmalloc(sizeof(*msg) + len, GFP_KERNEL);
104         if (!msg)
105                 return NULL;
106
107         msg->version = 0;
108         msg->command = EC_CMD_LIGHTBAR_CMD + ec->cmd_offset;
109         msg->outsize = sizeof(struct ec_params_lightbar);
110         msg->insize = sizeof(struct ec_response_lightbar);
111
112         return msg;
113 }
114
115 static int get_lightbar_version(struct cros_ec_dev *ec,
116                                 uint32_t *ver_ptr, uint32_t *flg_ptr)
117 {
118         struct ec_params_lightbar *param;
119         struct ec_response_lightbar *resp;
120         struct cros_ec_command *msg;
121         int ret;
122
123         msg = alloc_lightbar_cmd_msg(ec);
124         if (!msg)
125                 return 0;
126
127         param = (struct ec_params_lightbar *)msg->data;
128         param->cmd = LIGHTBAR_CMD_VERSION;
129         ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
130         if (ret < 0) {
131                 ret = 0;
132                 goto exit;
133         }
134
135         switch (msg->result) {
136         case EC_RES_INVALID_PARAM:
137                 /* Pixel had no version command. */
138                 if (ver_ptr)
139                         *ver_ptr = 0;
140                 if (flg_ptr)
141                         *flg_ptr = 0;
142                 ret = 1;
143                 goto exit;
144
145         case EC_RES_SUCCESS:
146                 resp = (struct ec_response_lightbar *)msg->data;
147
148                 /* Future devices w/lightbars should implement this command */
149                 if (ver_ptr)
150                         *ver_ptr = resp->version.num;
151                 if (flg_ptr)
152                         *flg_ptr = resp->version.flags;
153                 ret = 1;
154                 goto exit;
155         }
156
157         /* Anything else (ie, EC_RES_INVALID_COMMAND) - no lightbar */
158         ret = 0;
159 exit:
160         kfree(msg);
161         return ret;
162 }
163
164 static ssize_t version_show(struct device *dev,
165                             struct device_attribute *attr, char *buf)
166 {
167         uint32_t version = 0, flags = 0;
168         struct cros_ec_dev *ec = container_of(dev,
169                                               struct cros_ec_dev, class_dev);
170         int ret;
171
172         ret = lb_throttle();
173         if (ret)
174                 return ret;
175
176         /* This should always succeed, because we check during init. */
177         if (!get_lightbar_version(ec, &version, &flags))
178                 return -EIO;
179
180         return scnprintf(buf, PAGE_SIZE, "%d %d\n", version, flags);
181 }
182
183 static ssize_t brightness_store(struct device *dev,
184                                 struct device_attribute *attr,
185                                 const char *buf, size_t count)
186 {
187         struct ec_params_lightbar *param;
188         struct cros_ec_command *msg;
189         int ret;
190         unsigned int val;
191         struct cros_ec_dev *ec = container_of(dev,
192                                               struct cros_ec_dev, class_dev);
193
194         if (kstrtouint(buf, 0, &val))
195                 return -EINVAL;
196
197         msg = alloc_lightbar_cmd_msg(ec);
198         if (!msg)
199                 return -ENOMEM;
200
201         param = (struct ec_params_lightbar *)msg->data;
202         param->cmd = LIGHTBAR_CMD_SET_BRIGHTNESS;
203         param->set_brightness.num = val;
204         ret = lb_throttle();
205         if (ret)
206                 goto exit;
207
208         ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
209         if (ret < 0)
210                 goto exit;
211
212         if (msg->result != EC_RES_SUCCESS) {
213                 ret = -EINVAL;
214                 goto exit;
215         }
216
217         ret = count;
218 exit:
219         kfree(msg);
220         return ret;
221 }
222
223
224 /*
225  * We expect numbers, and we'll keep reading until we find them, skipping over
226  * any whitespace (sysfs guarantees that the input is null-terminated). Every
227  * four numbers are sent to the lightbar as <LED,R,G,B>. We fail at the first
228  * parsing error, if we don't parse any numbers, or if we have numbers left
229  * over.
230  */
231 static ssize_t led_rgb_store(struct device *dev, struct device_attribute *attr,
232                              const char *buf, size_t count)
233 {
234         struct ec_params_lightbar *param;
235         struct cros_ec_command *msg;
236         struct cros_ec_dev *ec = container_of(dev,
237                                               struct cros_ec_dev, class_dev);
238         unsigned int val[4];
239         int ret, i = 0, j = 0, ok = 0;
240
241         msg = alloc_lightbar_cmd_msg(ec);
242         if (!msg)
243                 return -ENOMEM;
244
245         do {
246                 /* Skip any whitespace */
247                 while (*buf && isspace(*buf))
248                         buf++;
249
250                 if (!*buf)
251                         break;
252
253                 ret = sscanf(buf, "%i", &val[i++]);
254                 if (ret == 0)
255                         goto exit;
256
257                 if (i == 4) {
258                         param = (struct ec_params_lightbar *)msg->data;
259                         param->cmd = LIGHTBAR_CMD_SET_RGB;
260                         param->set_rgb.led = val[0];
261                         param->set_rgb.red = val[1];
262                         param->set_rgb.green = val[2];
263                         param->set_rgb.blue = val[3];
264                         /*
265                          * Throttle only the first of every four transactions,
266                          * so that the user can update all four LEDs at once.
267                          */
268                         if ((j++ % 4) == 0) {
269                                 ret = lb_throttle();
270                                 if (ret)
271                                         goto exit;
272                         }
273
274                         ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
275                         if (ret < 0)
276                                 goto exit;
277
278                         if (msg->result != EC_RES_SUCCESS)
279                                 goto exit;
280
281                         i = 0;
282                         ok = 1;
283                 }
284
285                 /* Skip over the number we just read */
286                 while (*buf && !isspace(*buf))
287                         buf++;
288
289         } while (*buf);
290
291 exit:
292         kfree(msg);
293         return (ok && i == 0) ? count : -EINVAL;
294 }
295
296 static char const *seqname[] = {
297         "ERROR", "S5", "S3", "S0", "S5S3", "S3S0",
298         "S0S3", "S3S5", "STOP", "RUN", "PULSE", "TEST", "KONAMI",
299 };
300
301 static ssize_t sequence_show(struct device *dev,
302                              struct device_attribute *attr, char *buf)
303 {
304         struct ec_params_lightbar *param;
305         struct ec_response_lightbar *resp;
306         struct cros_ec_command *msg;
307         int ret;
308         struct cros_ec_dev *ec = container_of(dev,
309                                               struct cros_ec_dev, class_dev);
310
311         msg = alloc_lightbar_cmd_msg(ec);
312         if (!msg)
313                 return -ENOMEM;
314
315         param = (struct ec_params_lightbar *)msg->data;
316         param->cmd = LIGHTBAR_CMD_GET_SEQ;
317         ret = lb_throttle();
318         if (ret)
319                 goto exit;
320
321         ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
322         if (ret < 0)
323                 goto exit;
324
325         if (msg->result != EC_RES_SUCCESS) {
326                 ret = scnprintf(buf, PAGE_SIZE,
327                                 "ERROR: EC returned %d\n", msg->result);
328                 goto exit;
329         }
330
331         resp = (struct ec_response_lightbar *)msg->data;
332         if (resp->get_seq.num >= ARRAY_SIZE(seqname))
333                 ret = scnprintf(buf, PAGE_SIZE, "%d\n", resp->get_seq.num);
334         else
335                 ret = scnprintf(buf, PAGE_SIZE, "%s\n",
336                                 seqname[resp->get_seq.num]);
337
338 exit:
339         kfree(msg);
340         return ret;
341 }
342
343 static ssize_t sequence_store(struct device *dev, struct device_attribute *attr,
344                               const char *buf, size_t count)
345 {
346         struct ec_params_lightbar *param;
347         struct cros_ec_command *msg;
348         unsigned int num;
349         int ret, len;
350         struct cros_ec_dev *ec = container_of(dev,
351                                               struct cros_ec_dev, class_dev);
352
353         for (len = 0; len < count; len++)
354                 if (!isalnum(buf[len]))
355                         break;
356
357         for (num = 0; num < ARRAY_SIZE(seqname); num++)
358                 if (!strncasecmp(seqname[num], buf, len))
359                         break;
360
361         if (num >= ARRAY_SIZE(seqname)) {
362                 ret = kstrtouint(buf, 0, &num);
363                 if (ret)
364                         return ret;
365         }
366
367         msg = alloc_lightbar_cmd_msg(ec);
368         if (!msg)
369                 return -ENOMEM;
370
371         param = (struct ec_params_lightbar *)msg->data;
372         param->cmd = LIGHTBAR_CMD_SEQ;
373         param->seq.num = num;
374         ret = lb_throttle();
375         if (ret)
376                 goto exit;
377
378         ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
379         if (ret < 0)
380                 goto exit;
381
382         if (msg->result != EC_RES_SUCCESS) {
383                 ret = -EINVAL;
384                 goto exit;
385         }
386
387         ret = count;
388 exit:
389         kfree(msg);
390         return ret;
391 }
392
393 /* Module initialization */
394
395 static DEVICE_ATTR_RW(interval_msec);
396 static DEVICE_ATTR_RO(version);
397 static DEVICE_ATTR_WO(brightness);
398 static DEVICE_ATTR_WO(led_rgb);
399 static DEVICE_ATTR_RW(sequence);
400 static struct attribute *__lb_cmds_attrs[] = {
401         &dev_attr_interval_msec.attr,
402         &dev_attr_version.attr,
403         &dev_attr_brightness.attr,
404         &dev_attr_led_rgb.attr,
405         &dev_attr_sequence.attr,
406         NULL,
407 };
408
409 static umode_t cros_ec_lightbar_attrs_are_visible(struct kobject *kobj,
410                                                   struct attribute *a, int n)
411 {
412         struct device *dev = container_of(kobj, struct device, kobj);
413         struct cros_ec_dev *ec = container_of(dev,
414                                               struct cros_ec_dev, class_dev);
415         struct platform_device *pdev = to_platform_device(ec->dev);
416         struct cros_ec_platform *pdata = pdev->dev.platform_data;
417         int is_cros_ec;
418
419         is_cros_ec = strcmp(pdata->ec_name, CROS_EC_DEV_NAME);
420
421         if (is_cros_ec != 0)
422                 return 0;
423
424         /* Only instantiate this stuff if the EC has a lightbar */
425         if (get_lightbar_version(ec, NULL, NULL))
426                 return a->mode;
427         else
428                 return 0;
429 }
430
431 struct attribute_group cros_ec_lightbar_attr_group = {
432         .name = "lightbar",
433         .attrs = __lb_cmds_attrs,
434         .is_visible = cros_ec_lightbar_attrs_are_visible,
435 };