static void fw_device_init()

in core-device.c [984:1101]


static void fw_device_init(struct work_struct *work)
{
	struct fw_device *device =
		container_of(work, struct fw_device, work.work);
	struct fw_card *card = device->card;
	struct device *revived_dev;
	int minor, ret;

	/*
	 * All failure paths here set node->data to NULL, so that we
	 * don't try to do device_for_each_child() on a kfree()'d
	 * device.
	 */

	ret = read_config_rom(device, device->generation);
	if (ret != RCODE_COMPLETE) {
		if (device->config_rom_retries < MAX_RETRIES &&
		    atomic_read(&device->state) == FW_DEVICE_INITIALIZING) {
			device->config_rom_retries++;
			fw_schedule_device_work(device, RETRY_DELAY);
		} else {
			if (device->node->link_on)
				fw_notice(card, "giving up on node %x: reading config rom failed: %s\n",
					  device->node_id,
					  fw_rcode_string(ret));
			if (device->node == card->root_node)
				fw_schedule_bm_work(card, 0);
			fw_device_release(&device->device);
		}
		return;
	}

	revived_dev = device_find_child(card->device,
					device, lookup_existing_device);
	if (revived_dev) {
		put_device(revived_dev);
		fw_device_release(&device->device);

		return;
	}

	device_initialize(&device->device);

	fw_device_get(device);
	down_write(&fw_device_rwsem);
	minor = idr_alloc(&fw_device_idr, device, 0, 1 << MINORBITS,
			GFP_KERNEL);
	up_write(&fw_device_rwsem);

	if (minor < 0)
		goto error;

	device->device.bus = &fw_bus_type;
	device->device.type = &fw_device_type;
	device->device.parent = card->device;
	device->device.devt = MKDEV(fw_cdev_major, minor);
	dev_set_name(&device->device, "fw%d", minor);

	BUILD_BUG_ON(ARRAY_SIZE(device->attribute_group.attrs) <
			ARRAY_SIZE(fw_device_attributes) +
			ARRAY_SIZE(config_rom_attributes));
	init_fw_attribute_group(&device->device,
				fw_device_attributes,
				&device->attribute_group);

	if (device_add(&device->device)) {
		fw_err(card, "failed to add device\n");
		goto error_with_cdev;
	}

	create_units(device);

	/*
	 * Transition the device to running state.  If it got pulled
	 * out from under us while we did the initialization work, we
	 * have to shut down the device again here.  Normally, though,
	 * fw_node_event will be responsible for shutting it down when
	 * necessary.  We have to use the atomic cmpxchg here to avoid
	 * racing with the FW_NODE_DESTROYED case in
	 * fw_node_event().
	 */
	if (atomic_cmpxchg(&device->state,
			   FW_DEVICE_INITIALIZING,
			   FW_DEVICE_RUNNING) == FW_DEVICE_GONE) {
		device->workfn = fw_device_shutdown;
		fw_schedule_device_work(device, SHUTDOWN_DELAY);
	} else {
		fw_notice(card, "created device %s: GUID %08x%08x, S%d00\n",
			  dev_name(&device->device),
			  device->config_rom[3], device->config_rom[4],
			  1 << device->max_speed);
		device->config_rom_retries = 0;

		set_broadcast_channel(device, device->generation);

		add_device_randomness(&device->config_rom[3], 8);
	}

	/*
	 * Reschedule the IRM work if we just finished reading the
	 * root node config rom.  If this races with a bus reset we
	 * just end up running the IRM work a couple of extra times -
	 * pretty harmless.
	 */
	if (device->node == card->root_node)
		fw_schedule_bm_work(card, 0);

	return;

 error_with_cdev:
	down_write(&fw_device_rwsem);
	idr_remove(&fw_device_idr, minor);
	up_write(&fw_device_rwsem);
 error:
	fw_device_put(device);		/* fw_device_idr's reference */

	put_device(&device->device);	/* our reference */
}