From patchwork Mon Jul 25 01:44:49 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Anthony Liguori X-Patchwork-Id: 106584 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [140.186.70.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id DDAD9B6F7F for ; Mon, 25 Jul 2011 11:46:32 +1000 (EST) Received: from localhost ([::1]:56862 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1QlAFV-0006UD-Ai for incoming@patchwork.ozlabs.org; Sun, 24 Jul 2011 21:46:25 -0400 Received: from eggs.gnu.org ([140.186.70.92]:42461) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1QlAEM-0004Fa-Sf for qemu-devel@nongnu.org; Sun, 24 Jul 2011 21:45:18 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1QlAEI-0005ZL-IF for qemu-devel@nongnu.org; Sun, 24 Jul 2011 21:45:14 -0400 Received: from e8.ny.us.ibm.com ([32.97.182.138]:50160) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1QlAEI-0005Yj-6f for qemu-devel@nongnu.org; Sun, 24 Jul 2011 21:45:10 -0400 Received: from d01relay01.pok.ibm.com (d01relay01.pok.ibm.com [9.56.227.233]) by e8.ny.us.ibm.com (8.14.4/8.13.1) with ESMTP id p6P1WR0O014322 for ; Sun, 24 Jul 2011 21:32:27 -0400 Received: from d01av04.pok.ibm.com (d01av04.pok.ibm.com [9.56.224.64]) by d01relay01.pok.ibm.com (8.13.8/8.13.8/NCO v10.0) with ESMTP id p6P1j9KW138940 for ; Sun, 24 Jul 2011 21:45:09 -0400 Received: from d01av04.pok.ibm.com (loopback [127.0.0.1]) by d01av04.pok.ibm.com (8.14.4/8.13.1/NCO v10.0 AVout) with ESMTP id p6P1j9b1008022 for ; Sun, 24 Jul 2011 21:45:09 -0400 Received: from titi.austin.rr.com (sig-9-65-207-230.mts.ibm.com [9.65.207.230]) by d01av04.pok.ibm.com (8.14.4/8.13.1/NCO v10.0 AVin) with ESMTP id p6P1itKw006824; Sun, 24 Jul 2011 21:45:08 -0400 From: Anthony Liguori To: qemu-devel@nongnu.org Date: Sun, 24 Jul 2011 20:44:49 -0500 Message-Id: <1311558293-5855-18-git-send-email-aliguori@us.ibm.com> X-Mailer: git-send-email 1.7.4.1 In-Reply-To: <1311558293-5855-1-git-send-email-aliguori@us.ibm.com> References: <1311558293-5855-1-git-send-email-aliguori@us.ibm.com> X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.6, seldom 2.4 (older, 4) X-Received-From: 32.97.182.138 Cc: Anthony Liguori Subject: [Qemu-devel] [PATCH 17/21] qom: add CharDriver class X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org 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 --- 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 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