mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	[DRIVER MODEL] Improved dynamically allocated platform_device interface
Re-jig the simple platform device support to allow private data
to be attached to a platform device, as well as allowing the
parent device to be set.
Example usage:
	pdev = platform_device_alloc("mydev", id);
	if (pdev) {
		err = platform_device_add_resources(pdev, &resources,
						    ARRAY_SIZE(resources));
		if (err == 0)
			err = platform_device_add_data(pdev, &platform_data,
						       sizeof(platform_data));
		if (err == 0)
			err = platform_device_add(pdev);
	} else {
		err = -ENOMEM;
	}
	if (err)
		platform_device_put(pdev);
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Acked-by: Greg Kroah-Hartman <gregkh@suse.de>
			
			
This commit is contained in:
		
							parent
							
								
									7015faa7df
								
							
						
					
					
						commit
						37c12e7497
					
				
					 2 changed files with 132 additions and 27 deletions
				
			
		| 
						 | 
					@ -116,12 +116,115 @@ int platform_add_devices(struct platform_device **devs, int num)
 | 
				
			||||||
	return ret;
 | 
						return ret;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct platform_object {
 | 
				
			||||||
 | 
						struct platform_device pdev;
 | 
				
			||||||
 | 
						char name[1];
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 *	platform_device_register - add a platform-level device
 | 
					 *	platform_device_put
 | 
				
			||||||
 | 
					 *	@pdev:	platform device to free
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *	Free all memory associated with a platform device.  This function
 | 
				
			||||||
 | 
					 *	must _only_ be externally called in error cases.  All other usage
 | 
				
			||||||
 | 
					 *	is a bug.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void platform_device_put(struct platform_device *pdev)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (pdev)
 | 
				
			||||||
 | 
							put_device(&pdev->dev);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					EXPORT_SYMBOL_GPL(platform_device_put);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void platform_device_release(struct device *dev)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct platform_object *pa = container_of(dev, struct platform_object, pdev.dev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						kfree(pa->pdev.dev.platform_data);
 | 
				
			||||||
 | 
						kfree(pa->pdev.resource);
 | 
				
			||||||
 | 
						kfree(pa);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 *	platform_device_alloc
 | 
				
			||||||
 | 
					 *	@name:	base name of the device we're adding
 | 
				
			||||||
 | 
					 *	@id:    instance id
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *	Create a platform device object which can have other objects attached
 | 
				
			||||||
 | 
					 *	to it, and which will have attached objects freed when it is released.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					struct platform_device *platform_device_alloc(const char *name, unsigned int id)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct platform_object *pa;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pa = kzalloc(sizeof(struct platform_object) + strlen(name), GFP_KERNEL);
 | 
				
			||||||
 | 
						if (pa) {
 | 
				
			||||||
 | 
							strcpy(pa->name, name);
 | 
				
			||||||
 | 
							pa->pdev.name = pa->name;
 | 
				
			||||||
 | 
							pa->pdev.id = id;
 | 
				
			||||||
 | 
							device_initialize(&pa->pdev.dev);
 | 
				
			||||||
 | 
							pa->pdev.dev.release = platform_device_release;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return pa ? &pa->pdev : NULL;	
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					EXPORT_SYMBOL_GPL(platform_device_alloc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 *	platform_device_add_resources
 | 
				
			||||||
 | 
					 *	@pdev:	platform device allocated by platform_device_alloc to add resources to
 | 
				
			||||||
 | 
					 *	@res:   set of resources that needs to be allocated for the device
 | 
				
			||||||
 | 
					 *	@num:	number of resources
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *	Add a copy of the resources to the platform device.  The memory
 | 
				
			||||||
 | 
					 *	associated with the resources will be freed when the platform
 | 
				
			||||||
 | 
					 *	device is released.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					int platform_device_add_resources(struct platform_device *pdev, struct resource *res, unsigned int num)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct resource *r;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						r = kmalloc(sizeof(struct resource) * num, GFP_KERNEL);
 | 
				
			||||||
 | 
						if (r) {
 | 
				
			||||||
 | 
							memcpy(r, res, sizeof(struct resource) * num);
 | 
				
			||||||
 | 
							pdev->resource = r;
 | 
				
			||||||
 | 
							pdev->num_resources = num;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return r ? 0 : -ENOMEM;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					EXPORT_SYMBOL_GPL(platform_device_add_resources);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 *	platform_device_add_data
 | 
				
			||||||
 | 
					 *	@pdev:	platform device allocated by platform_device_alloc to add resources to
 | 
				
			||||||
 | 
					 *	@data:	platform specific data for this platform device
 | 
				
			||||||
 | 
					 *	@size:	size of platform specific data
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *	Add a copy of platform specific data to the platform device's platform_data
 | 
				
			||||||
 | 
					 *	pointer.  The memory associated with the platform data will be freed
 | 
				
			||||||
 | 
					 *	when the platform device is released.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					int platform_device_add_data(struct platform_device *pdev, void *data, size_t size)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						void *d;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						d = kmalloc(size, GFP_KERNEL);
 | 
				
			||||||
 | 
						if (d) {
 | 
				
			||||||
 | 
							memcpy(d, data, size);
 | 
				
			||||||
 | 
							pdev->dev.platform_data = d;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return d ? 0 : -ENOMEM;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					EXPORT_SYMBOL_GPL(platform_device_add_data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 *	platform_device_add - add a platform device to device hierarchy
 | 
				
			||||||
 *	@pdev:	platform device we're adding
 | 
					 *	@pdev:	platform device we're adding
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 | 
					 *	This is part 2 of platform_device_register(), though may be called
 | 
				
			||||||
 | 
					 *	separately _iff_ pdev was allocated by platform_device_alloc().
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
int platform_device_register(struct platform_device * pdev)
 | 
					int platform_device_add(struct platform_device *pdev)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int i, ret = 0;
 | 
						int i, ret = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -174,6 +277,18 @@ int platform_device_register(struct platform_device * pdev)
 | 
				
			||||||
			release_resource(&pdev->resource[i]);
 | 
								release_resource(&pdev->resource[i]);
 | 
				
			||||||
	return ret;
 | 
						return ret;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					EXPORT_SYMBOL_GPL(platform_device_add);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 *	platform_device_register - add a platform-level device
 | 
				
			||||||
 | 
					 *	@pdev:	platform device we're adding
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					int platform_device_register(struct platform_device * pdev)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						device_initialize(&pdev->dev);
 | 
				
			||||||
 | 
						return platform_device_add(pdev);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 *	platform_device_unregister - remove a platform-level device
 | 
					 *	platform_device_unregister - remove a platform-level device
 | 
				
			||||||
| 
						 | 
					@ -197,18 +312,6 @@ void platform_device_unregister(struct platform_device * pdev)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct platform_object {
 | 
					 | 
				
			||||||
        struct platform_device pdev;
 | 
					 | 
				
			||||||
        struct resource resources[0];
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void platform_device_release_simple(struct device *dev)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct platform_device *pdev = to_platform_device(dev);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	kfree(container_of(pdev, struct platform_object, pdev));
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 *	platform_device_register_simple
 | 
					 *	platform_device_register_simple
 | 
				
			||||||
 *	@name:  base name of the device we're adding
 | 
					 *	@name:  base name of the device we're adding
 | 
				
			||||||
| 
						 | 
					@ -225,33 +328,29 @@ static void platform_device_release_simple(struct device *dev)
 | 
				
			||||||
struct platform_device *platform_device_register_simple(char *name, unsigned int id,
 | 
					struct platform_device *platform_device_register_simple(char *name, unsigned int id,
 | 
				
			||||||
							struct resource *res, unsigned int num)
 | 
												struct resource *res, unsigned int num)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct platform_object *pobj;
 | 
						struct platform_device *pdev;
 | 
				
			||||||
	int retval;
 | 
						int retval;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pobj = kzalloc(sizeof(*pobj) + sizeof(struct resource) * num, GFP_KERNEL);
 | 
						pdev = platform_device_alloc(name, id);
 | 
				
			||||||
	if (!pobj) {
 | 
						if (!pdev) {
 | 
				
			||||||
		retval = -ENOMEM;
 | 
							retval = -ENOMEM;
 | 
				
			||||||
		goto error;
 | 
							goto error;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pobj->pdev.name = name;
 | 
					 | 
				
			||||||
	pobj->pdev.id = id;
 | 
					 | 
				
			||||||
	pobj->pdev.dev.release = platform_device_release_simple;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (num) {
 | 
						if (num) {
 | 
				
			||||||
		memcpy(pobj->resources, res, sizeof(struct resource) * num);
 | 
							retval = platform_device_add_resources(pdev, res, num);
 | 
				
			||||||
		pobj->pdev.resource = pobj->resources;
 | 
							if (retval)
 | 
				
			||||||
		pobj->pdev.num_resources = num;
 | 
								goto error;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	retval = platform_device_register(&pobj->pdev);
 | 
						retval = platform_device_add(pdev);
 | 
				
			||||||
	if (retval)
 | 
						if (retval)
 | 
				
			||||||
		goto error;
 | 
							goto error;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return &pobj->pdev;
 | 
						return pdev;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
error:
 | 
					error:
 | 
				
			||||||
	kfree(pobj);
 | 
						platform_device_put(pdev);
 | 
				
			||||||
	return ERR_PTR(retval);
 | 
						return ERR_PTR(retval);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -37,4 +37,10 @@ extern int platform_add_devices(struct platform_device **, int);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
extern struct platform_device *platform_device_register_simple(char *, unsigned int, struct resource *, unsigned int);
 | 
					extern struct platform_device *platform_device_register_simple(char *, unsigned int, struct resource *, unsigned int);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern struct platform_device *platform_device_alloc(const char *name, unsigned int id);
 | 
				
			||||||
 | 
					extern int platform_device_add_resources(struct platform_device *pdev, struct resource *res, unsigned int num);
 | 
				
			||||||
 | 
					extern int platform_device_add_data(struct platform_device *pdev, void *data, size_t size);
 | 
				
			||||||
 | 
					extern int platform_device_add(struct platform_device *pdev);
 | 
				
			||||||
 | 
					extern void platform_device_put(struct platform_device *pdev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif /* _PLATFORM_DEVICE_H_ */
 | 
					#endif /* _PLATFORM_DEVICE_H_ */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue