static int rio_mport_add_riodev()

in devices/rio_mport_cdev.c [1676:1815]


static int rio_mport_add_riodev(struct mport_cdev_priv *priv,
				   void __user *arg)
{
	struct mport_dev *md = priv->md;
	struct rio_rdev_info dev_info;
	struct rio_dev *rdev;
	struct rio_switch *rswitch = NULL;
	struct rio_mport *mport;
	struct device *dev;
	size_t size;
	u32 rval;
	u32 swpinfo = 0;
	u16 destid;
	u8 hopcount;
	int err;

	if (copy_from_user(&dev_info, arg, sizeof(dev_info)))
		return -EFAULT;
	dev_info.name[sizeof(dev_info.name) - 1] = '\0';

	rmcd_debug(RDEV, "name:%s ct:0x%x did:0x%x hc:0x%x", dev_info.name,
		   dev_info.comptag, dev_info.destid, dev_info.hopcount);

	dev = bus_find_device_by_name(&rio_bus_type, NULL, dev_info.name);
	if (dev) {
		rmcd_debug(RDEV, "device %s already exists", dev_info.name);
		put_device(dev);
		return -EEXIST;
	}

	size = sizeof(*rdev);
	mport = md->mport;
	destid = dev_info.destid;
	hopcount = dev_info.hopcount;

	if (rio_mport_read_config_32(mport, destid, hopcount,
				     RIO_PEF_CAR, &rval))
		return -EIO;

	if (rval & RIO_PEF_SWITCH) {
		rio_mport_read_config_32(mport, destid, hopcount,
					 RIO_SWP_INFO_CAR, &swpinfo);
		size += struct_size(rswitch, nextdev, RIO_GET_TOTAL_PORTS(swpinfo));
	}

	rdev = kzalloc(size, GFP_KERNEL);
	if (rdev == NULL)
		return -ENOMEM;

	if (mport->net == NULL) {
		struct rio_net *net;

		net = rio_alloc_net(mport);
		if (!net) {
			err = -ENOMEM;
			rmcd_debug(RDEV, "failed to allocate net object");
			goto cleanup;
		}

		net->id = mport->id;
		net->hport = mport;
		dev_set_name(&net->dev, "rnet_%d", net->id);
		net->dev.parent = &mport->dev;
		net->dev.release = rio_release_net;
		err = rio_add_net(net);
		if (err) {
			rmcd_debug(RDEV, "failed to register net, err=%d", err);
			kfree(net);
			goto cleanup;
		}
	}

	rdev->net = mport->net;
	rdev->pef = rval;
	rdev->swpinfo = swpinfo;
	rio_mport_read_config_32(mport, destid, hopcount,
				 RIO_DEV_ID_CAR, &rval);
	rdev->did = rval >> 16;
	rdev->vid = rval & 0xffff;
	rio_mport_read_config_32(mport, destid, hopcount, RIO_DEV_INFO_CAR,
				 &rdev->device_rev);
	rio_mport_read_config_32(mport, destid, hopcount, RIO_ASM_ID_CAR,
				 &rval);
	rdev->asm_did = rval >> 16;
	rdev->asm_vid = rval & 0xffff;
	rio_mport_read_config_32(mport, destid, hopcount, RIO_ASM_INFO_CAR,
				 &rval);
	rdev->asm_rev = rval >> 16;

	if (rdev->pef & RIO_PEF_EXT_FEATURES) {
		rdev->efptr = rval & 0xffff;
		rdev->phys_efptr = rio_mport_get_physefb(mport, 0, destid,
						hopcount, &rdev->phys_rmap);

		rdev->em_efptr = rio_mport_get_feature(mport, 0, destid,
						hopcount, RIO_EFB_ERR_MGMNT);
	}

	rio_mport_read_config_32(mport, destid, hopcount, RIO_SRC_OPS_CAR,
				 &rdev->src_ops);
	rio_mport_read_config_32(mport, destid, hopcount, RIO_DST_OPS_CAR,
				 &rdev->dst_ops);

	rdev->comp_tag = dev_info.comptag;
	rdev->destid = destid;
	/* hopcount is stored as specified by a caller, regardles of EP or SW */
	rdev->hopcount = hopcount;

	if (rdev->pef & RIO_PEF_SWITCH) {
		rswitch = rdev->rswitch;
		rswitch->route_table = NULL;
	}

	if (strlen(dev_info.name))
		dev_set_name(&rdev->dev, "%s", dev_info.name);
	else if (rdev->pef & RIO_PEF_SWITCH)
		dev_set_name(&rdev->dev, "%02x:s:%04x", mport->id,
			     rdev->comp_tag & RIO_CTAG_UDEVID);
	else
		dev_set_name(&rdev->dev, "%02x:e:%04x", mport->id,
			     rdev->comp_tag & RIO_CTAG_UDEVID);

	INIT_LIST_HEAD(&rdev->net_list);
	rdev->dev.parent = &mport->net->dev;
	rio_attach_device(rdev);
	rdev->dev.release = rio_release_dev;

	if (rdev->dst_ops & RIO_DST_OPS_DOORBELL)
		rio_init_dbell_res(&rdev->riores[RIO_DOORBELL_RESOURCE],
				   0, 0xffff);
	err = rio_add_device(rdev);
	if (err)
		goto cleanup;
	rio_dev_get(rdev);

	return 0;
cleanup:
	kfree(rdev);
	return err;
}