Beispiel #1
0
/**
 * mei_release - the release function
 *
 * @inode: pointer to inode structure
 * @file: pointer to file structure
 *
 * returns 0 on success, <0 on error
 */
static int mei_release(struct inode *inode, struct file *file)
{
	struct mei_cl *cl = file->private_data;
	struct mei_cl_cb *cb;
	struct mei_device *dev;
	int rets = 0;

	if (WARN_ON(!cl || !cl->dev))
		return -ENODEV;

	dev = cl->dev;

	mutex_lock(&dev->device_lock);
	if (cl == &dev->iamthif_cl) {
		rets = mei_amthif_release(dev, file);
		goto out;
	}
	if (cl->state == MEI_FILE_CONNECTED) {
		cl->state = MEI_FILE_DISCONNECTING;
		cl_dbg(dev, cl, "disconnecting\n");
		rets = mei_cl_disconnect(cl);
	}
	mei_cl_flush_queues(cl);
	cl_dbg(dev, cl, "removing\n");

	mei_cl_unlink(cl);


	/* free read cb */
	cb = NULL;
	if (cl->read_cb) {
		cb = mei_cl_find_read_cb(cl);
		/* Remove entry from read list */
		if (cb)
			list_del(&cb->list);

		cb = cl->read_cb;
		cl->read_cb = NULL;
	}

	file->private_data = NULL;

	mei_io_cb_free(cb);

	kfree(cl);
out:
	mutex_unlock(&dev->device_lock);
	return rets;
}
Beispiel #2
0
/**
 * mei_release - the release function
 *
 * @inode: pointer to inode structure
 * @file: pointer to file structure
 *
 * Return: 0 on success, <0 on error
 */
static int mei_release(struct inode *inode, struct file *file)
{
	struct mei_cl *cl = file->private_data;
	struct mei_device *dev;
	int rets;

	if (WARN_ON(!cl || !cl->dev))
		return -ENODEV;

	dev = cl->dev;

	mutex_lock(&dev->device_lock);
	if (cl == &dev->iamthif_cl) {
		rets = mei_amthif_release(dev, file);
		goto out;
	}
	rets = mei_cl_disconnect(cl);

	mei_cl_flush_queues(cl, file);
	cl_dbg(dev, cl, "removing\n");

	mei_cl_unlink(cl);

	file->private_data = NULL;

	kfree(cl);
out:
	mutex_unlock(&dev->device_lock);
	return rets;
}
Beispiel #3
0
/**
 * mei_wd_ops_start - wd start command from the watchdog core.
 *
 * @wd_dev: watchdog device struct
 *
 * Return: 0 if success, negative errno code for failure
 */
static int mei_wd_ops_start(struct watchdog_device *wd_dev)
{
	struct mei_device *dev;
	struct mei_cl *cl;
	int err = -ENODEV;

	dev = watchdog_get_drvdata(wd_dev);
	if (!dev)
		return -ENODEV;

	cl = &dev->wd_cl;

	mutex_lock(&dev->device_lock);

	if (dev->dev_state != MEI_DEV_ENABLED) {
		dev_dbg(dev->dev, "wd: dev_state != MEI_DEV_ENABLED  dev_state = %s\n",
			mei_dev_state_str(dev->dev_state));
		goto end_unlock;
	}

	if (!mei_cl_is_connected(cl)) {
		cl_dbg(dev, cl, "MEI Driver is not connected to Watchdog Client\n");
		goto end_unlock;
	}

	mei_wd_set_start_timeout(dev, dev->wd_timeout);

	err = 0;
end_unlock:
	mutex_unlock(&dev->device_lock);
	return err;
}
Beispiel #4
0
/**
 * mei_read - the read function.
 *
 * @file: pointer to file structure
 * @ubuf: pointer to user buffer
 * @length: buffer length
 * @offset: data offset in buffer
 *
 * Return: >=0 data length on success , <0 on error
 */
