diff mbox

[RFC,v2,06/20] qdev: Add 'accepted-device-types' property to BusClass

Message ID 1480111556-32586-7-git-send-email-ehabkost@redhat.com (mailing list archive)
State New, archived
Headers show

Commit Message

Eduardo Habkost Nov. 25, 2016, 10:05 p.m. UTC
Each bus class will now be aware of the specific device types
that can be plugged on it. That will be useful for:

* Runtime check for which devices types can be plugged to the
  machine;
* Validation of query-machines output by automated tests.

By default, a single type name is used on all bus instances (set
at BusClass::device_type), but bus instances can override it and
return a different type list.

Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
---
Changes v1 -> v2:
* v1 patch subject was
  "qdev: Add device_type field to BusClass"
* Replace 'device-type' with 'accepted-device-types'
  property
---
 hw/audio/intel-hda.c          |  7 +++++++
 hw/block/fdc.c                | 15 +++++++++++----
 hw/char/virtio-serial-bus.c   |  1 +
 hw/core/bus.c                 | 21 +++++++++++++++++++++
 hw/core/sysbus.c              |  1 +
 hw/i2c/core.c                 |  7 +++++++
 hw/ide/qdev.c                 |  1 +
 hw/input/adb.c                |  7 +++++++
 hw/ipack/ipack.c              |  7 +++++++
 hw/isa/isa-bus.c              |  1 +
 hw/misc/auxbus.c              |  1 +
 hw/pci/pci.c                  |  1 +
 hw/ppc/spapr_vio.c            |  1 +
 hw/s390x/css-bridge.c         |  2 ++
 hw/s390x/event-facility.c     |  1 +
 hw/s390x/s390-pci-bus.c       |  7 +++++++
 hw/scsi/scsi-bus.c            |  1 +
 hw/sd/core.c                  |  7 +++++++
 hw/ssi/ssi.c                  |  7 +++++++
 hw/usb/bus.c                  |  1 +
 hw/usb/dev-smartcard-reader.c |  7 +++++++
 hw/virtio/virtio-bus.c        |  1 +
 include/hw/qdev-core.h        |  8 ++++++++
 23 files changed, 109 insertions(+), 4 deletions(-)

Comments

Cornelia Huck Nov. 29, 2016, 1:57 p.m. UTC | #1
On Fri, 25 Nov 2016 20:05:42 -0200
Eduardo Habkost <ehabkost@redhat.com> wrote:

> Each bus class will now be aware of the specific device types
> that can be plugged on it. That will be useful for:
> 
> * Runtime check for which devices types can be plugged to the
>   machine;
> * Validation of query-machines output by automated tests.
> 
> By default, a single type name is used on all bus instances (set
> at BusClass::device_type), but bus instances can override it and
> return a different type list.
> 
> Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>

I've read (well, more skimmed through) the whole patchset and I think
we have different cases of "multiple device types for one bus":

- the q35 root bus case later in this patchset, where just a certain
instance of the bus can accept multiple types
- the case where every instance of a bus may accept multiple types
(none currently; but this will be the case for e.g. virtual-css once we
have other derivates of CCW_DEVICE than virtio-ccw)

For the second case, a static initializer for multiple types in the
class makes sense; but the first case is a bit hackish. Should there be
a generic way to pass a list of types to the individual bus instance's
initializers (that could fall back to a static list in the class)?
Eduardo Habkost Nov. 29, 2016, 2:18 p.m. UTC | #2
On Tue, Nov 29, 2016 at 02:57:07PM +0100, Cornelia Huck wrote:
> On Fri, 25 Nov 2016 20:05:42 -0200
> Eduardo Habkost <ehabkost@redhat.com> wrote:
> 
> > Each bus class will now be aware of the specific device types
> > that can be plugged on it. That will be useful for:
> > 
> > * Runtime check for which devices types can be plugged to the
> >   machine;
> > * Validation of query-machines output by automated tests.
> > 
> > By default, a single type name is used on all bus instances (set
> > at BusClass::device_type), but bus instances can override it and
> > return a different type list.
> > 
> > Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
> 
> I've read (well, more skimmed through) the whole patchset and I think
> we have different cases of "multiple device types for one bus":
> 
> - the q35 root bus case later in this patchset, where just a certain
> instance of the bus can accept multiple types
> - the case where every instance of a bus may accept multiple types
> (none currently; but this will be the case for e.g. virtual-css once we
> have other derivates of CCW_DEVICE than virtio-ccw)

