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;
|
qdev_hot_added = true;
|
||||||
}
|
}
|
||||||
dev->instance_id_alias = -1;
|
dev->instance_id_alias = -1;
|
||||||
|
QTAILQ_INIT(&dev->properties);
|
||||||
dev->state = DEV_STATE_CREATED;
|
dev->state = DEV_STATE_CREATED;
|
||||||
return dev;
|
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. */
|
/* Unlink device from bus and free the structure. */
|
||||||
void qdev_free(DeviceState *dev)
|
void qdev_free(DeviceState *dev)
|
||||||
{
|
{
|
||||||
BusState *bus;
|
BusState *bus;
|
||||||
Property *prop;
|
Property *prop;
|
||||||
|
|
||||||
|
qdev_property_del_all(dev);
|
||||||
|
|
||||||
if (dev->state == DEV_STATE_INITIALIZED) {
|
if (dev->state == DEV_STATE_INITIALIZED) {
|
||||||
while (dev->num_child_bus) {
|
while (dev->num_child_bus) {
|
||||||
bus = QLIST_FIRST(&dev->child_bus);
|
bus = QLIST_FIRST(&dev->child_bus);
|
||||||
@ -978,3 +998,80 @@ void qdev_unref(DeviceState *dev)
|
|||||||
g_assert(dev->ref > 0);
|
g_assert(dev->ref > 0);
|
||||||
dev->ref--;
|
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-queue.h"
|
||||||
#include "qemu-char.h"
|
#include "qemu-char.h"
|
||||||
#include "qemu-option.h"
|
#include "qemu-option.h"
|
||||||
|
#include "qapi/qapi-visit-core.h"
|
||||||
|
|
||||||
typedef struct Property Property;
|
typedef struct Property Property;
|
||||||
|
|
||||||
@ -27,6 +28,44 @@ enum {
|
|||||||
DEV_NVECTORS_UNSPECIFIED = -1,
|
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
|
/* This structure should not be accessed directly. We declare it here
|
||||||
so that it can be embedded in individual device state structures. */
|
so that it can be embedded in individual device state structures. */
|
||||||
struct DeviceState {
|
struct DeviceState {
|
||||||
@ -51,6 +90,8 @@ struct DeviceState {
|
|||||||
* more information.
|
* more information.
|
||||||
*/
|
*/
|
||||||
uint32_t ref;
|
uint32_t ref;
|
||||||
|
|
||||||
|
QTAILQ_HEAD(, DeviceProperty) properties;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef void (*bus_dev_printfn)(Monitor *mon, DeviceState *dev, int indent);
|
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);
|
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
|
#endif
|
||||||
|
4
qerror.c
4
qerror.c
@ -185,6 +185,10 @@ static const QErrorStringTable qerror_table[] = {
|
|||||||
.error_fmt = QERR_OPEN_FILE_FAILED,
|
.error_fmt = QERR_OPEN_FILE_FAILED,
|
||||||
.desc = "Could not open '%(filename)'",
|
.desc = "Could not open '%(filename)'",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.error_fmt = QERR_PERMISSION_DENIED,
|
||||||
|
.desc = "Insufficient permission to perform this operation",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
.error_fmt = QERR_PROPERTY_NOT_FOUND,
|
.error_fmt = QERR_PROPERTY_NOT_FOUND,
|
||||||
.desc = "Property '%(device).%(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 \
|
#define QERR_OPEN_FILE_FAILED \
|
||||||
"{ 'class': 'OpenFileFailed', 'data': { 'filename': %s } }"
|
"{ 'class': 'OpenFileFailed', 'data': { 'filename': %s } }"
|
||||||
|
|
||||||
|
#define QERR_PERMISSION_DENIED \
|
||||||
|
"{ 'class': 'PermissionDenied', 'data': {} }"
|
||||||
|
|
||||||
#define QERR_PROPERTY_NOT_FOUND \
|
#define QERR_PROPERTY_NOT_FOUND \
|
||||||
"{ 'class': 'PropertyNotFound', 'data': { 'device': %s, 'property': %s } }"
|
"{ 'class': 'PropertyNotFound', 'data': { 'device': %s, 'property': %s } }"
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user