/* Rate-limit the lightbar interface to prevent DoS. */ staticunsignedlong lb_interval_jiffies = 50 * HZ / 1000;
/* * Whether or not we have given userspace control of the lightbar. * If this is true, we won't do anything during suspend/resume.
*/ staticbool userspace_control;
static DEFINE_MUTEX(lb_mutex); /* Return 0 if able to throttle correctly, error otherwise */ staticint lb_throttle(void)
{ staticunsignedlong last_access; unsignedlong 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;
}
msg = alloc_lightbar_cmd_msg(ec); if (!msg) return 0;
param = (struct ec_params_lightbar *)msg->data;
param->cmd = LIGHTBAR_CMD_VERSION;
msg->outsize = sizeof(param->cmd);
msg->result = sizeof(resp->version);
ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg); if (ret < 0 && ret != -EINVAL) {
ret = 0; gotoexit;
}
switch (msg->result) { case EC_RES_INVALID_PARAM: /* Pixel had no version command. */ if (ver_ptr)
*ver_ptr = 0; if (flg_ptr)
*flg_ptr = 0;
ret = 1; gotoexit;
case EC_RES_SUCCESS:
resp = (struct ec_response_lightbar *)msg->data;
/* Future devices w/lightbars should implement this command */ if (ver_ptr)
*ver_ptr = resp->version.num; if (flg_ptr)
*flg_ptr = resp->version.flags;
ret = 1; gotoexit;
}
/* Anything else (ie, EC_RES_INVALID_COMMAND) - no lightbar */
ret = 0; exit:
kfree(msg); return ret;
}
msg = alloc_lightbar_cmd_msg(ec); if (!msg) return -ENOMEM;
param = (struct ec_params_lightbar *)msg->data;
param->cmd = LIGHTBAR_CMD_SET_BRIGHTNESS;
param->set_brightness.num = val;
ret = lb_throttle(); if (ret) gotoexit;
ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg); if (ret < 0) gotoexit;
ret = count; exit:
kfree(msg); return ret;
}
/* * 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, constchar *buf, size_t count)
{ struct ec_params_lightbar *param; struct cros_ec_command *msg; struct cros_ec_dev *ec = to_cros_ec_dev(dev); unsignedint val[4]; int ret, i = 0, j = 0, ok = 0;
msg = alloc_lightbar_cmd_msg(ec); if (!msg) return -ENOMEM;
do { /* Skip any whitespace */ while (*buf && isspace(*buf))
buf++;
if (!*buf) break;
ret = sscanf(buf, "%i", &val[i++]); if (ret == 0) gotoexit;
if (i == 4) {
param = (struct ec_params_lightbar *)msg->data;
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]; /* * 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) gotoexit;
}
ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg); if (ret < 0) gotoexit;
i = 0;
ok = 1;
}
/* Skip over the number we just read */ while (*buf && !isspace(*buf))
buf++;
/* * 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)",
(unsignedint)count, max_size);
return -EINVAL;
}
msg = alloc_lightbar_cmd_msg(ec); if (!msg) return -ENOMEM;
ret = lb_throttle(); if (ret) gotoexit;
dev_info(dev, "Copying %zu byte program to EC", 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_status(ec->ec_dev, msg); if (ret < 0) gotoexit;
/* * 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;
/* 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\n",
cros_ec_lightbar_attr_group.name, ret);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.