Patchwork [17/21] qom: add CharDriver class

login
register
mail settings
Submitter Anthony Liguori
Date July 25, 2011, 1:44 a.m.
Message ID <1311558293-5855-18-git-send-email-aliguori@us.ibm.com>
Download mbox | patch
Permalink /patch/106584/
State New
Headers show

Comments

Anthony Liguori - July 25, 2011, 1:44 a.m.
The CharDriver type replaces the CharDriverState in QEMU today.  Here's how
everything matches up:

 1) qemu_chr_open() no longer exists.  This function used to act as a factory
    and used parsing of the filename to determine which type to create.  A newer
    version using QemuOpts was introduced, qemu_chr_open_opts() which eliminates
    the filename in favor of typed key value pairs.

    Now, plug_create() can be used to create CharDrivers using an explicit
    type that subclasses CharDriver.

 2) query-chardev is deprecated.  This is replaced by
    plug_list(type=char-driver) and plug_list_props().

 3) We can now dynamically add and remove new CharDrivers

Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
---
 Makefile.qom          |    3 +
 Qconfig               |    1 +
 chrdrv/Makefile       |    1 +
 chrdrv/Qconfig        |    5 +
 chrdrv/chrdrv.c       |  229 ++++++++++++++++++++++++++++++++++++++++
 configure             |    2 +-
 include/qemu/chrdrv.h |  281 +++++++++++++++++++++++++++++++++++++++++++++++++
 7 files changed, 521 insertions(+), 1 deletions(-)
 create mode 100644 chrdrv/Makefile
 create mode 100644 chrdrv/Qconfig
 create mode 100644 chrdrv/chrdrv.c
 create mode 100644 include/qemu/chrdrv.h

Patch

diff --git a/Makefile.qom b/Makefile.qom
index c694cbb..02a5ca5 100644
--- a/Makefile.qom
+++ b/Makefile.qom
@@ -16,3 +16,6 @@  common-obj-y += $(addprefix qom/,$(qom-obj-y))
 include $(SRC_PATH)/devices/Makefile
 common-obj-y += $(addprefix devices/,$(devices-obj-y))
 
+include $(SRC_PATH)/chrdrv/Makefile
+common-obj-y += $(addprefix chrdrv/,$(chrdrv-obj-y))
+
diff --git a/Qconfig b/Qconfig
index 03f2a87..57c1c7d 100644
--- a/Qconfig
+++ b/Qconfig
@@ -1,3 +1,4 @@ 
 source qapi/Qconfig
 source qom/Qconfig
 source devices/Qconfig
