Commit 37c12e74 authored by Russell King's avatar Russell King Committed by Russell King

[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: default avatarRussell King <rmk+kernel@arm.linux.org.uk>
Acked-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 7015faa7
......@@ -116,12 +116,115 @@ int platform_add_devices(struct platform_device **devs, int num)
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
*
* 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;
......@@ -174,6 +277,18 @@ int platform_device_register(struct platform_device * pdev)
release_resource(&pdev->resource[i]);
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
......@@ -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
* @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 resource *res, unsigned int num)
{
struct platform_object *pobj;
struct platform_device *pdev;
int retval;
pobj = kzalloc(sizeof(*pobj) + sizeof(struct resource) * num, GFP_KERNEL);
if (!pobj) {
pdev = platform_device_alloc(name, id);
if (!pdev) {
retval = -ENOMEM;
goto error;
}
pobj->pdev.name = name;
pobj->pdev.id = id;
pobj->pdev.dev.release = platform_device_release_simple;
if (num) {
memcpy(pobj->resources, res, sizeof(struct resource) * num);
pobj->pdev.resource = pobj->resources;
pobj->pdev.num_resources = num;
retval = platform_device_add_resources(pdev, res, num);
if (retval)
goto error;
}
retval = platform_device_register(&pobj->pdev);
retval = platform_device_add(pdev);
if (retval)
goto error;
return &pobj->pdev;
return pdev;
error:
kfree(pobj);
platform_device_put(pdev);
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_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_ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment