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>
This commit is contained in:
parent
85ed303bfe
commit
44677ded43
97
hw/qdev.c
97
hw/qdev.c
@ -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
120
hw/qdev.h
@ -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
|
||||
|
4
qerror.c
4
qerror.c
@ -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",
|
||||
|
3
qerror.h
3
qerror.h
@ -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 } }"
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user