core.c

来自「linux 内核源代码」· C语言 代码 · 共 1,357 行 · 第 1/3 页

C
1,357
字号
	if (parent)		klist_del(&dev->knode_parent);	if (MAJOR(dev->devt))		device_remove_file(dev, &devt_attr);	if (dev->class) {		sysfs_remove_link(&dev->kobj, "subsystem");		/* If this is not a "fake" compatible device, remove the		 * symlink from the class to the device. */		if (dev->kobj.parent != &dev->class->subsys.kobj)			sysfs_remove_link(&dev->class->subsys.kobj,					  dev->bus_id);		if (parent) {#ifdef CONFIG_SYSFS_DEPRECATED			char *class_name = make_class_name(dev->class->name,							   &dev->kobj);			if (class_name)				sysfs_remove_link(&dev->parent->kobj,						  class_name);			kfree(class_name);#endif			sysfs_remove_link(&dev->kobj, "device");		}		down(&dev->class->sem);		/* notify any interfaces that the device is now gone */		list_for_each_entry(class_intf, &dev->class->interfaces, node)			if (class_intf->remove_dev)				class_intf->remove_dev(dev, class_intf);		/* remove the device from the class list */		list_del_init(&dev->node);		up(&dev->class->sem);		/* If we live in a parent class-directory, unreference it */		if (dev->kobj.parent->kset == &dev->class->class_dirs) {			struct device *d;			int other = 0;			/*			 * if we are the last child of our class, delete			 * our class-directory at this parent			 */			down(&dev->class->sem);			list_for_each_entry(d, &dev->class->devices, node) {				if (d == dev)					continue;				if (d->kobj.parent == dev->kobj.parent) {					other = 1;					break;				}			}			if (!other)				kobject_del(dev->kobj.parent);			kobject_put(dev->kobj.parent);			up(&dev->class->sem);		}	}	device_remove_file(dev, &uevent_attr);	device_remove_attrs(dev);	bus_remove_device(dev);	/*	 * Some platform devices are driven without driver attached	 * and managed resources may have been acquired.  Make sure	 * all resources are released.	 */	devres_release_all(dev);	/* Notify the platform of the removal, in case they	 * need to do anything...	 */	if (platform_notify_remove)		platform_notify_remove(dev);	if (dev->bus)		blocking_notifier_call_chain(&dev->bus->bus_notifier,					     BUS_NOTIFY_DEL_DEVICE, dev);	device_pm_remove(dev);	kobject_uevent(&dev->kobj, KOBJ_REMOVE);	kobject_del(&dev->kobj);	if (parent)		put_device(parent);}/** *	device_unregister - unregister device from system. *	@dev:	device going away. * *	We do this in two parts, like we do device_register(). First, *	we remove it from all the subsystems with device_del(), then *	we decrement the reference count via put_device(). If that *	is the final reference count, the device will be cleaned up *	via device_release() above. Otherwise, the structure will *	stick around until the final reference to the device is dropped. */void device_unregister(struct device * dev){	pr_debug("DEV: Unregistering device. ID = '%s'\n", dev->bus_id);	device_del(dev);	put_device(dev);}static struct device * next_device(struct klist_iter * i){	struct klist_node * n = klist_next(i);	return n ? container_of(n, struct device, knode_parent) : NULL;}/** *	device_for_each_child - device child iterator. *	@parent: parent struct device. *	@data:	data for the callback. *	@fn:	function to be called for each device. * *	Iterate over @parent's child devices, and call @fn for each, *	passing it @data. * *	We check the return of @fn each time. If it returns anything *	other than 0, we break out and return that value. */int device_for_each_child(struct device * parent, void * data,		     int (*fn)(struct device *, void *)){	struct klist_iter i;	struct device * child;	int error = 0;	klist_iter_init(&parent->klist_children, &i);	while ((child = next_device(&i)) && !error)		error = fn(child, data);	klist_iter_exit(&i);	return error;}/** * device_find_child - device iterator for locating a particular device. * @parent: parent struct device * @data: Data to pass to match function * @match: Callback function to check device * * This is similar to the device_for_each_child() function above, but it * returns a reference to a device that is 'found' for later use, as * determined by the @match callback. * * The callback should return 0 if the device doesn't match and non-zero * if it does.  If the callback returns non-zero and a reference to the * current device can be obtained, this function will return to the caller * and not iterate over any more devices. */struct device * device_find_child(struct device *parent, void *data,				  int (*match)(struct device *, void *)){	struct klist_iter i;	struct device *child;	if (!parent)		return NULL;	klist_iter_init(&parent->klist_children, &i);	while ((child = next_device(&i)))		if (match(child, data) && get_device(child))			break;	klist_iter_exit(&i);	return child;}int __init devices_init(void){	return subsystem_register(&devices_subsys);}EXPORT_SYMBOL_GPL(device_for_each_child);EXPORT_SYMBOL_GPL(device_find_child);EXPORT_SYMBOL_GPL(device_initialize);EXPORT_SYMBOL_GPL(device_add);EXPORT_SYMBOL_GPL(device_register);EXPORT_SYMBOL_GPL(device_del);EXPORT_SYMBOL_GPL(device_unregister);EXPORT_SYMBOL_GPL(get_device);EXPORT_SYMBOL_GPL(put_device);EXPORT_SYMBOL_GPL(device_create_file);EXPORT_SYMBOL_GPL(device_remove_file);static void device_create_release(struct device *dev){	pr_debug("%s called for %s\n", __FUNCTION__, dev->bus_id);	kfree(dev);}/** * device_create - creates a device and registers it with sysfs * @class: pointer to the struct class that this device should be registered to * @parent: pointer to the parent struct device of this new device, if any * @devt: the dev_t for the char device to be added * @fmt: string for the device's name * * This function can be used by char device classes.  A struct device * will be created in sysfs, registered to the specified class. * * A "dev" file will be created, showing the dev_t for the device, if * the dev_t is not 0,0. * If a pointer to a parent struct device is passed in, the newly created * struct device will be a child of that device in sysfs. * The pointer to the struct device will be returned from the call. * Any further sysfs files that might be required can be created using this * pointer. * * Note: the struct class passed to this function must have previously * been created with a call to class_create(). */struct device *device_create(struct class *class, struct device *parent,			     dev_t devt, const char *fmt, ...){	va_list args;	struct device *dev = NULL;	int retval = -ENODEV;	if (class == NULL || IS_ERR(class))		goto error;	dev = kzalloc(sizeof(*dev), GFP_KERNEL);	if (!dev) {		retval = -ENOMEM;		goto error;	}	dev->devt = devt;	dev->class = class;	dev->parent = parent;	dev->release = device_create_release;	va_start(args, fmt);	vsnprintf(dev->bus_id, BUS_ID_SIZE, fmt, args);	va_end(args);	retval = device_register(dev);	if (retval)		goto error;	return dev;error:	kfree(dev);	return ERR_PTR(retval);}EXPORT_SYMBOL_GPL(device_create);/** * device_destroy - removes a device that was created with device_create() * @class: pointer to the struct class that this device was registered with * @devt: the dev_t of the device that was previously registered * * This call unregisters and cleans up a device that was created with a * call to device_create(). */void device_destroy(struct class *class, dev_t devt){	struct device *dev = NULL;	struct device *dev_tmp;	down(&class->sem);	list_for_each_entry(dev_tmp, &class->devices, node) {		if (dev_tmp->devt == devt) {			dev = dev_tmp;			break;		}	}	up(&class->sem);	if (dev)		device_unregister(dev);}EXPORT_SYMBOL_GPL(device_destroy);/** * device_rename - renames a device * @dev: the pointer to the struct device to be renamed * @new_name: the new name of the device */int device_rename(struct device *dev, char *new_name){	char *old_class_name = NULL;	char *new_class_name = NULL;	char *old_device_name = NULL;	int error;	dev = get_device(dev);	if (!dev)		return -EINVAL;	pr_debug("DEVICE: renaming '%s' to '%s'\n", dev->bus_id, new_name);#ifdef CONFIG_SYSFS_DEPRECATED	if ((dev->class) && (dev->parent))		old_class_name = make_class_name(dev->class->name, &dev->kobj);#endif	old_device_name = kmalloc(BUS_ID_SIZE, GFP_KERNEL);	if (!old_device_name) {		error = -ENOMEM;		goto out;	}	strlcpy(old_device_name, dev->bus_id, BUS_ID_SIZE);	strlcpy(dev->bus_id, new_name, BUS_ID_SIZE);	error = kobject_rename(&dev->kobj, new_name);	if (error) {		strlcpy(dev->bus_id, old_device_name, BUS_ID_SIZE);		goto out;	}#ifdef CONFIG_SYSFS_DEPRECATED	if (old_class_name) {		new_class_name = make_class_name(dev->class->name, &dev->kobj);		if (new_class_name) {			error = sysfs_create_link(&dev->parent->kobj,						  &dev->kobj, new_class_name);			if (error)				goto out;			sysfs_remove_link(&dev->parent->kobj, old_class_name);		}	}#else	if (dev->class) {		sysfs_remove_link(&dev->class->subsys.kobj, old_device_name);		error = sysfs_create_link(&dev->class->subsys.kobj, &dev->kobj,					  dev->bus_id);		if (error) {			dev_err(dev, "%s: sysfs_create_symlink failed (%d)\n",				__FUNCTION__, error);		}	}#endifout:	put_device(dev);	kfree(new_class_name);	kfree(old_class_name);	kfree(old_device_name);	return error;}EXPORT_SYMBOL_GPL(device_rename);static int device_move_class_links(struct device *dev,				   struct device *old_parent,				   struct device *new_parent){	int error = 0;#ifdef CONFIG_SYSFS_DEPRECATED	char *class_name;	class_name = make_class_name(dev->class->name, &dev->kobj);	if (!class_name) {		error = -ENOMEM;		goto out;	}	if (old_parent) {		sysfs_remove_link(&dev->kobj, "device");		sysfs_remove_link(&old_parent->kobj, class_name);	}	if (new_parent) {		error = sysfs_create_link(&dev->kobj, &new_parent->kobj,					  "device");		if (error)			goto out;		error = sysfs_create_link(&new_parent->kobj, &dev->kobj,					  class_name);		if (error)			sysfs_remove_link(&dev->kobj, "device");	}	else		error = 0;out:	kfree(class_name);	return error;#else	if (old_parent)		sysfs_remove_link(&dev->kobj, "device");	if (new_parent)		error = sysfs_create_link(&dev->kobj, &new_parent->kobj,					  "device");	return error;#endif}/** * device_move - moves a device to a new parent * @dev: the pointer to the struct device to be moved * @new_parent: the new parent of the device (can by NULL) */int device_move(struct device *dev, struct device *new_parent){	int error;	struct device *old_parent;	struct kobject *new_parent_kobj;	dev = get_device(dev);	if (!dev)		return -EINVAL;	new_parent = get_device(new_parent);	new_parent_kobj = get_device_parent (dev, new_parent);	if (IS_ERR(new_parent_kobj)) {		error = PTR_ERR(new_parent_kobj);		put_device(new_parent);		goto out;	}	pr_debug("DEVICE: moving '%s' to '%s'\n", dev->bus_id,		 new_parent ? new_parent->bus_id : "<NULL>");	error = kobject_move(&dev->kobj, new_parent_kobj);	if (error) {		put_device(new_parent);		goto out;	}	old_parent = dev->parent;	dev->parent = new_parent;	if (old_parent)		klist_remove(&dev->knode_parent);	if (new_parent)		klist_add_tail(&dev->knode_parent, &new_parent->klist_children);	if (!dev->class)		goto out_put;	error = device_move_class_links(dev, old_parent, new_parent);	if (error) {		/* We ignore errors on cleanup since we're hosed anyway... */		device_move_class_links(dev, new_parent, old_parent);		if (!kobject_move(&dev->kobj, &old_parent->kobj)) {			if (new_parent)				klist_remove(&dev->knode_parent);			if (old_parent)				klist_add_tail(&dev->knode_parent,					       &old_parent->klist_children);		}		put_device(new_parent);		goto out;	}out_put:	put_device(old_parent);out:	put_device(dev);	return error;}EXPORT_SYMBOL_GPL(device_move);

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?