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 35 #include "cros_ec_dev.h" 36 37 /* Rate-limit the lightbar interface to prevent DoS. */ 38 static unsigned long lb_interval_jiffies = 50 * HZ / 1000; 39 40 static ssize_t interval_msec_show(struct device *dev, 41 struct device_attribute *attr, char *buf) 42 { 43 unsigned long msec = lb_interval_jiffies * 1000 / HZ; 44 45 return scnprintf(buf, PAGE_SIZE, "%lu\n", msec); 46 } 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 54 if (kstrtoul(buf, 0, &msec)) 55 return -EINVAL; 56 57 lb_interval_jiffies = msec * HZ / 1000; 58 59 return count; 60 } 61 62 static DEFINE_MUTEX(lb_mutex); 63 /* Return 0 if able to throttle correctly, error otherwise */ 64 static int lb_throttle(void) 65 { 66 static unsigned long last_access; 67 unsigned long now, next_timeslot; 68 long delay; 69 int ret = 0; 70 71 mutex_lock(&lb_mutex); 72 73 now = jiffies; 74 next_timeslot = last_access + lb_interval_jiffies; 75 76 if (time_before(now, next_timeslot)) { 77 delay = (long)(next_timeslot) - (long)now; 78 set_current_state(TASK_INTERRUPTIBLE); 79 if (schedule_timeout(delay) > 0) { 80 /* interrupted - just abort */ 81 ret = -EINTR; 82 goto out; 83 } 84 now = jiffies; 85 } 86 87 last_access = now; 88 out: 89 mutex_unlock(&lb_mutex); 90 91 return ret; 92 } 93 94 #define INIT_MSG(P, R) { \ 95 .command = EC_CMD_LIGHTBAR_CMD, \ 96 .outsize = sizeof(*P), \ 97 .insize = sizeof(*R), \ 98 } 99 100 static int get_lightbar_version(struct cros_ec_device *ec, 101 uint32_t *ver_ptr, uint32_t *flg_ptr) 102 { 103 struct ec_params_lightbar *param; 104 struct ec_response_lightbar *resp; 105 struct cros_ec_command msg = INIT_MSG(param, resp); 106 int ret; 107 108 param = (struct ec_params_lightbar *)msg.outdata; 109 param->cmd = LIGHTBAR_CMD_VERSION; 110 ret = cros_ec_cmd_xfer(ec, &msg); 111 if (ret < 0) 112 return 0; 113 114 switch (msg.result) { 115 case EC_RES_INVALID_PARAM: 116 /* Pixel had no version command. */ 117 if (ver_ptr) 118 *ver_ptr = 0; 119 if (flg_ptr) 120 *flg_ptr = 0; 121 return 1; 122 123 case EC_RES_SUCCESS: 124 resp = (struct ec_response_lightbar *)msg.indata; 125 126 /* Future devices w/lightbars should implement this command */ 127 if (ver_ptr) 128 *ver_ptr = resp->version.num; 129 if (flg_ptr) 130 *flg_ptr = resp->version.flags; 131 return 1; 132 } 133 134 /* Anything else (ie, EC_RES_INVALID_COMMAND) - no lightbar */ 135 return 0; 136 } 137 138 static ssize_t version_show(struct device *dev, 139 struct device_attribute *attr, char *buf) 140 { 141 uint32_t version, flags; 142 struct cros_ec_device *ec = dev_get_drvdata(dev); 143 int ret; 144 145 ret = lb_throttle(); 146 if (ret) 147 return ret; 148 149 /* This should always succeed, because we check during init. */ 150 if (!get_lightbar_version(ec, &version, &flags)) 151 return -EIO; 152 153 return scnprintf(buf, PAGE_SIZE, "%d %d\n", version, flags); 154 } 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 ec_response_lightbar *resp; 162 struct cros_ec_command msg = INIT_MSG(param, resp); 163 int ret; 164 unsigned int val; 165 struct cros_ec_device *ec = dev_get_drvdata(dev); 166 167 if (kstrtouint(buf, 0, &val)) 168 return -EINVAL; 169 170 param = (struct ec_params_lightbar *)msg.outdata; 171 param->cmd = LIGHTBAR_CMD_BRIGHTNESS; 172 param->brightness.num = val; 173 ret = lb_throttle(); 174 if (ret) 175 return ret; 176 177 ret = cros_ec_cmd_xfer(ec, &msg); 178 if (ret < 0) 179 return ret; 180 181 if (msg.result != EC_RES_SUCCESS) 182 return -EINVAL; 183 184 return count; 185 } 186 187 188 /* 189 * We expect numbers, and we'll keep reading until we find them, skipping over 190 * any whitespace (sysfs guarantees that the input is null-terminated). Every 191 * four numbers are sent to the lightbar as <LED,R,G,B>. We fail at the first 192 * parsing error, if we don't parse any numbers, or if we have numbers left 193 * over. 194 */ 195 static ssize_t led_rgb_store(struct device *dev, struct device_attribute *attr, 196 const char *buf, size_t count) 197 { 198 struct ec_params_lightbar *param; 199 struct ec_response_lightbar *resp; 200 struct cros_ec_command msg = INIT_MSG(param, resp); 201 struct cros_ec_device *ec = dev_get_drvdata(dev); 202 unsigned int val[4]; 203 int ret, i = 0, j = 0, ok = 0; 204 205 do { 206 /* Skip any whitespace */ 207 while (*buf && isspace(*buf)) 208 buf++; 209 210 if (!*buf) 211 break; 212 213 ret = sscanf(buf, "%i", &val[i++]); 214 if (ret == 0) 215 return -EINVAL; 216 217 if (i == 4) { 218 param = (struct ec_params_lightbar *)msg.outdata; 219 param->cmd = LIGHTBAR_CMD_RGB; 220 param->rgb.led = val[0]; 221 param->rgb.red = val[1]; 222 param->rgb.green = val[2]; 223 param->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 return ret; 232 } 233 234 ret = cros_ec_cmd_xfer(ec, &msg); 235 if (ret < 0) 236 return ret; 237 238 if (msg.result != EC_RES_SUCCESS) 239 return -EINVAL; 240 241 i = 0; 242 ok = 1; 243 } 244 245 /* Skip over the number we just read */ 246 while (*buf && !isspace(*buf)) 247 buf++; 248 249 } while (*buf); 250 251 return (ok && i == 0) ? count : -EINVAL; 252 } 253 254 static char const *seqname[] = { 255 "ERROR", "S5", "S3", "S0", "S5S3", "S3S0", 256 "S0S3", "S3S5", "STOP", "RUN", "PULSE", "TEST", "KONAMI", 257 }; 258 259 static ssize_t sequence_show(struct device *dev, 260 struct device_attribute *attr, char *buf) 261 { 262 struct ec_params_lightbar *param; 263 struct ec_response_lightbar *resp; 264 struct cros_ec_command msg = INIT_MSG(param, resp); 265 int ret; 266 struct cros_ec_device *ec = dev_get_drvdata(dev); 267 268 param = (struct ec_params_lightbar *)msg.outdata; 269 param->cmd = LIGHTBAR_CMD_GET_SEQ; 270 ret = lb_throttle(); 271 if (ret) 272 return ret; 273 274 ret = cros_ec_cmd_xfer(ec, &msg); 275 if (ret < 0) 276 return ret; 277 278 if (msg.result != EC_RES_SUCCESS) 279 return scnprintf(buf, PAGE_SIZE, 280 "ERROR: EC returned %d\n", msg.result); 281 282 resp = (struct ec_response_lightbar *)msg.indata; 283 if (resp->get_seq.num >= ARRAY_SIZE(seqname)) 284 return scnprintf(buf, PAGE_SIZE, "%d\n", resp->get_seq.num); 285 else 286 return scnprintf(buf, PAGE_SIZE, "%s\n", 287 seqname[resp->get_seq.num]); 288 } 289 290 static ssize_t sequence_store(struct device *dev, struct device_attribute *attr, 291 const char *buf, size_t count) 292 { 293 struct ec_params_lightbar *param; 294 struct ec_response_lightbar *resp; 295 struct cros_ec_command msg = INIT_MSG(param, resp); 296 unsigned int num; 297 int ret, len; 298 struct cros_ec_device *ec = dev_get_drvdata(dev); 299 300 for (len = 0; len < count; len++) 301 if (!isalnum(buf[len])) 302 break; 303 304 for (num = 0; num < ARRAY_SIZE(seqname); num++) 305 if (!strncasecmp(seqname[num], buf, len)) 306 break; 307 308 if (num >= ARRAY_SIZE(seqname)) { 309 ret = kstrtouint(buf, 0, &num); 310 if (ret) 311 return ret; 312 } 313 314 param = (struct ec_params_lightbar *)msg.outdata; 315 param->cmd = LIGHTBAR_CMD_SEQ; 316 param->seq.num = num; 317 ret = lb_throttle(); 318 if (ret) 319 return ret; 320 321 ret = cros_ec_cmd_xfer(ec, &msg); 322 if (ret < 0) 323 return ret; 324 325 if (msg.result != EC_RES_SUCCESS) 326 return -EINVAL; 327 328 return count; 329 } 330 331 /* Module initialization */ 332 333 static DEVICE_ATTR_RW(interval_msec); 334 static DEVICE_ATTR_RO(version); 335 static DEVICE_ATTR_WO(brightness); 336 static DEVICE_ATTR_WO(led_rgb); 337 static DEVICE_ATTR_RW(sequence); 338 static struct attribute *__lb_cmds_attrs[] = { 339 &dev_attr_interval_msec.attr, 340 &dev_attr_version.attr, 341 &dev_attr_brightness.attr, 342 &dev_attr_led_rgb.attr, 343 &dev_attr_sequence.attr, 344 NULL, 345 }; 346 static struct attribute_group lb_cmds_attr_group = { 347 .name = "lightbar", 348 .attrs = __lb_cmds_attrs, 349 }; 350 351 void ec_dev_lightbar_init(struct cros_ec_device *ec) 352 { 353 int ret = 0; 354 355 /* Only instantiate this stuff if the EC has a lightbar */ 356 if (!get_lightbar_version(ec, NULL, NULL)) 357 return; 358 359 ret = sysfs_create_group(&ec->vdev->kobj, &lb_cmds_attr_group); 360 if (ret) 361 pr_warn("sysfs_create_group() failed: %d\n", ret); 362 } 363 364 void ec_dev_lightbar_remove(struct cros_ec_device *ec) 365 { 366 sysfs_remove_group(&ec->vdev->kobj, &lb_cmds_attr_group); 367 } 368