Are all going to be subclasses of TYPE_CCW_DEVICE, and are all
TYPE_CCW_DEVICE subclasses going to be accepted by virtual-css?
In this case, you could just set BusClass::device_type to
TYPE_CCW_DEVICE on virtual-css-bus.

> 
> For the second case, a static initializer for multiple types in the
> class makes sense;

If necessary, we can change BusClass::device_type to a
BusClass::device_types list. I didn't do that because it didn't
seem necessary (and I would like to encourage buses to encode
compatiblity using a single type/interface name whenever
possible).


>                    but the first case is a bit hackish. Should there be
> a generic way to pass a list of types to the individual bus instance's
> initializers (that could fall back to a static list in the class)?

Sound good, but I don't know how that generic mechanism could
look like. Would a:
  void bus_add_accepted_device_type(BusState *bus, const char *devtype)
function be enough?
Cornelia Huck Dec. 2, 2016, 12:03 p.m. UTC | #3
On Tue, 29 Nov 2016 12:18:19 -0200
Eduardo Habkost <ehabkost@redhat.com> wrote:

> On Tue, Nov 29, 2016 at 02:57:07PM +0100, Cornelia Huck wrote:
> > On Fri, 25 Nov 2016 20:05:42 -0200
> > Eduardo Habkost <ehabkost@redhat.com> wrote:
> > 
> > > Each bus class will now be aware of the specific device types
> > > that can be plugged on it. That will be useful for:
> > > 
> > > * Runtime check for which devices types can be plugged to the
> > >   machine;
> > > * Validation of query-machines output by automated tests.
> > > 
> > > By default, a single type name is used on all bus instances (set
> > > at BusClass::device_type), but bus instances can override it and
> > > return a different type list.
> > > 
> > > Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
> > 
> > I've read (well, more skimmed through) the whole patchset and I think
> > we have different cases of "multiple device types for one bus":
> > 
> > - the q35 root bus case later in this patchset, where just a certain
> > instance of the bus can accept multiple types
> > - the case where every instance of a bus may accept multiple types
> > (none currently; but this will be the case for e.g. virtual-css once we
> > have other derivates of CCW_DEVICE than virtio-ccw)
> 
> Are all going to be subclasses of TYPE_CCW_DEVICE, and are all
> TYPE_CCW_DEVICE subclasses going to be accepted by virtual-css?

That's what we plan to do, yes.

> In this case, you could just set BusClass::device_type to
> TYPE_CCW_DEVICE on virtual-css-bus.

Sounds reasonable.

> 
> > 
> > For the second case, a static initializer for multiple types in the
> > class makes sense;
> 
> If necessary, we can change BusClass::device_type to a
> BusClass::device_types list. I didn't do that because it didn't
> seem necessary (and I would like to encourage buses to encode
> compatiblity using a single type/interface name whenever
> possible).

Most busses will probably allow a single (parent) device type anyway,
so that's fine, I guess.

> 
> 
> >                    but the first case is a bit hackish. Should there be
> > a generic way to pass a list of types to the individual bus instance's
> > initializers (that could fall back to a static list in the class)?
> 
> Sound good, but I don't know how that generic mechanism could
> look like. Would a:
>   void bus_add_accepted_device_type(BusState *bus, const char *devtype)
> function be enough?