+source chrdrv/Qconfig
diff --git a/chrdrv/Makefile b/chrdrv/Makefile
new file mode 100644
index 0000000..43a51e7
--- /dev/null
+++ b/chrdrv/Makefile
@@ -0,0 +1 @@ 
+chrdrv-obj-$(CONFIG_CHRDRV) := chrdrv.o
diff --git a/chrdrv/Qconfig b/chrdrv/Qconfig
new file mode 100644
index 0000000..845c205
--- /dev/null
+++ b/chrdrv/Qconfig
@@ -0,0 +1,5 @@ 
+config CHRDRV
+       bool "QEMU Character Drivers"
+       default y
+       help
+         Character layer
diff --git a/chrdrv/chrdrv.c b/chrdrv/chrdrv.c
new file mode 100644
index 0000000..a9b8dc2
--- /dev/null
+++ b/chrdrv/chrdrv.c
@@ -0,0 +1,229 @@ 
+#include "qemu/chrdrv.h"
+
+int char_driver_write(CharDriver *s, const uint8_t *buf, int len)
+{
+    CharDriverClass *cdc = CHAR_DRIVER_GET_CLASS(s);
+
+    return cdc->write(s, buf, len);
+}
+
+int char_driver_ioctl(CharDriver *s, int cmd, void *arg)
+{
+    CharDriverClass *cdc = CHAR_DRIVER_GET_CLASS(s);
+
+    return cdc->ioctl(s, cmd, arg);
+}
+
+int char_driver_get_msgfd(CharDriver *s)
+{
+    CharDriverClass *cdc = CHAR_DRIVER_GET_CLASS(s);
+
+    return cdc->get_msgfd(s);
+}
+
+void char_driver_send_event(CharDriver *s, int event)
+{
+    CharDriverClass *cdc = CHAR_DRIVER_GET_CLASS(s);
+
+    cdc->send_event(s, event);
+}
+
+void char_driver_accept_input(CharDriver *s)
+{
+    CharDriverClass *cdc = CHAR_DRIVER_GET_CLASS(s);
+
+    cdc->accept_input(s);
+}
+
+void char_driver_set_echo(CharDriver *s, bool echo)
+{
+    CharDriverClass *cdc = CHAR_DRIVER_GET_CLASS(s);
+
+    cdc->set_echo(s, echo);
+}
+
+void char_driver_guest_open(CharDriver *s)
+{
+    CharDriverClass *cdc = CHAR_DRIVER_GET_CLASS(s);
+
+    cdc->guest_open(s);
+}
+
+void char_driver_guest_close(CharDriver *s)
+{
+    CharDriverClass *cdc = CHAR_DRIVER_GET_CLASS(s);
+
+    cdc->guest_close(s);
+}
+
+static int char_driver_def_write(CharDriver *s, const uint8_t *buf, int len)
+{
+    return -ENOTSUP;
+}
+
+static void char_driver_def_update_read_handler(CharDriver *s)
+{
+}
+
+static int char_driver_def_ioctl(CharDriver *s, int cmd, void *arg)
+{
+    return -ENOTSUP;
+}
+
+static int char_driver_def_get_msgfd(CharDriver *s)
+{
+    return -1;
+}
+
+static void char_driver_def_send_event(CharDriver *chr, int event)
+{
+}
+
+static void char_driver_def_close(CharDriver *chr)
+{
+    char_driver_send_event(chr, CHR_EVENT_CLOSED);
+}
+
+static void char_driver_def_accept_input(CharDriver *chr)
+{
+}
+
+static void char_driver_def_set_echo(CharDriver *chr, bool echo)
+{
+}
+
+static void char_driver_def_guest_open(CharDriver *chr)
+{
+}
+
+static void char_driver_def_guest_close(CharDriver *chr)
+{
+}
+
+static void char_driver_def_open(CharDriver *chr, Error **errp)
+{
+}
+
+static void char_driver_realize(Plug *plug)
+{
+    CharDriver *chr = CHAR_DRIVER(plug);
+    CharDriverClass *cdc = CHAR_DRIVER_GET_CLASS(chr);
+
+    cdc->open(chr, NULL);
+}
+
+int char_driver_can_read(CharDriver *chr)
+{
+    if (!chr->chr_can_read) {
+        return 1024;
+    }
+
+    return chr->chr_can_read(chr->handler_opaque);
+}
+
+void char_driver_read(CharDriver *chr, uint8_t *buf, int len)
+{
+    if (!chr->chr_read) {
+        return;
+    }
+
+    chr->chr_read(chr->handler_opaque, buf, len);
+}
+
+void char_driver_event(CharDriver *chr, int event)
+{
+    /* Keep track if the char device is open */
+    switch (event) {
+    case CHR_EVENT_OPENED:
+        chr->opened = 1;
+        break;
+    case CHR_EVENT_CLOSED:
+        chr->opened = 0;
+        break;
+    }
+
+    if (!chr->chr_event) {
+        return;
+    }
+
+    chr->chr_event(chr->handler_opaque, event);
+}
+
+static void char_driver_class_init(TypeClass *class)
+{
+    PlugClass *pc = PLUG_CLASS(class);
+    CharDriverClass *cdc = CHAR_DRIVER_CLASS(class);
+
+    pc->realize = char_driver_realize;
+    cdc->write = char_driver_def_write;
+    cdc->ioctl = char_driver_def_ioctl;
+    cdc->get_msgfd = char_driver_def_get_msgfd;
+    cdc->send_event = char_driver_def_send_event;
+    cdc->close = char_driver_def_close;
+    cdc->accept_input = char_driver_def_accept_input;
+    cdc->set_echo = char_driver_def_set_echo;
+    cdc->guest_open = char_driver_def_guest_open;
+    cdc->guest_close = char_driver_def_guest_close;
+    cdc->open = char_driver_def_open;
+    cdc->update_read_handler = char_driver_def_update_read_handler;
+
+}
+
+static void char_driver_generic_open(CharDriver *s)
+{
+    char_driver_event(s, CHR_EVENT_OPENED);
+}
+
+void char_driver_add_handlers(CharDriver *s,
+                              IOCanReadHandler *fd_can_read,
+                              IOReadHandler *fd_read,
+                              IOEventHandler *fd_event,
+                              void *opaque)
+{
+    CharDriverClass *cdc = CHAR_DRIVER_GET_CLASS(s);
+
+    if (!opaque && !fd_can_read && !fd_read && !fd_event) {
+        /* chr driver being released. */
+        ++s->avail_connections;
+    }
+    s->chr_can_read = fd_can_read;
+    s->chr_read = fd_read;
+    s->chr_event = fd_event;
+    s->handler_opaque = opaque;
+    if (cdc->update_read_handler) {
+        cdc->update_read_handler(s);
+    }
+
+    /* We're connecting to an already opened device, so let's make sure we
+       also get the open event */
+    if (s->opened) {
+        char_driver_generic_open(s);
+    }
+}
+
+static void char_driver_fini(TypeInstance *inst)
+{
+    CharDriver *chr = CHAR_DRIVER(inst);
+    CharDriverClass *cdc = CHAR_DRIVER_GET_CLASS(chr);
+
+    if (cdc->close) {
+        cdc->close(chr);
+    }
+}
+
+static TypeInfo chrdrv_type_info = {
+    .name = TYPE_CHAR_DRIVER,
+    .parent = TYPE_PLUG,
+    .instance_size = sizeof(CharDriver),
+    .instance_finalize = char_driver_fini,
+    .class_size = sizeof(CharDriverClass),
+    .class_init = char_driver_class_init,
+    .abstract = true,
+};
+
+static void register_backends(void)
+{
+    type_register_static(&chrdrv_type_info);
+}
+
+device_init(register_backends);
diff --git a/configure b/configure
index 6ec1020..63c62b0 100755
--- a/configure
+++ b/configure
@@ -3516,7 +3516,7 @@  DIRS="$DIRS pc-bios/spapr-rtas"
 DIRS="$DIRS roms/seabios roms/vgabios"
 DIRS="$DIRS fsdev ui"
 DIRS="$DIRS qapi"
