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;
}