static ssize_t mei_read(struct file *file, char __user *ubuf,
			size_t length, loff_t *offset)
{
	struct mei_cl *cl = file->private_data;
	struct mei_device *dev;
	struct mei_cl_cb *cb = NULL;
	bool nonblock = !!(file->f_flags & O_NONBLOCK);
	int rets;

	if (WARN_ON(!cl || !cl->dev))
		return -ENODEV;

	dev = cl->dev;


	mutex_lock(&dev->device_lock);
	if (dev->dev_state != MEI_DEV_ENABLED) {
		rets = -ENODEV;
		goto out;
	}

	if (length == 0) {
		rets = 0;
		goto out;
	}

	if (ubuf == NULL) {
		rets = -EMSGSIZE;
		goto out;
	}

	cb = mei_cl_read_cb(cl, file);
	if (cb)
		goto copy_buffer;

	if (*offset > 0)
		*offset = 0;

	rets = mei_cl_read_start(cl, length, file);
	if (rets && rets != -EBUSY) {
		cl_dbg(dev, cl, "mei start read failure status = %d\n", rets);
		goto out;
	}

	if (nonblock) {
		rets = -EAGAIN;
		goto out;
	}

	if (rets == -EBUSY &&
	    !mei_cl_enqueue_ctrl_wr_cb(cl, length, MEI_FOP_READ, file)) {
		rets = -ENOMEM;
		goto out;
	}

	do {
		mutex_unlock(&dev->device_lock);

		if (wait_event_interruptible(cl->rx_wait,
					     (!list_empty(&cl->rd_completed)) ||
					     (!mei_cl_is_connected(cl)))) {

			if (signal_pending(current))
				return -EINTR;
			return -ERESTARTSYS;
		}

		mutex_lock(&dev->device_lock);
		if (!mei_cl_is_connected(cl)) {
			rets = -ENODEV;
			goto out;
		}

		cb = mei_cl_read_cb(cl, file);
	} while (!cb);

copy_buffer:
	/* now copy the data to user space */
	if (cb->status) {
		rets = cb->status;
		cl_dbg(dev, cl, "read operation failed %d\n", rets);
		goto free;
	}

	cl_dbg(dev, cl, "buf.size = %zu buf.idx = %zu offset = %lld\n",
	       cb->buf.size, cb->buf_idx, *offset);
	if (*offset >= cb->buf_idx) {
		rets = 0;
		goto free;
	}

	/* length is being truncated to PAGE_SIZE,
	 * however buf_idx may point beyond that */
	length = min_t(size_t, length, cb->buf_idx - *offset);

	if (copy_to_user(ubuf, cb->buf.data + *offset, length)) {
		dev_dbg(dev->dev, "failed to copy data to userland\n");
		rets = -EFAULT;
		goto free;
	}

	rets = length;
	*offset += length;
	/* not all data was read, keep the cb */
	if (*offset < cb->buf_idx)
		goto out;

free:
	mei_io_cb_free(cb);
	*offset = 0;

out:
	cl_dbg(dev, cl, "end mei read rets = %d\n", rets);
	mutex_unlock(&dev->device_lock);
	return rets;
}
Beispiel #5
0
/**
 * mei_read - the read function.
 *
 * @file: pointer to file structure
 * @ubuf: pointer to user buffer
 * @length: buffer length
 * @offset: data offset in buffer
 *
 * Return: >=0 data length on success , <0 on error
 */
static ssize_t mei_read(struct file *file, char __user *ubuf,
			size_t length, loff_t *offset)
{
	struct mei_cl *cl = file->private_data;
	struct mei_device *dev;
	struct mei_cl_cb *cb = NULL;
	int rets;
	int err;


	if (WARN_ON(!cl || !cl->dev))
		return -ENODEV;

	dev = cl->dev;


	mutex_lock(&dev->device_lock);
	if (dev->dev_state != MEI_DEV_ENABLED) {
		rets = -ENODEV;
		goto out;
	}

	if (length == 0) {
		rets = 0;
		goto out;
	}

	if (cl == &dev->iamthif_cl) {
		rets = mei_amthif_read(dev, file, ubuf, length, offset);
		goto out;
	}

	cb = mei_cl_read_cb(cl, file);
	if (cb) {
		/* read what left */
		if (cb->buf_idx > *offset)
			goto copy_buffer;
		/* offset is beyond buf_idx we have no more data return 0 */
		if (cb->buf_idx > 0 && cb->buf_idx <= *offset) {
			rets = 0;
			goto free;
		}
		/* Offset needs to be cleaned for contiguous reads*/
		if (cb->buf_idx == 0 && *offset > 0)
			*offset = 0;
	} else if (*offset > 0) {
		*offset = 0;
	}

	err = mei_cl_read_start(cl, length, file);
	if (err && err != -EBUSY) {
		cl_dbg(dev, cl, "mei start read failure status = %d\n", err);
		rets = err;
		goto out;
	}

	if (list_empty(&cl->rd_completed) && !waitqueue_active(&cl->rx_wait)) {
		if (file->f_flags & O_NONBLOCK) {
			rets = -EAGAIN;
			goto out;
		}

		mutex_unlock(&dev->device_lock);

		if (wait_event_interruptible(cl->rx_wait,
				(!list_empty(&cl->rd_completed)) ||
				(!mei_cl_is_connected(cl)))) {

			if (signal_pending(current))
				return -EINTR;
			return -ERESTARTSYS;
		}

		mutex_lock(&dev->device_lock);
		if (!mei_cl_is_connected(cl)) {
			rets = -EBUSY;
			goto out;
		}
	}

	cb = mei_cl_read_cb(cl, file);
	if (!cb) {
		if (mei_cl_is_fixed_address(cl) && dev->allow_fixed_address) {
			cb = mei_cl_read_cb(cl, NULL);
			if (cb)
				goto copy_buffer;
		}
		rets = 0;
		goto out;
	}

copy_buffer:
	/* now copy the data to user space */
	if (cb->status) {
		rets = cb->status;
		cl_dbg(dev, cl, "read operation failed %d\n", rets);
		goto free;
	}

	cl_dbg(dev, cl, "buf.size = %d buf.idx = %ld\n",
	    cb->buf.size, cb->buf_idx);
	if (length == 0 || ubuf == NULL || *offset > cb->buf_idx) {
		rets = -EMSGSIZE;
		goto free;
	}

	/* length is being truncated to PAGE_SIZE,
	 * however buf_idx may point beyond that */
	length = min_t(size_t, length, cb->buf_idx - *offset);

	if (copy_to_user(ubuf, cb->buf.data + *offset, length)) {
		dev_dbg(dev->dev, "failed to copy data to userland\n");
		rets = -EFAULT;
		goto free;
	}

	rets = length;
	*offset += length;
	if ((unsigned long)*offset < cb->buf_idx)
		goto out;

free:
	mei_io_cb_free(cb);

out:
	cl_dbg(dev, cl, "end mei read rets = %d\n", rets);
	mutex_unlock(&dev->device_lock);
	return rets;
}