static int get_next_event(struct cros_ec_device *ec_dev) { u8 buffer[sizeof(struct cros_ec_command) + sizeof(ec_dev->event_data)]; struct cros_ec_command *msg = (struct cros_ec_command *)&buffer; int ret; if (ec_dev->suspended) { dev_dbg(ec_dev->dev, "Device suspended.\n"); return -EHOSTDOWN; } msg->version = 0; msg->command = EC_CMD_GET_NEXT_EVENT; msg->insize = sizeof(ec_dev->event_data); msg->outsize = 0; ret = cros_ec_cmd_xfer(ec_dev, msg); if (ret > 0) { ec_dev->event_size = ret - 1; memcpy(&ec_dev->event_data, msg->data, sizeof(ec_dev->event_data)); } return ret; }
static int cros_ec_keyb_get_state(struct cros_ec_keyb *ckdev, uint8_t *kb_state) { int ret = 0; struct cros_ec_command *msg; msg = kmalloc(sizeof(*msg) + ckdev->cols, GFP_KERNEL); if (!msg) return -ENOMEM; msg->version = 0; msg->command = EC_CMD_MKBP_STATE; msg->insize = ckdev->cols; msg->outsize = 0; ret = cros_ec_cmd_xfer(ckdev->ec, msg); if (ret < 0) { dev_err(ckdev->dev, "Error transferring EC message %d\n", ret); goto exit; } memcpy(kb_state, msg->data, ckdev->cols); exit: kfree(msg); return ret; }
static ssize_t show_ec_flashinfo(struct device *dev, struct device_attribute *attr, char *buf) { struct ec_response_flash_info *resp; struct cros_ec_command msg = { 0 }; int ret; struct cros_ec_device *ec = dev_get_drvdata(dev); /* The flash info shouldn't ever change, but ask each time anyway. */ msg.command = EC_CMD_FLASH_INFO; msg.insize = sizeof(*resp); ret = cros_ec_cmd_xfer(ec, &msg); if (ret < 0) return ret; if (msg.result != EC_RES_SUCCESS) return scnprintf(buf, PAGE_SIZE, "ERROR: EC returned %d\n", msg.result); resp = (struct ec_response_flash_info *)msg.indata; return scnprintf(buf, PAGE_SIZE, "FlashSize %d\nWriteSize %d\n" "EraseSize %d\nProtectSize %d\n", resp->flash_size, resp->write_block_size, resp->erase_block_size, resp->protect_block_size); }
static ssize_t show_ec_flashinfo(struct device *dev, struct device_attribute *attr, char *buf) { struct ec_response_flash_info resp; struct cros_ec_command msg = { 0 }; int ret; struct cros_ec_dev *ec = container_of( dev, struct cros_ec_dev, class_dev); /* The flash info shouldn't ever change, but ask each time anyway. */ msg.command = EC_CMD_FLASH_INFO + ec->cmd_offset; msg.indata = (uint8_t *)&resp; msg.insize = sizeof(resp); ret = cros_ec_cmd_xfer(ec->ec_dev, &msg); if (ret < 0) return ret; if (msg.result != EC_RES_SUCCESS) return scnprintf(buf, PAGE_SIZE, "ERROR: EC returned %d\n", msg.result); return scnprintf(buf, PAGE_SIZE, "FlashSize %d\nWriteSize %d\n" "EraseSize %d\nProtectSize %d\n", resp.flash_size, resp.write_block_size, resp.erase_block_size, resp.protect_block_size); }
static int cros_ec_get_host_command_version_mask(struct cros_ec_device *ec_dev, u16 cmd, u32 *mask) { struct ec_params_get_cmd_versions *pver; struct ec_response_get_cmd_versions *rver; struct cros_ec_command *msg; int ret; msg = kmalloc(sizeof(*msg) + max(sizeof(*rver), sizeof(*pver)), GFP_KERNEL); if (!msg) return -ENOMEM; msg->version = 0; msg->command = EC_CMD_GET_CMD_VERSIONS; msg->insize = sizeof(*rver); msg->outsize = sizeof(*pver); pver = (struct ec_params_get_cmd_versions *)msg->data; pver->cmd = cmd; ret = cros_ec_cmd_xfer(ec_dev, msg); if (ret > 0) { rver = (struct ec_response_get_cmd_versions *)msg->data; *mask = rver->version_mask; } kfree(msg); return ret; }
static ssize_t sequence_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct ec_params_lightbar *param; struct cros_ec_command *msg; unsigned int num; int ret, len; struct cros_ec_dev *ec = container_of(dev, struct cros_ec_dev, class_dev); 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; } msg = alloc_lightbar_cmd_msg(ec); if (!msg) return -ENOMEM; param = (struct ec_params_lightbar *)msg->data; param->cmd = LIGHTBAR_CMD_SEQ; param->seq.num = num; ret = lb_throttle(); if (ret) goto exit; 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; }
int cros_ec_cmd_xfer_status(struct cros_ec_device *ec_dev, struct cros_ec_command *msg) { int ret; ret = cros_ec_cmd_xfer(ec_dev, msg); if (ret < 0) { dev_err(ec_dev->dev, "Command xfer error (err:%d)\n", ret); } else if (msg->result != EC_RES_SUCCESS) { dev_dbg(ec_dev->dev, "Command result (err: %d)\n", msg->result); return -EPROTO; } return ret; }
static int get_lightbar_version(struct cros_ec_dev *ec, uint32_t *ver_ptr, uint32_t *flg_ptr) { struct ec_params_lightbar *param; struct ec_response_lightbar *resp; struct cros_ec_command *msg; int ret; msg = alloc_lightbar_cmd_msg(ec); if (!msg) return 0; param = (struct ec_params_lightbar *)msg->data; param->cmd = LIGHTBAR_CMD_VERSION; ret = cros_ec_cmd_xfer(ec->ec_dev, msg); if (ret < 0) { ret = 0; goto exit; } 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; goto exit; 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; goto exit; } /* Anything else (ie, EC_RES_INVALID_COMMAND) - no lightbar */ ret = 0; exit: kfree(msg); return ret; }
static int cros_ec_sleep_event(struct cros_ec_device *ec_dev, u8 sleep_event) { struct { struct cros_ec_command msg; struct ec_params_host_sleep_event req; } __packed buf; memset(&buf, 0, sizeof(buf)); buf.req.sleep_event = sleep_event; buf.msg.command = EC_CMD_HOST_SLEEP_EVENT; buf.msg.version = 0; buf.msg.outsize = sizeof(buf.req); return cros_ec_cmd_xfer(ec_dev, &buf.msg); }
static int get_keyboard_state_event(struct cros_ec_device *ec_dev) { u8 buffer[sizeof(struct cros_ec_command) + sizeof(ec_dev->event_data.data)]; struct cros_ec_command *msg = (struct cros_ec_command *)&buffer; msg->version = 0; msg->command = EC_CMD_MKBP_STATE; msg->insize = sizeof(ec_dev->event_data.data); msg->outsize = 0; ec_dev->event_size = cros_ec_cmd_xfer(ec_dev, msg); ec_dev->event_data.event_type = EC_MKBP_EVENT_KEY_MATRIX; memcpy(&ec_dev->event_data.data, msg->data, sizeof(ec_dev->event_data.data)); return ec_dev->event_size; }
static ssize_t vboot_context_write(struct file *filp, struct kobject *kobj, struct bin_attribute *attr, char *buf, loff_t pos, size_t count) { struct device *dev = container_of(kobj, struct device, kobj); struct cros_ec_dev *ec = container_of(dev, struct cros_ec_dev, class_dev); struct cros_ec_device *ecdev = ec->ec_dev; struct ec_params_vbnvcontext *params; struct cros_ec_command *msg; int err; const size_t para_sz = sizeof(*params); const size_t data_sz = sizeof(params->block); /* Only write full values */ if (count != data_sz) return -EINVAL; msg = kmalloc(sizeof(*msg) + para_sz, GFP_KERNEL); if (!msg) return -ENOMEM; params = (struct ec_params_vbnvcontext *)msg->data; params->op = EC_VBNV_CONTEXT_OP_WRITE; memcpy(params->block, buf, data_sz); msg->version = EC_VER_VBNV_CONTEXT; msg->command = EC_CMD_VBNV_CONTEXT; msg->outsize = para_sz; msg->insize = 0; err = cros_ec_cmd_xfer(ecdev, msg); if (err < 0) { dev_err(dev, "Error sending write request: %d\n", err); kfree(msg); return err; } kfree(msg); return data_sz; }
static ssize_t sequence_show(struct device *dev, struct device_attribute *attr, char *buf) { struct ec_params_lightbar *param; struct ec_response_lightbar *resp; struct cros_ec_command *msg; int ret; struct cros_ec_dev *ec = container_of(dev, struct cros_ec_dev, class_dev); msg = alloc_lightbar_cmd_msg(ec); if (!msg) return -ENOMEM; param = (struct ec_params_lightbar *)msg->data; param->cmd = LIGHTBAR_CMD_GET_SEQ; ret = lb_throttle(); if (ret) goto exit; ret = cros_ec_cmd_xfer(ec->ec_dev, msg); if (ret < 0) goto exit; if (msg->result != EC_RES_SUCCESS) { ret = scnprintf(buf, PAGE_SIZE, "ERROR: EC returned %d\n", msg->result); goto exit; } resp = (struct ec_response_lightbar *)msg->data; if (resp->get_seq.num >= ARRAY_SIZE(seqname)) ret = scnprintf(buf, PAGE_SIZE, "%d\n", resp->get_seq.num); else ret = scnprintf(buf, PAGE_SIZE, "%s\n", seqname[resp->get_seq.num]); exit: kfree(msg); return ret; }
static ssize_t vboot_context_read(struct file *filp, struct kobject *kobj, struct bin_attribute *att, char *buf, loff_t pos, size_t count) { struct device *dev = container_of(kobj, struct device, kobj); struct cros_ec_dev *ec = container_of(dev, struct cros_ec_dev, class_dev); struct cros_ec_device *ecdev = ec->ec_dev; struct ec_params_vbnvcontext *params; struct cros_ec_command *msg; int err; const size_t para_sz = sizeof(params->op); const size_t resp_sz = sizeof(struct ec_response_vbnvcontext); const size_t payload = max(para_sz, resp_sz); msg = kmalloc(sizeof(*msg) + payload, GFP_KERNEL); if (!msg) return -ENOMEM; /* NB: we only kmalloc()ated enough space for the op field */ params = (struct ec_params_vbnvcontext *)msg->data; params->op = EC_VBNV_CONTEXT_OP_READ; msg->version = EC_VER_VBNV_CONTEXT; msg->command = EC_CMD_VBNV_CONTEXT; msg->outsize = para_sz; msg->insize = resp_sz; err = cros_ec_cmd_xfer(ecdev, msg); if (err < 0) { dev_err(dev, "Error sending read request: %d\n", err); kfree(msg); return err; } memcpy(buf, msg->data, resp_sz); kfree(msg); return resp_sz; }
static ssize_t brightness_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct ec_params_lightbar *param; struct cros_ec_command *msg; int ret; unsigned int val; struct cros_ec_dev *ec = container_of(dev, struct cros_ec_dev, class_dev); if (kstrtouint(buf, 0, &val)) return -EINVAL; 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) goto exit; 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; }
static ssize_t show_ec_flashinfo(struct device *dev, struct device_attribute *attr, char *buf) { struct ec_response_flash_info *resp; struct cros_ec_command *msg; int ret; struct cros_ec_dev *ec = container_of(dev, struct cros_ec_dev, class_dev); msg = kmalloc(sizeof(*msg) + sizeof(*resp), GFP_KERNEL); if (!msg) return -ENOMEM; /* The flash info shouldn't ever change, but ask each time anyway. */ msg->version = 0; msg->command = EC_CMD_FLASH_INFO + ec->cmd_offset; msg->insize = sizeof(*resp); msg->outsize = 0; ret = cros_ec_cmd_xfer(ec->ec_dev, msg); if (ret < 0) goto exit; if (msg->result != EC_RES_SUCCESS) { ret = scnprintf(buf, PAGE_SIZE, "ERROR: EC returned %d\n", msg->result); goto exit; } resp = (struct ec_response_flash_info *)msg->data; ret = scnprintf(buf, PAGE_SIZE, "FlashSize %d\nWriteSize %d\n" "EraseSize %d\nProtectSize %d\n", resp->flash_size, resp->write_block_size, resp->erase_block_size, resp->protect_block_size); exit: kfree(msg); return ret; }
static ssize_t store_ec_reboot(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { static const struct { const char * const str; uint8_t cmd; uint8_t flags; } words[] = { {"cancel", EC_REBOOT_CANCEL, 0}, {"ro", EC_REBOOT_JUMP_RO, 0}, {"rw", EC_REBOOT_JUMP_RW, 0}, {"cold", EC_REBOOT_COLD, 0}, {"disable-jump", EC_REBOOT_DISABLE_JUMP, 0}, {"hibernate", EC_REBOOT_HIBERNATE, 0}, {"at-shutdown", -1, EC_REBOOT_FLAG_ON_AP_SHUTDOWN}, }; struct ec_params_reboot_ec param; struct cros_ec_command msg = { 0 }; int got_cmd = 0, offset = 0; int i; int ret; struct cros_ec_dev *ec = container_of( dev, struct cros_ec_dev, class_dev); param.flags = 0; while (1) { /* Find word to start scanning */ while (buf[offset] & isspace(buf[offset])) offset++; if (!buf[offset]) break; for (i = 0; i < ARRAY_SIZE(words); i++) { if (!strncasecmp(words[i].str, buf+offset, strlen(words[i].str))) { if (words[i].flags) { param.flags |= words[i].flags; } else { param.cmd = words[i].cmd; got_cmd = 1; } break; } } /* On to the next word, if any */ while (buf[offset] && !isspace(buf[offset])) offset++; } if (!got_cmd) return -EINVAL; msg.command = EC_CMD_REBOOT_EC + ec->cmd_offset; msg.outdata = (uint8_t *)¶m; msg.outsize = sizeof(param); ret = cros_ec_cmd_xfer(ec->ec_dev, &msg); if (ret < 0) return ret; if (msg.result != EC_RES_SUCCESS) { dev_dbg(&ec->class_dev, "EC result %d\n", msg.result); return -EINVAL; } return count; }
static ssize_t show_ec_version(struct device *dev, struct device_attribute *attr, char *buf) { static const char * const image_names[] = {"unknown", "RO", "RW"}; struct ec_response_get_version *r_ver; struct ec_response_get_chip_info *r_chip; struct ec_response_board_version *r_board; struct cros_ec_command msg = { 0 }; int ret; int count = 0; struct cros_ec_device *ec = dev_get_drvdata(dev); /* Get versions. RW may change. */ msg.command = EC_CMD_GET_VERSION; msg.insize = sizeof(*r_ver); ret = cros_ec_cmd_xfer(ec, &msg); if (ret < 0) return ret; if (msg.result != EC_RES_SUCCESS) return scnprintf(buf, PAGE_SIZE, "ERROR: EC returned %d\n", msg.result); r_ver = (struct ec_response_get_version *)msg.indata; /* Strings should be null-terminated, but let's be sure. */ r_ver->version_string_ro[sizeof(r_ver->version_string_ro) - 1] = '\0'; r_ver->version_string_rw[sizeof(r_ver->version_string_rw) - 1] = '\0'; count += scnprintf(buf + count, PAGE_SIZE - count, "RO version: %s\n", r_ver->version_string_ro); count += scnprintf(buf + count, PAGE_SIZE - count, "RW version: %s\n", r_ver->version_string_rw); count += scnprintf(buf + count, PAGE_SIZE - count, "Firmware copy: %s\n", (r_ver->current_image < ARRAY_SIZE(image_names) ? image_names[r_ver->current_image] : "?")); /* Get build info. */ msg.command = EC_CMD_GET_BUILD_INFO; msg.insize = sizeof(msg.indata); ret = cros_ec_cmd_xfer(ec, &msg); if (ret < 0) count += scnprintf(buf + count, PAGE_SIZE - count, "Build info: XFER ERROR %d\n", ret); else if (msg.result != EC_RES_SUCCESS) count += scnprintf(buf + count, PAGE_SIZE - count, "Build info: EC error %d\n", msg.result); else { msg.indata[sizeof(msg.indata) - 1] = '\0'; count += scnprintf(buf + count, PAGE_SIZE - count, "Build info: %s\n", msg.indata); } /* Get chip info. */ msg.command = EC_CMD_GET_CHIP_INFO; msg.insize = sizeof(*r_chip); ret = cros_ec_cmd_xfer(ec, &msg); if (ret < 0) count += scnprintf(buf + count, PAGE_SIZE - count, "Chip info: XFER ERROR %d\n", ret); else if (msg.result != EC_RES_SUCCESS) count += scnprintf(buf + count, PAGE_SIZE - count, "Chip info: EC error %d\n", msg.result); else { r_chip = (struct ec_response_get_chip_info *)msg.indata; r_chip->vendor[sizeof(r_chip->vendor) - 1] = '\0'; r_chip->name[sizeof(r_chip->name) - 1] = '\0'; r_chip->revision[sizeof(r_chip->revision) - 1] = '\0'; count += scnprintf(buf + count, PAGE_SIZE - count, "Chip vendor: %s\n", r_chip->vendor); count += scnprintf(buf + count, PAGE_SIZE - count, "Chip name: %s\n", r_chip->name); count += scnprintf(buf + count, PAGE_SIZE - count, "Chip revision: %s\n", r_chip->revision); } /* Get board version */ msg.command = EC_CMD_GET_BOARD_VERSION; msg.insize = sizeof(*r_board); ret = cros_ec_cmd_xfer(ec, &msg); if (ret < 0) count += scnprintf(buf + count, PAGE_SIZE - count, "Board version: XFER ERROR %d\n", ret); else if (msg.result != EC_RES_SUCCESS) count += scnprintf(buf + count, PAGE_SIZE - count, "Board version: EC error %d\n", msg.result); else { r_board = (struct ec_response_board_version *)msg.indata; count += scnprintf(buf + count, PAGE_SIZE - count, "Board version: %d\n", r_board->board_version); } return count; }
/* * 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; struct cros_ec_command *msg; struct cros_ec_dev *ec = container_of(dev, struct cros_ec_dev, class_dev); unsigned int 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) goto exit; 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) goto exit; } ret = cros_ec_cmd_xfer(ec->ec_dev, msg); if (ret < 0) goto exit; if (msg->result != EC_RES_SUCCESS) goto exit; i = 0; ok = 1; } /* Skip over the number we just read */ while (*buf && !isspace(*buf)) buf++; } while (*buf); exit: kfree(msg); return (ok && i == 0) ? count : -EINVAL; }
static int cros_ec_keyb_get_state(struct cros_ec_keyb *ckdev, uint8_t *kb_state) { int ret; struct cros_ec_command msg = { .version = 0, .command = EC_CMD_MKBP_STATE, .outdata = NULL, .outsize = 0, .indata = kb_state, .insize = ckdev->cols, }; ret = cros_ec_cmd_xfer(ckdev->ec, &msg); /* FIXME: This assumes msg.result == EC_RES_SUCCESS */ return ret; } static irqreturn_t cros_ec_keyb_irq(int irq, void *data) { struct cros_ec_keyb *ckdev = data; struct cros_ec_device *ec = ckdev->ec; int ret; uint8_t kb_state[ckdev->cols]; if (device_may_wakeup(ec->dev)) pm_wakeup_event(ec->dev, 0); ret = cros_ec_keyb_get_state(ckdev, kb_state); if (ret >= 0) cros_ec_keyb_process(ckdev, kb_state, ret); else dev_err(ec->dev, "failed to get keyboard state: %d\n", ret); return IRQ_HANDLED; } static int cros_ec_keyb_open(struct input_dev *dev) { struct cros_ec_keyb *ckdev = input_get_drvdata(dev); struct cros_ec_device *ec = ckdev->ec; return request_threaded_irq(ec->irq, NULL, cros_ec_keyb_irq, IRQF_TRIGGER_LOW | IRQF_ONESHOT, "cros_ec_keyb", ckdev); } static void cros_ec_keyb_close(struct input_dev *dev) { struct cros_ec_keyb *ckdev = input_get_drvdata(dev); struct cros_ec_device *ec = ckdev->ec; free_irq(ec->irq, ckdev); } /* Clear any keys in the buffer */ static void cros_ec_keyb_clear_keyboard(struct cros_ec_keyb *ckdev) { uint8_t old_state[ckdev->cols]; uint8_t new_state[ckdev->cols]; unsigned long duration; int i, ret; /* * Keep reading until we see that the scan state does not change. * That indicates that we are done. * * Assume that the EC keyscan buffer is at most 32 deep. */ duration = jiffies; ret = cros_ec_keyb_get_state(ckdev, new_state); for (i = 1; !ret && i < 32; i++) { memcpy(old_state, new_state, sizeof(old_state)); ret = cros_ec_keyb_get_state(ckdev, new_state); if (0 == memcmp(old_state, new_state, sizeof(old_state))) break; } duration = jiffies - duration; dev_info(ckdev->dev, "Discarded %d keyscan(s) in %dus\n", i, jiffies_to_usecs(duration)); }
static ssize_t store_ec_reboot(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { static const struct { const char * const str; uint8_t cmd; uint8_t flags; } words[] = { {"cancel", EC_REBOOT_CANCEL, 0}, {"ro", EC_REBOOT_JUMP_RO, 0}, {"rw", EC_REBOOT_JUMP_RW, 0}, {"cold", EC_REBOOT_COLD, 0}, {"disable-jump", EC_REBOOT_DISABLE_JUMP, 0}, {"hibernate", EC_REBOOT_HIBERNATE, 0}, {"at-shutdown", -1, EC_REBOOT_FLAG_ON_AP_SHUTDOWN}, }; struct cros_ec_command *msg; struct ec_params_reboot_ec *param; int got_cmd = 0, offset = 0; int i; int ret; struct cros_ec_dev *ec = container_of(dev, struct cros_ec_dev, class_dev); msg = kmalloc(sizeof(*msg) + sizeof(*param), GFP_KERNEL); if (!msg) return -ENOMEM; param = (struct ec_params_reboot_ec *)msg->data; param->flags = 0; while (1) { /* Find word to start scanning */ while (buf[offset] && isspace(buf[offset])) offset++; if (!buf[offset]) break; for (i = 0; i < ARRAY_SIZE(words); i++) { if (!strncasecmp(words[i].str, buf+offset, strlen(words[i].str))) { if (words[i].flags) { param->flags |= words[i].flags; } else { param->cmd = words[i].cmd; got_cmd = 1; } break; } } /* On to the next word, if any */ while (buf[offset] && !isspace(buf[offset])) offset++; } if (!got_cmd) { count = -EINVAL; goto exit; } msg->version = 0; msg->command = EC_CMD_REBOOT_EC + ec->cmd_offset; msg->outsize = sizeof(*param); msg->insize = 0; ret = cros_ec_cmd_xfer(ec->ec_dev, msg); if (ret < 0) { count = ret; goto exit; } if (msg->result != EC_RES_SUCCESS) { dev_dbg(ec->dev, "EC result %d\n", msg->result); count = -EINVAL; } exit: kfree(msg); return count; }
static ssize_t show_ec_version(struct device *dev, struct device_attribute *attr, char *buf) { static const char * const image_names[] = {"unknown", "RO", "RW"}; struct ec_response_get_version *r_ver; struct ec_response_get_chip_info *r_chip; struct ec_response_board_version *r_board; struct cros_ec_command *msg; int ret; int count = 0; struct cros_ec_dev *ec = container_of(dev, struct cros_ec_dev, class_dev); msg = kmalloc(sizeof(*msg) + EC_HOST_PARAM_SIZE, GFP_KERNEL); if (!msg) return -ENOMEM; /* Get versions. RW may change. */ msg->version = 0; msg->command = EC_CMD_GET_VERSION + ec->cmd_offset; msg->insize = sizeof(*r_ver); msg->outsize = 0; ret = cros_ec_cmd_xfer(ec->ec_dev, msg); if (ret < 0) { count = ret; goto exit; } if (msg->result != EC_RES_SUCCESS) { count = scnprintf(buf, PAGE_SIZE, "ERROR: EC returned %d\n", msg->result); goto exit; } r_ver = (struct ec_response_get_version *)msg->data; /* Strings should be null-terminated, but let's be sure. */ r_ver->version_string_ro[sizeof(r_ver->version_string_ro) - 1] = '\0'; r_ver->version_string_rw[sizeof(r_ver->version_string_rw) - 1] = '\0'; count += scnprintf(buf + count, PAGE_SIZE - count, "RO version: %s\n", r_ver->version_string_ro); count += scnprintf(buf + count, PAGE_SIZE - count, "RW version: %s\n", r_ver->version_string_rw); count += scnprintf(buf + count, PAGE_SIZE - count, "Firmware copy: %s\n", (r_ver->current_image < ARRAY_SIZE(image_names) ? image_names[r_ver->current_image] : "?")); /* Get build info. */ msg->command = EC_CMD_GET_BUILD_INFO + ec->cmd_offset; msg->insize = EC_HOST_PARAM_SIZE; ret = cros_ec_cmd_xfer(ec->ec_dev, msg); if (ret < 0) count += scnprintf(buf + count, PAGE_SIZE - count, "Build info: XFER ERROR %d\n", ret); else if (msg->result != EC_RES_SUCCESS) count += scnprintf(buf + count, PAGE_SIZE - count, "Build info: EC error %d\n", msg->result); else { msg->data[EC_HOST_PARAM_SIZE - 1] = '\0'; count += scnprintf(buf + count, PAGE_SIZE - count, "Build info: %s\n", msg->data); } /* Get chip info. */ msg->command = EC_CMD_GET_CHIP_INFO + ec->cmd_offset; msg->insize = sizeof(*r_chip); ret = cros_ec_cmd_xfer(ec->ec_dev, msg); if (ret < 0) count += scnprintf(buf + count, PAGE_SIZE - count, "Chip info: XFER ERROR %d\n", ret); else if (msg->result != EC_RES_SUCCESS) count += scnprintf(buf + count, PAGE_SIZE - count, "Chip info: EC error %d\n", msg->result); else { r_chip = (struct ec_response_get_chip_info *)msg->data; r_chip->vendor[sizeof(r_chip->vendor) - 1] = '\0'; r_chip->name[sizeof(r_chip->name) - 1] = '\0'; r_chip->revision[sizeof(r_chip->revision) - 1] = '\0'; count += scnprintf(buf + count, PAGE_SIZE - count, "Chip vendor: %s\n", r_chip->vendor); count += scnprintf(buf + count, PAGE_SIZE - count, "Chip name: %s\n", r_chip->name); count += scnprintf(buf + count, PAGE_SIZE - count, "Chip revision: %s\n", r_chip->revision); } /* Get board version */ msg->command = EC_CMD_GET_BOARD_VERSION + ec->cmd_offset; msg->insize = sizeof(*r_board); ret = cros_ec_cmd_xfer(ec->ec_dev, msg); if (ret < 0) count += scnprintf(buf + count, PAGE_SIZE - count, "Board version: XFER ERROR %d\n", ret); else if (msg->result != EC_RES_SUCCESS) count += scnprintf(buf + count, PAGE_SIZE - count, "Board version: EC error %d\n", msg->result); else { r_board = (struct ec_response_board_version *)msg->data; count += scnprintf(buf + count, PAGE_SIZE - count, "Board version: %d\n", r_board->board_version); } exit: kfree(msg); return count; }
static int cros_ec_keyb_get_state(struct cros_ec_keyb *ckdev, uint8_t *kb_state) { struct cros_ec_command msg = { .version = 0, .command = EC_CMD_MKBP_STATE, .outdata = NULL, .outsize = 0, .indata = kb_state, .insize = ckdev->cols, }; return cros_ec_cmd_xfer(ckdev->ec, &msg); } static irqreturn_t cros_ec_keyb_irq(int irq, void *data) { struct cros_ec_keyb *ckdev = data; struct cros_ec_device *ec = ckdev->ec; int ret; uint8_t kb_state[ckdev->cols]; if (device_may_wakeup(ec->dev)) pm_wakeup_event(ec->dev, 0); ret = cros_ec_keyb_get_state(ckdev, kb_state); if (ret >= 0) cros_ec_keyb_process(ckdev, kb_state, ret); else dev_err(ec->dev, "failed to get keyboard state: %d\n", ret); return IRQ_HANDLED; } static int cros_ec_keyb_open(struct input_dev *dev) { struct cros_ec_keyb *ckdev = input_get_drvdata(dev); struct cros_ec_device *ec = ckdev->ec; return request_threaded_irq(ec->irq, NULL, cros_ec_keyb_irq, IRQF_TRIGGER_LOW | IRQF_ONESHOT, "cros_ec_keyb", ckdev); } static void cros_ec_keyb_close(struct input_dev *dev) { struct cros_ec_keyb *ckdev = input_get_drvdata(dev); struct cros_ec_device *ec = ckdev->ec; free_irq(ec->irq, ckdev); } /* * Walks keycodes flipping bit in buffer COLUMNS deep where bit is ROW. Used by * ghosting logic to ignore NULL or virtual keys. */ static void cros_ec_keyb_compute_valid_keys(struct cros_ec_keyb *ckdev) { int row, col; int row_shift = ckdev->row_shift; unsigned short *keymap = ckdev->idev->keycode; unsigned short code; BUG_ON(ckdev->idev->keycodesize != sizeof(*keymap)); for (col = 0; col < ckdev->cols; col++) { for (row = 0; row < ckdev->rows; row++) { code = keymap[MATRIX_SCAN_CODE(row, col, row_shift)]; if (code && (code != KEY_BATTERY)) ckdev->valid_keys[col] |= 1 << row; } dev_dbg(ckdev->dev, "valid_keys[%02d] = 0x%02x\n", col, ckdev->valid_keys[col]); } }
static ssize_t show_ec_version(struct device *dev, struct device_attribute *attr, char *buf) { static const char * const image_names[] = {"unknown", "RO", "RW"}; struct ec_response_get_version r_ver; struct ec_response_get_chip_info r_chip; struct ec_response_board_version r_board; struct cros_ec_command msg = { 0 }; char r_build_string[EC_HOST_PARAM_SIZE]; int ret; int count = 0; struct cros_ec_dev *ec = container_of( dev, struct cros_ec_dev, class_dev); /* Get versions. RW may change. */ msg.command = EC_CMD_GET_VERSION + ec->cmd_offset; msg.indata = (uint8_t *)&r_ver; msg.insize = sizeof(r_ver); ret = cros_ec_cmd_xfer(ec->ec_dev, &msg); if (ret < 0) return ret; if (msg.result != EC_RES_SUCCESS) return scnprintf(buf, PAGE_SIZE, "ERROR: EC returned %d\n", msg.result); /* Strings should be null-terminated, but let's be sure. */ r_ver.version_string_ro[sizeof(r_ver.version_string_ro) - 1] = '\0'; r_ver.version_string_rw[sizeof(r_ver.version_string_rw) - 1] = '\0'; count += scnprintf(buf + count, PAGE_SIZE - count, "RO version: %s\n", r_ver.version_string_ro); count += scnprintf(buf + count, PAGE_SIZE - count, "RW version: %s\n", r_ver.version_string_rw); count += scnprintf(buf + count, PAGE_SIZE - count, "Firmware copy: %s\n", (r_ver.current_image < ARRAY_SIZE(image_names) ? image_names[r_ver.current_image] : "?")); /* Get build info. */ msg.command = EC_CMD_GET_BUILD_INFO + ec->cmd_offset; msg.indata = (uint8_t *)r_build_string; msg.insize = sizeof(r_build_string); ret = cros_ec_cmd_xfer(ec->ec_dev, &msg); if (ret < 0) count += scnprintf(buf + count, PAGE_SIZE - count, "Build info: XFER ERROR %d\n", ret); else if (msg.result != EC_RES_SUCCESS) count += scnprintf(buf + count, PAGE_SIZE - count, "Build info: EC error %d\n", msg.result); else { r_build_string[sizeof(r_build_string) - 1] = '\0'; count += scnprintf(buf + count, PAGE_SIZE - count, "Build info: %s\n", r_build_string); } /* Get chip info. */ msg.command = EC_CMD_GET_CHIP_INFO + ec->cmd_offset; msg.indata = (uint8_t *)&r_chip; msg.insize = sizeof(r_chip); ret = cros_ec_cmd_xfer(ec->ec_dev, &msg); if (ret < 0) count += scnprintf(buf + count, PAGE_SIZE - count, "Chip info: XFER ERROR %d\n", ret); else if (msg.result != EC_RES_SUCCESS) count += scnprintf(buf + count, PAGE_SIZE - count, "Chip info: EC error %d\n", msg.result); else { r_chip.vendor[sizeof(r_chip.vendor) - 1] = '\0'; r_chip.name[sizeof(r_chip.name) - 1] = '\0'; r_chip.revision[sizeof(r_chip.revision) - 1] = '\0'; count += scnprintf(buf + count, PAGE_SIZE - count, "Chip vendor: %s\n", r_chip.vendor); count += scnprintf(buf + count, PAGE_SIZE - count, "Chip name: %s\n", r_chip.name); count += scnprintf(buf + count, PAGE_SIZE - count, "Chip revision: %s\n", r_chip.revision); } /* Get board version */ msg.command = EC_CMD_GET_BOARD_VERSION + ec->cmd_offset; msg.indata = (uint8_t *)&r_board; msg.insize = sizeof(r_board); ret = cros_ec_cmd_xfer(ec->ec_dev, &msg); if (ret < 0) count += scnprintf(buf + count, PAGE_SIZE - count, "Board version: XFER ERROR %d\n", ret); else if (msg.result != EC_RES_SUCCESS) count += scnprintf(buf + count, PAGE_SIZE - count, "Board version: EC error %d\n", msg.result); else { count += scnprintf(buf + count, PAGE_SIZE - count, "Board version: %d\n", r_board.board_version); } return count; }