Beispiel #1
0
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;
}
Beispiel #2
0
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;
}
Beispiel #3
0
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);
}
Beispiel #4
0
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);
}
Beispiel #5
0
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;
}
Beispiel #6
0
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;
}
Beispiel #7
0
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;
}
Beispiel #8
0
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;
}
Beispiel #9
0
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);
}
Beispiel #10
0
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;
}
Beispiel #11
0
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;
}
Beispiel #12
0
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;
}
Beispiel #13
0
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;
}
Beispiel #14
0
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;
}
Beispiel #15
0
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;
}
Beispiel #16
0
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 *)&param;
	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;
}
Beispiel #17
0
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;
}
Beispiel #18
0
/*
 * 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));
}
Beispiel #20
0
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;
}
Beispiel #21
0
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;
}
Beispiel #22
0
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]);
	}
}
Beispiel #23
0
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;
}