-DIRS="$DIRS qga qom devices"
+DIRS="$DIRS qga qom devices chrdrv"
 FILES="Makefile tests/Makefile"
 FILES="$FILES tests/cris/Makefile tests/cris/.gdbinit"
 FILES="$FILES pc-bios/optionrom/Makefile pc-bios/keymaps"
diff --git a/include/qemu/chrdrv.h b/include/qemu/chrdrv.h
new file mode 100644
index 0000000..075b589
--- /dev/null
+++ b/include/qemu/chrdrv.h
@@ -0,0 +1,281 @@ 
+#ifndef QEMU_CHAR_DRIVER
+#define QEMU_CHAR_DRIVER
+
+#include "qemu/plug.h"
+
+/* Temporarily for a couple of enum */
+#include "qemu-char.h"
+
+/**
+ * @CharDriver
+ *
+ * A streaming data connection, typically used to transfer data from a @Device
+ * to some process on the host.
+ *
+ * A @CharDriver subclass implements the driver.  Typically, this is the host
+ * routine and may include a TCP, stdio, or fd transport.  The @Device that is
+ * interacting with the driver is the client.  This is typically a device that
+ * looks like a serial port including UARTs, virtio-serial, etc.
+ *
+ * @CharDriver also supports by direction messaging.  These messages carry no
+ * data though.
+ */
+typedef struct CharDriver
+{
+    Plug parent;
+
+    /* Public */
+
+    /**
+     * @avail_connection used by qdev to keep track of how "connections" are
+     * available in the @CharDriver verses how many qdev are using.  This is
+     * meant to ensure that the same @CharDriver isn't connected to multiple
+     * sockets at the same time.
+     *
+     * It's not well thought through though as there are other things that can
+     * use a @CharDriver and the attempt at supporting mux drivers results in
+     * this value just being ignored for mux (although not predictably).
+     *
+     * Sufficed to say, this needs to go away.
+     */
+    int avail_connections;
+
+    /**
+     * @opened despite what the name implies, this doesn't correspond to whether
+     * the @CharDriver is opened.  @CharDriver doesn't have a notion of opened,
+     * instead, @opened tracks whether the CHR_EVENT_OPEN event has been
+     * generated.  The CHR_EVENT_CLOSED event will clear the @opened flag.
+     *
+     * The primary purpose of this flag is to ensure the CHR_EVENT_OPEN event is
+     * not generated twice in a row.
+     */
+    int opened;
+
+    /* Private */
+
+    /**
+     * @chr_can_read when a client has added its handlers to a @CharDriver, this
+     * contains the can read callback for the client.
+     */
+    IOCanReadHandler *chr_can_read;
+
+    /**
+     * @chr_read when a client has added its handlers to a @CharDriver, this
+     * contains the read callback for the client.
+     */
+    IOReadHandler *chr_read;
+
+    /**
+     * @chr_event when a client has added its handlers to a @CharDriver, this
+     * contains the event callback for the client.
+     */
+    IOEventHandler *chr_event;
+
+    /**
+     * @handler_opaque when a client has added its handlers to a @CharDriver,
+     * this contains the opaque associated with the callbacks for the client.
+     */
+    void *handler_opaque;
+} CharDriver;
+
+typedef struct CharDriverClass
+{
+    PlugClass parent_class;
+
+    /* Public */
+    int (*write)(CharDriver *s, const uint8_t *buf, int len);
+    int (*ioctl)(CharDriver *s, int cmd, void *arg);
+    int (*get_msgfd)(CharDriver *s);
+    void (*send_event)(CharDriver *chr, int event);
+    void (*accept_input)(CharDriver *chr);
+    void (*set_echo)(CharDriver *chr, bool echo);
+    void (*guest_open)(CharDriver *chr);
+    void (*guest_close)(CharDriver *chr);
+
+    /* Protected */
+    /**
+     * @open:
+     *
+     * This is called during realize to initialize the object.  At this point,
+     * all of the properties should have been set.
+     */
+    void (*open)(CharDriver *s, Error **errp);
+
+    /**
+     * @update_read_handler:
+     *
+     * This is called after @char_driver_add_handlers is called to allow sub-
+     * classes to re-register their callbacks if necessary.
+     */
+    void (*update_read_handler)(CharDriver *s);
+
+    /**
+     * @close:
+     *
+     * Called during the finalize path.  The default behavior sends a
+     * CHR_EVENT_CLOSED.  It's generally better to use the classes destructor to
+     * implement driver specific cleanup.
+     */
+    void (*close)(CharDriver *chr);
+} CharDriverClass;
+
+#define TYPE_CHAR_DRIVER "char-driver"
+#define CHAR_DRIVER(obj) TYPE_CHECK(CharDriver, obj, TYPE_CHAR_DRIVER)
+#define CHAR_DRIVER_CLASS(class) \
+    TYPE_CLASS_CHECK(CharDriverClass, class, TYPE_CHAR_DRIVER)
+#define CHAR_DRIVER_GET_CLASS(obj) \
+    TYPE_GET_CLASS(CharDriverClass, obj, TYPE_CHAR_DRIVER)
+
+/**
+ * @char_driver_write:
+ *
+ * Write data to a @CharDriver
+ *
+ * @buf      The data to write
+ * @len      The size of the data in buf
+ *
+ * Returns:  The number of bytes written to the device
+ *
+ * Notes:    Each backend deals with flow control on its own.  Depending on the
+ *           driver, this function may block execution or silently drop data.
+ *
+ *           Dropping data may also occur if the backend uses a connection
+ *           oriented transport and the transport is disconnected.
+ *
+ *           The caller receives no indication that data was dropped.  This
+ *           function may return a partial write result.
+ */
+int char_driver_write(CharDriver *s, const uint8_t *buf, int len);
+
+/**
+ * @char_driver_ioctl:
+ *
+ * Performs a device specific ioctl.
+ *
+ * @cmd      an ioctl, see CHR_IOCTL_*
+ * @arg      values depends on @cmd
+ *
+ * Returns:  The result of this depends on the @cmd.  If @cmd is not supported
+ *           by this device, -ENOTSUP.
+ */
+int char_driver_ioctl(CharDriver *s, int cmd, void *arg);
+
+/**
+ * @char_driver_get_msgfd:
+ *
+ * If the driver has received a file descriptor through its transport, this
+ * function will return the file descriptor.
+ *
+ * Returns:  The file descriptor or -1 if transport doesn't support file
+ *           descriptor passing or doesn't currently have a file descriptor.
+ */
+int char_driver_get_msgfd(CharDriver *s);
+
+/**
+ * @char_driver_send_event:
+ *
+ * Raise an event to a driver.
+ *
+ * @event  see CHR_EVENT_*
+ *
+ * Notes:  There is no way to determine if the driver successfully handled the
+ *         event.
+ */
+void char_driver_send_event(CharDriver *chr, int event);
+
+/**
+ * @char_driver_accept_input:
+ *
+ * I honestly can't tell what this function is meant to do.
+ */
+void char_driver_accept_input(CharDriver *chr);
+
+/**
+ * @char_driver_set_echo:
+ *
+ * Requests to override the backends use of echo.  This is really only
+ * applicable to the stdio backend but is a generic interface today.
+ *
+ * @echo true to enable echo
+ */
+void char_driver_set_echo(CharDriver *chr, bool echo);
+
+/**
+ * @char_driver_guest_open:
+ *
+ * If the client has a notion of a connection, this is invoked when the
+ * connection is created.
+ *
+ * Note:  There is no way to determine if a client has the notion of a
+ *        connection.  A driver cannot rely on this function ever being called.
+ */
+void char_driver_guest_open(CharDriver *chr);
+
+/**
+ * @char_driver_guest_close:
+ *
+ * If the client has a notion of a connection, this is invoked when the
+ * connection is closed.
+ *
+ * Note:  There is no way to determine if a client has the notion of a
+ *        connection.  A driver cannot rely on this function ever being called.
+ */
+void char_driver_guest_close(CharDriver *chr);
+
+/**
+ * @char_driver_can_read:
+ *
+ * Returns:  The maximum number of bytes the client connected to the driver can
+ *           receive at the moment.
+ */
+int char_driver_can_read(CharDriver *chr);
+
+/**
+ * @char_driver_read:
+ *
+ * This function transfers the contents of buf to the client.
+ *
+ * @buf  the buffer to write to the client
+ * @len  the number of bytes to write
+ *
+ * Notes:  This function should only be invoked after receiving a non-zero
+ *         value from @char_driver_can_read.  @len may be larger than the return
+ *         value but the results are undefined.  It may result in the entire
+ *         message being dropped or the message being truncated.
+ */
+void char_driver_read(CharDriver *chr, uint8_t *buf, int len);
+
+/**
+ * @char_driver_event:
+ *
+ * Sends an event to the client of a driver.
+ *
+ * @event  the CHR_EVENT_* to send to the client
+ */
+void char_driver_event(CharDriver *chr, int event);
+
+/**
+ * @char_driver_add_handlers:
+ *
+ * Connect a client to a @CharDriver.  A connected client may send data to the
+ * driver by using the @char_driver_write function.  Data is received from the
+ * driver to the client using the callbacks registered in this function.
+ *
+ * @fd_can_read  This callback returns the maximum amount of data the client is
+ *               prepared to receive from the driver.
+ *
+ * @fd_read      This callback is used by the driver to pass data to the client.
+ *
+ * @fd_event     This callback is used to send events from the driver to the
+ *               client.
+ *
+ * @opaque       An opaque value that is passed with the registered callbacks to
+ *               form a closure.
+ */
+void char_driver_add_handlers(CharDriver *s,
+                              IOCanReadHandler *fd_can_read,
+                              IOReadHandler *fd_read,
+                              IOEventHandler *fd_event,
+                              void *opaque);
+
+#endif