static int rproc_handle_vdev()

in remoteproc_core.c [515:616]


static int rproc_handle_vdev(struct rproc *rproc, void *ptr,
			     int offset, int avail)
{
	struct fw_rsc_vdev *rsc = ptr;
	struct device *dev = &rproc->dev;
	struct rproc_vdev *rvdev;
	int i, ret;
	char name[16];

	/* make sure resource isn't truncated */
	if (struct_size(rsc, vring, rsc->num_of_vrings) + rsc->config_len >
			avail) {
		dev_err(dev, "vdev rsc is truncated\n");
		return -EINVAL;
	}

	/* make sure reserved bytes are zeroes */
	if (rsc->reserved[0] || rsc->reserved[1]) {
		dev_err(dev, "vdev rsc has non zero reserved bytes\n");
		return -EINVAL;
	}

	dev_dbg(dev, "vdev rsc: id %d, dfeatures 0x%x, cfg len %d, %d vrings\n",
		rsc->id, rsc->dfeatures, rsc->config_len, rsc->num_of_vrings);

	/* we currently support only two vrings per rvdev */
	if (rsc->num_of_vrings > ARRAY_SIZE(rvdev->vring)) {
		dev_err(dev, "too many vrings: %d\n", rsc->num_of_vrings);
		return -EINVAL;
	}

	rvdev = kzalloc(sizeof(*rvdev), GFP_KERNEL);
	if (!rvdev)
		return -ENOMEM;

	kref_init(&rvdev->refcount);

	rvdev->id = rsc->id;
	rvdev->rproc = rproc;
	rvdev->index = rproc->nb_vdev++;

	/* Initialise vdev subdevice */
	snprintf(name, sizeof(name), "vdev%dbuffer", rvdev->index);
	rvdev->dev.parent = &rproc->dev;
	rvdev->dev.release = rproc_rvdev_release;
	dev_set_name(&rvdev->dev, "%s#%s", dev_name(rvdev->dev.parent), name);
	dev_set_drvdata(&rvdev->dev, rvdev);

	ret = device_register(&rvdev->dev);
	if (ret) {
		put_device(&rvdev->dev);
		return ret;
	}

	ret = copy_dma_range_map(&rvdev->dev, rproc->dev.parent);
	if (ret)
		goto free_rvdev;

	/* Make device dma capable by inheriting from parent's capabilities */
	set_dma_ops(&rvdev->dev, get_dma_ops(rproc->dev.parent));

	ret = dma_coerce_mask_and_coherent(&rvdev->dev,
					   dma_get_mask(rproc->dev.parent));
	if (ret) {
		dev_warn(dev,
			 "Failed to set DMA mask %llx. Trying to continue... (%pe)\n",
			 dma_get_mask(rproc->dev.parent), ERR_PTR(ret));
	}

	/* parse the vrings */
	for (i = 0; i < rsc->num_of_vrings; i++) {
		ret = rproc_parse_vring(rvdev, rsc, i);
		if (ret)
			goto free_rvdev;
	}

	/* remember the resource offset*/
	rvdev->rsc_offset = offset;

	/* allocate the vring resources */
	for (i = 0; i < rsc->num_of_vrings; i++) {
		ret = rproc_alloc_vring(rvdev, i);
		if (ret)
			goto unwind_vring_allocations;
	}

	list_add_tail(&rvdev->node, &rproc->rvdevs);

	rvdev->subdev.start = rproc_vdev_do_start;
	rvdev->subdev.stop = rproc_vdev_do_stop;

	rproc_add_subdev(rproc, &rvdev->subdev);

	return 0;

unwind_vring_allocations:
	for (i--; i >= 0; i--)
		rproc_free_vring(&rvdev->vring[i]);
free_rvdev:
	device_unregister(&rvdev->dev);
	return ret;
}