qom: add new dynamic property infrastructure based on Visitors (v2)

qdev properties are settable only during construction and static to classes.
This isn't flexible enough for QOM.

This patch introduces a property interface for qdev that provides dynamic
properties that are tied to objects, instead of classes.  These properties are
Visitor based instead of string based too.

Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
master
Anthony Liguori 2011-12-12 14:29:26 -06:00
parent 85ed303bfe
commit 44677ded43
4 changed files with 224 additions and 0 deletions

View File

@ -98,6 +98,7 @@ static DeviceState *qdev_create_from_info(BusState *bus, DeviceInfo *info)
qdev_hot_added = true;
}
dev->instance_id_alias = -1;
QTAILQ_INIT(&dev->properties);
dev->state = DEV_STATE_CREATED;
return dev;
}
@ -395,12 +396,31 @@ void qdev_init_nofail(DeviceState *dev)
}
}
static void qdev_property_del_all(DeviceState *dev)
{
while (!QTAILQ_EMPTY(&dev->properties)) {
DeviceProperty *prop = QTAILQ_FIRST(&dev->properties);
QTAILQ_REMOVE(&dev->properties, prop, node);
if (prop->release) {
prop->release(dev, prop->name, prop->opaque);
}
g_free(prop->name);
g_free(prop->type);
g_free(prop);
}
}
/* Unlink device from bus and free the structure. */
void qdev_free(DeviceState *dev)
{
BusState *bus;
Property *prop;
qdev_property_del_all(dev);
if (dev->state == DEV_STATE_INITIALIZED) {
while (dev->num_child_bus) {
bus = QLIST_FIRST(&dev->child_bus);
@ -978,3 +998,80 @@ void qdev_unref(DeviceState *dev)
g_assert(dev->ref > 0);
dev->ref--;
}
void qdev_property_add(DeviceState *dev, const char *name, const char *type,
DevicePropertyAccessor *get, DevicePropertyAccessor *set,
DevicePropertyRelease *release,
void *opaque, Error **errp)
{
DeviceProperty *prop = g_malloc0(sizeof(*prop));
prop->name = g_strdup(name);
prop->type = g_strdup(type);
prop->get = get;
prop->set = set;
prop->release = release;
prop->opaque = opaque;
QTAILQ_INSERT_TAIL(&dev->properties, prop, node);
}
static DeviceProperty *qdev_property_find(DeviceState *dev, const char *name)
{
DeviceProperty *prop;
QTAILQ_FOREACH(prop, &dev->properties, node) {
if (strcmp(prop->name, name) == 0) {
return prop;
}
}
return NULL;
}
void qdev_property_get(DeviceState *dev, Visitor *v, const char *name,
Error **errp)
{
DeviceProperty *prop = qdev_property_find(dev, name);
if (prop == NULL) {
error_set(errp, QERR_PROPERTY_NOT_FOUND, dev->id?:"", name);
return;
}
if (!prop->get) {
error_set(errp, QERR_PERMISSION_DENIED);
} else {
prop->get(dev, v, prop->opaque, name, errp);
}
}
void qdev_property_set(DeviceState *dev, Visitor *v, const char *name,
Error **errp)
{
DeviceProperty *prop = qdev_property_find(dev, name);
if (prop == NULL) {
error_set(errp, QERR_PROPERTY_NOT_FOUND, dev->id?:"", name);
return;
}
if (!prop->set) {
error_set(errp, QERR_PERMISSION_DENIED);
} else {
prop->set(dev, prop->opaque, v, name, errp);
}
}
const char *qdev_property_get_type(DeviceState *dev, const char *name, Error **errp)
{
DeviceProperty *prop = qdev_property_find(dev, name);
if (prop == NULL) {
error_set(errp, QERR_PROPERTY_NOT_FOUND, dev->id?:"", name);
return NULL;
}
return prop->type;
}

120
hw/qdev.h
View File

@ -5,6 +5,7 @@
#include "qemu-queue.h"
#include "qemu-char.h"
#include "qemu-option.h"
#include "qapi/qapi-visit-core.h"
typedef struct Property Property;
@ -27,6 +28,44 @@ enum {
DEV_NVECTORS_UNSPECIFIED = -1,
};
/**
* @DevicePropertyAccessor - called when trying to get/set a property
*
* @dev the device that owns the property
* @v the visitor that contains the property data
* @opaque the device property opaque
* @name the name of the property
* @errp a pointer to an Error that is filled if getting/setting fails.
*/
typedef void (DevicePropertyAccessor)(DeviceState *dev,
Visitor *v,
void *opaque,
const char *name,
Error **errp);
/**
* @DevicePropertyRelease - called when a property is removed from a device
*
* @dev the device that owns the property
* @name the name of the property
* @opaque the opaque registered with the property
*/
typedef void (DevicePropertyRelease)(DeviceState *dev,
const char *name,
void *opaque);
typedef struct DeviceProperty
{
gchar *name;
gchar *type;
DevicePropertyAccessor *get;
DevicePropertyAccessor *set;
DevicePropertyRelease *release;
void *opaque;
QTAILQ_ENTRY(DeviceProperty) node;
} DeviceProperty;
/* This structure should not be accessed directly. We declare it here
so that it can be embedded in individual device state structures. */
struct DeviceState {
@ -51,6 +90,8 @@ struct DeviceState {
* more information.
*/
uint32_t ref;
QTAILQ_HEAD(, DeviceProperty) properties;
};
typedef void (*bus_dev_printfn)(Monitor *mon, DeviceState *dev, int indent);
@ -355,4 +396,83 @@ void qdev_ref(DeviceState *dev);
*/
void qdev_unref(DeviceState *dev);
/**
* @qdev_property_add - add a new property to a device
*
* @dev - the device to add a property to
*
* @name - the name of the property. This can contain any character except for
* a forward slash. In general, you should use hyphens '-' instead of
* underscores '_' when naming properties.
*
* @type - the type name of the property. This namespace is pretty loosely
* defined. Sub namespaces are constructed by using a prefix and then
* to angle brackets. For instance, the type 'virtio-net-pci' in the
* 'link' namespace would be 'link<virtio-net-pci>'.
*
* @get - the getter to be called to read a property. If this is NULL, then
* the property cannot be read.
*
* @set - the setter to be called to write a property. If this is NULL,
* then the property cannot be written.
*
* @release - called when the property is removed from the device. This is
* meant to allow a property to free its opaque upon device
* destruction. This may be NULL.
*
* @opaque - an opaque pointer to pass to the callbacks for the property
*
* @errp - returns an error if this function fails
*/
void qdev_property_add(DeviceState *dev, const char *name, const char *type,
DevicePropertyAccessor *get, DevicePropertyAccessor *set,
DevicePropertyRelease *release,
void *opaque, Error **errp);
/**
* @qdev_property_get - reads a property from a device
*
* @dev - the device
*
* @v - the visitor that will receive the property value. This should be an
* Output visitor and the data will be written with @name as the name.
*
* @name - the name of the property
*
* @errp - returns an error if this function fails
*/
void qdev_property_get(DeviceState *dev, Visitor *v, const char *name,
Error **errp);
/**
* @qdev_property_set - writes a property to a device
*
* @dev - the device
*
* @v - the visitor that will be used to write the property value. This should
* be an Input visitor and the data will be first read with @name as the
* name and then written as the property value.
*
* @name - the name of the property
*
* @errp - returns an error if this function fails
*/
void qdev_property_set(DeviceState *dev, Visitor *v, const char *name,
Error **errp);
/**
* @qdev_property_get_type - returns the type of a property
*
* @dev - the device
*
* @name - the name of the property
*
* @errp - returns an error if this function fails
*
* Returns:
* The type name of the property.
*/
const char *qdev_property_get_type(DeviceState *dev, const char *name,
Error **errp);
#endif

View File

@ -185,6 +185,10 @@ static const QErrorStringTable qerror_table[] = {
.error_fmt = QERR_OPEN_FILE_FAILED,
.desc = "Could not open '%(filename)'",
},
{
.error_fmt = QERR_PERMISSION_DENIED,
.desc = "Insufficient permission to perform this operation",
},
{
.error_fmt = QERR_PROPERTY_NOT_FOUND,
.desc = "Property '%(device).%(property)' not found",

View File

@ -156,6 +156,9 @@ QError *qobject_to_qerror(const QObject *obj);
#define QERR_OPEN_FILE_FAILED \
"{ 'class': 'OpenFileFailed', 'data': { 'filename': %s } }"
#define QERR_PERMISSION_DENIED \
"{ 'class': 'PermissionDenied', 'data': {} }"
#define QERR_PROPERTY_NOT_FOUND \
"{ 'class': 'PropertyNotFound', 'data': { 'device': %s, 'property': %s } }"