Would we need a remove helper as well (i.e. "this bus normally supports
types a and b, but this instance accepts only a")? It is probably
enough to add it should the need arise some day.
diff mbox

Patch

diff --git a/hw/audio/intel-hda.c b/hw/audio/intel-hda.c
index 537face..a363993 100644
--- a/hw/audio/intel-hda.c
+++ b/hw/audio/intel-hda.c
@@ -36,9 +36,16 @@  static Property hda_props[] = {
     DEFINE_PROP_END_OF_LIST()
 };
 
+static void hda_codec_bus_class_init(ObjectClass *oc, void *opaque)
+{
+    BusClass *bc = BUS_CLASS(oc);
+    bc->device_type = TYPE_HDA_CODEC_DEVICE;
+}
+
 static const TypeInfo hda_codec_bus_info = {
     .name = TYPE_HDA_BUS,
     .parent = TYPE_BUS,
+    .class_init = hda_codec_bus_class_init,
     .instance_size = sizeof(HDACodecBus),
 };
 
diff --git a/hw/block/fdc.c b/hw/block/fdc.c
index 17d29e7..09bfdda 100644
--- a/hw/block/fdc.c
+++ b/hw/block/fdc.c
@@ -60,6 +60,10 @@ 
 #define TYPE_FLOPPY_BUS "floppy-bus"
 #define FLOPPY_BUS(obj) OBJECT_CHECK(FloppyBus, (obj), TYPE_FLOPPY_BUS)
 
+#define TYPE_FLOPPY_DRIVE "floppy"
+#define FLOPPY_DRIVE(obj) \
+     OBJECT_CHECK(FloppyDrive, (obj), TYPE_FLOPPY_DRIVE)
+
 typedef struct FDCtrl FDCtrl;
 typedef struct FDrive FDrive;
 static FDrive *get_drv(FDCtrl *fdctrl, int unit);
@@ -69,9 +73,16 @@  typedef struct FloppyBus {
     FDCtrl *fdc;
 } FloppyBus;
 
+static void floppy_bus_class_init(ObjectClass *oc, void *opaque)
+{
+    BusClass *bc = BUS_CLASS(oc);
+    bc->device_type = TYPE_FLOPPY_DRIVE;
+}
+
 static const TypeInfo floppy_bus_info = {
     .name = TYPE_FLOPPY_BUS,
     .parent = TYPE_BUS,
+    .class_init = floppy_bus_class_init,
     .instance_size = sizeof(FloppyBus),
 };
 
@@ -483,10 +494,6 @@  static const BlockDevOps fd_block_ops = {
 };
 
 
-#define TYPE_FLOPPY_DRIVE "floppy"
-#define FLOPPY_DRIVE(obj) \
-     OBJECT_CHECK(FloppyDrive, (obj), TYPE_FLOPPY_DRIVE)
-
 typedef struct FloppyDrive {
     DeviceState     qdev;
     uint32_t        unit;
diff --git a/hw/char/virtio-serial-bus.c b/hw/char/virtio-serial-bus.c
index 7975c2c..215e180 100644
--- a/hw/char/virtio-serial-bus.c
+++ b/hw/char/virtio-serial-bus.c
@@ -832,6 +832,7 @@  static void virtser_bus_class_init(ObjectClass *klass, void *data)
 {
     BusClass *k = BUS_CLASS(klass);
     k->print_dev = virtser_bus_dev_print;
+    k->device_type = TYPE_VIRTIO_SERIAL_PORT;
 }
 
 static const TypeInfo virtser_bus_info = {
diff --git a/hw/core/bus.c b/hw/core/bus.c
index cf383fc..d2bf717 100644
--- a/hw/core/bus.c
+++ b/hw/core/bus.c
@@ -21,6 +21,7 @@ 
 #include "qemu-common.h"
 #include "hw/qdev.h"
 #include "qapi/error.h"
+#include "qapi-visit.h"
 
 static void qbus_set_hotplug_handler_internal(BusState *bus, Object *handler,
                                               Error **errp)
@@ -191,8 +192,15 @@  static void bus_set_realized(Object *obj, bool value, Error **errp)
 static void qbus_initfn(Object *obj)
 {
     BusState *bus = BUS(obj);
+    BusClass *bc = BUS_GET_CLASS(bus);
 
     QTAILQ_INIT(&bus->children);
+
+    if (bc->device_type) {
+        bus->accepted_device_types = g_new0(strList, 1);
+        bus->accepted_device_types->value = g_strdup(bc->device_type);
+    }
+
     object_property_add_link(obj, QDEV_HOTPLUG_HANDLER_PROPERTY,
                              TYPE_HOTPLUG_HANDLER,
                              (Object **)&bus->hotplug_handler,
@@ -208,12 +216,25 @@  static char *default_bus_get_fw_dev_path(DeviceState *dev)
     return g_strdup(object_get_typename(OBJECT(dev)));
 }
 
+static void bus_get_device_type(Object *obj, Visitor *v,
+                                 const char *name, void *opaque,
+                                 Error **errp)
+{
+    BusState *bus = BUS(obj);
+
+    visit_type_strList(v, NULL, &bus->accepted_device_types, errp);
+}
+
 static void bus_class_init(ObjectClass *class, void *data)
 {
     BusClass *bc = BUS_CLASS(class);
 
     class->unparent = bus_unparent;
     bc->get_fw_dev_path = default_bus_get_fw_dev_path;
+
+    object_class_property_add(class, "accepted-device-types", "strList",
+                              bus_get_device_type, NULL, NULL, NULL,
+                              &error_abort);
 }
 
 static void qbus_finalize(Object *obj)
diff --git a/hw/core/sysbus.c b/hw/core/sysbus.c
index c0f560b..c065eb8 100644
--- a/hw/core/sysbus.c
+++ b/hw/core/sysbus.c
@@ -76,6 +76,7 @@  static void system_bus_class_init(ObjectClass *klass, void *data)
 
     k->print_dev = sysbus_dev_print;
     k->get_fw_dev_path = sysbus_get_fw_dev_path;
+    k->device_type = TYPE_SYS_BUS_DEVICE;
 }
 
 static const TypeInfo system_bus_info = {
diff --git a/hw/i2c/core.c b/hw/i2c/core.c
index abd4c4c..81b6ae4 100644
--- a/hw/i2c/core.c
+++ b/hw/i2c/core.c
@@ -35,9 +35,16 @@  static Property i2c_props[] = {
 #define TYPE_I2C_BUS "i2c-bus"
 #define I2C_BUS(obj) OBJECT_CHECK(I2CBus, (obj), TYPE_I2C_BUS)
 
+static void i2c_bus_class_init(ObjectClass *oc, void *opaque)
+{
+    BusClass *bc = BUS_CLASS(oc);
+    bc->device_type = TYPE_I2C_SLAVE;
+}
+
 static const TypeInfo i2c_bus_info = {
     .name = TYPE_I2C_BUS,
     .parent = TYPE_BUS,
+    .class_init = i2c_bus_class_init,
     .instance_size = sizeof(I2CBus),
 };
 
diff --git a/hw/ide/qdev.c b/hw/ide/qdev.c
index dbaa75c..b526f85 100644
--- a/hw/ide/qdev.c
+++ b/hw/ide/qdev.c
@@ -43,6 +43,7 @@  static void ide_bus_class_init(ObjectClass *klass, void *data)
     BusClass *k = BUS_CLASS(klass);
 
     k->get_fw_dev_path = idebus_get_fw_dev_path;
+    k->device_type = TYPE_IDE_DEVICE;
 }
 
 static void idebus_unrealize(DeviceState *qdev, Error **errp)
diff --git a/hw/input/adb.c b/hw/input/adb.c
index 43d3205..c6a2b31 100644
--- a/hw/input/adb.c
+++ b/hw/input/adb.c
@@ -121,9 +121,16 @@  int adb_poll(ADBBusState *s, uint8_t *obuf, uint16_t poll_mask)
     return olen;
 }
 
+static void adb_bus_class_init(ObjectClass *oc, void *opaque)
+{
+    BusClass *bc = BUS_CLASS(oc);
+    bc->device_type = TYPE_ADB_DEVICE;
+}
+
 static const TypeInfo adb_bus_type_info = {
     .name = TYPE_ADB_BUS,
     .parent = TYPE_BUS,
+    .class_init = adb_bus_class_init,
     .instance_size = sizeof(ADBBusState),
 };
 
diff --git a/hw/ipack/ipack.c b/hw/ipack/ipack.c
index 6021e6d..f22c504 100644
--- a/hw/ipack/ipack.c
+++ b/hw/ipack/ipack.c
@@ -106,9 +106,16 @@  static const TypeInfo ipack_device_info = {
     .abstract      = true,
 };
 
+static void ipack_bus_class_init(ObjectClass *oc, void *opaque)
+{
+    BusClass *bc = BUS_CLASS(oc);
+    bc->device_type = TYPE_IPACK_DEVICE;
+}
+
 static const TypeInfo ipack_bus_info = {
     .name = TYPE_IPACK_BUS,
     .parent = TYPE_BUS,
+    .class_init = ipack_bus_class_init,
     .instance_size = sizeof(IPackBus),
 };
 
diff --git a/hw/isa/isa-bus.c b/hw/isa/isa-bus.c
index 9d07b11..aad7669 100644
--- a/hw/isa/isa-bus.c
+++ b/hw/isa/isa-bus.c
@@ -36,6 +36,7 @@  static void isa_bus_class_init(ObjectClass *klass, void *data)
 
     k->print_dev = isabus_dev_print;
     k->get_fw_dev_path = isabus_get_fw_dev_path;
+    k->device_type = TYPE_ISA_DEVICE;
 }
 
 static const TypeInfo isa_dma_info = {
diff --git a/hw/misc/auxbus.c b/hw/misc/auxbus.c
index e4a7ba4..4b0d565 100644
--- a/hw/misc/auxbus.c
+++ b/hw/misc/auxbus.c
@@ -57,6 +57,7 @@  static void aux_bus_class_init(ObjectClass *klass, void *data)
      * in monitor.
      */
     k->print_dev = aux_slave_dev_print;
+    k->device_type = TYPE_AUX_SLAVE;
 }
 
 AUXBus *aux_init_bus(DeviceState *parent, const char *name)
diff --git a/hw/pci/pci.c b/hw/pci/pci.c
index 24fae16..74b8fef 100644
--- a/hw/pci/pci.c
+++ b/hw/pci/pci.c
@@ -152,6 +152,7 @@  static void pci_bus_class_init(ObjectClass *klass, void *data)
     k->realize = pci_bus_realize;
     k->unrealize = pci_bus_unrealize;
     k->reset = pcibus_reset;
+    k->device_type = TYPE_PCI_DEVICE;
 
     pbc->is_root = pcibus_is_root;
     pbc->bus_num = pcibus_num;
diff --git a/hw/ppc/spapr_vio.c b/hw/ppc/spapr_vio.c
index cc1e09c..c70b9b9 100644
--- a/hw/ppc/spapr_vio.c
+++ b/hw/ppc/spapr_vio.c
@@ -61,6 +61,7 @@  static void spapr_vio_bus_class_init(ObjectClass *klass, void *data)
 
     k->get_dev_path = spapr_vio_get_dev_name;
     k->get_fw_dev_path = spapr_vio_get_dev_name;
+    k->device_type = TYPE_VIO_SPAPR_DEVICE;
 }
 
 static const TypeInfo spapr_vio_bus_info = {
diff --git a/hw/s390x/css-bridge.c b/hw/s390x/css-bridge.c
index 9a7f7ee..8a6c1ae 100644
--- a/hw/s390x/css-bridge.c
+++ b/hw/s390x/css-bridge.c
@@ -17,6 +17,7 @@ 
 #include "hw/s390x/css.h"
 #include "ccw-device.h"
 #include "hw/s390x/css-bridge.h"
+#include "hw/s390x/virtio-ccw.h"
 
 /*
  * Invoke device-specific unplug handler, disable the subchannel
@@ -81,6 +82,7 @@  static void virtual_css_bus_class_init(ObjectClass *klass, void *data)
 
     k->reset = virtual_css_bus_reset;
     k->get_dev_path = virtual_css_bus_get_dev_path;
+    k->device_type = TYPE_VIRTIO_CCW_DEVICE;
 }
 
 static const TypeInfo virtual_css_bus_info = {
diff --git a/hw/s390x/event-facility.c b/hw/s390x/event-facility.c
index 34b2faf..f2562ce 100644
--- a/hw/s390x/event-facility.c
+++ b/hw/s390x/event-facility.c
@@ -307,6 +307,7 @@  static void sclp_events_bus_class_init(ObjectClass *klass, void *data)
     BusClass *bc = BUS_CLASS(klass);
 
     bc->realize = sclp_events_bus_realize;
+    bc->device_type = TYPE_SCLP_EVENT;
 }
 
 static const TypeInfo sclp_events_bus_info = {
diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c
index 63f6248..7470fdd 100644
--- a/hw/s390x/s390-pci-bus.c
+++ b/hw/s390x/s390-pci-bus.c
@@ -783,10 +783,17 @@  static const TypeInfo s390_pcihost_info = {
     }
 };
 
+static void s390_pcibus_class_init(ObjectClass *oc, void *opaque)
+{
+    BusClass *bc = BUS_CLASS(oc);
+    bc->device_type = TYPE_S390_PCI_DEVICE;
+}
+
 static const TypeInfo s390_pcibus_info = {
     .name = TYPE_S390_PCI_BUS,
     .parent = TYPE_BUS,
     .instance_size = sizeof(S390PCIBus),
+    .class_init = s390_pcibus_class_init,
 };
 
 static uint16_t s390_pci_generate_uid(void)
diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c
index 297216d..9faf45f 100644
--- a/hw/scsi/scsi-bus.c
+++ b/hw/scsi/scsi-bus.c
@@ -31,6 +31,7 @@  static void scsi_bus_class_init(ObjectClass *klass, void *data)
 
     k->get_dev_path = scsibus_get_dev_path;
     k->get_fw_dev_path = scsibus_get_fw_dev_path;
+    k->device_type = TYPE_SCSI_DEVICE;
     hc->unplug = qdev_simple_device_unplug_cb;
 }
 
diff --git a/hw/sd/core.c b/hw/sd/core.c
index 14c2bdf..49b9f59 100644
--- a/hw/sd/core.c
+++ b/hw/sd/core.c
@@ -131,11 +131,18 @@  void sdbus_set_readonly(SDBus *sdbus, bool readonly)
     }
 }
 
+static void sd_bus_class_init(ObjectClass *oc, void *opaque)
+{
+    BusClass *bc = BUS_CLASS(oc);
+    bc->device_type = TYPE_SD_CARD;
+}
+
 static const TypeInfo sd_bus_info = {
     .name = TYPE_SD_BUS,
     .parent = TYPE_BUS,
     .instance_size = sizeof(SDBus),
     .class_size = sizeof(SDBusClass),
+    .class_init = sd_bus_class_init,
 };
 
 static void sd_bus_register_types(void)
diff --git a/hw/ssi/ssi.c b/hw/ssi/ssi.c
index 7eaaf56..c48920d 100644
--- a/hw/ssi/ssi.c
+++ b/hw/ssi/ssi.c
@@ -22,10 +22,17 @@  struct SSIBus {
 #define TYPE_SSI_BUS "SSI"
 #define SSI_BUS(obj) OBJECT_CHECK(SSIBus, (obj), TYPE_SSI_BUS)
 
+static void ssi_bus_class_init(ObjectClass *oc, void *opaque)
+{
+    BusClass *bc = BUS_CLASS(oc);
+    bc->device_type = TYPE_SSI_SLAVE;
+}
+
 static const TypeInfo ssi_bus_info = {
     .name = TYPE_SSI_BUS,
     .parent = TYPE_BUS,
     .instance_size = sizeof(SSIBus),
+    .class_init = ssi_bus_class_init,
 };
 
 static void ssi_cs_default(void *opaque, int n, int level)
diff --git a/hw/usb/bus.c b/hw/usb/bus.c
index 25913ad..2cb7fa4 100644
--- a/hw/usb/bus.c
+++ b/hw/usb/bus.c
@@ -33,6 +33,7 @@  static void usb_bus_class_init(ObjectClass *klass, void *data)
     k->print_dev = usb_bus_dev_print;
     k->get_dev_path = usb_get_dev_path;
     k->get_fw_dev_path = usb_get_fw_dev_path;
+    k->device_type = TYPE_USB_DEVICE;
     hc->unplug = qdev_simple_device_unplug_cb;
 }
 
diff --git a/hw/usb/dev-smartcard-reader.c b/hw/usb/dev-smartcard-reader.c
index 89e11b6..2721f0c 100644
--- a/hw/usb/dev-smartcard-reader.c
+++ b/hw/usb/dev-smartcard-reader.c
@@ -1180,10 +1180,17 @@  static Property ccid_props[] = {
 #define TYPE_CCID_BUS "ccid-bus"
 #define CCID_BUS(obj) OBJECT_CHECK(CCIDBus, (obj), TYPE_CCID_BUS)
 
+static void ccid_bus_class_init(ObjectClass *oc, void *opaque)
+{
+    BusClass *bc = BUS_CLASS(oc);
+    bc->device_type = TYPE_CCID_CARD;
+}
+
 static const TypeInfo ccid_bus_info = {
     .name = TYPE_CCID_BUS,
     .parent = TYPE_BUS,
     .instance_size = sizeof(CCIDBus),
+    .class_init = ccid_bus_class_init,
 };
 
 void ccid_card_send_apdu_to_guest(CCIDCardState *card,
diff --git a/hw/virtio/virtio-bus.c b/hw/virtio/virtio-bus.c
index d6c0c72..815f3dd 100644
--- a/hw/virtio/virtio-bus.c
+++ b/hw/virtio/virtio-bus.c
@@ -293,6 +293,7 @@  static void virtio_bus_class_init(ObjectClass *klass, void *data)
     BusClass *bus_class = BUS_CLASS(klass);
     bus_class->get_dev_path = virtio_bus_get_dev_path;
     bus_class->get_fw_dev_path = virtio_bus_get_fw_dev_path;
+    bus_class->device_type = TYPE_VIRTIO_DEVICE;
 }
 
 static const TypeInfo virtio_bus_info = {
diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h
index 2c97347..a7f9ac4 100644
--- a/include/hw/qdev-core.h
+++ b/include/hw/qdev-core.h
@@ -207,6 +207,13 @@  struct BusClass {
     int max_dev;
     /* number of automatically allocated bus ids (e.g. ide.0) */
     int automatic_ids;
+
+    /* default device type for 'accepted-device-type' property
+     *
+     * Most buses return only BusClass::device_type on accepted-device-types,
+     * but on some cases bus instances may override it.
+     */
+    const char *device_type;
 };
 
 typedef struct BusChild {
@@ -228,6 +235,7 @@  struct BusState {
     HotplugHandler *hotplug_handler;
     int max_index;
     bool realized;
+    strList *accepted_device_types;
     QTAILQ_HEAD(ChildrenHead, BusChild) children;
     QLIST_ENTRY(BusState) sibling;
 };