/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */ /* Copyright (C) 2009-2015 Red Hat, Inc. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, see . */ #ifndef CHAR_DEVICE_H_ #define CHAR_DEVICE_H_ #include #include "spice.h" #include "red-channel.h" #include "migration-protocol.h" #define RED_TYPE_CHAR_DEVICE red_char_device_get_type() #define RED_CHAR_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), RED_TYPE_CHAR_DEVICE, RedCharDevice)) #define RED_CHAR_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), RED_TYPE_CHAR_DEVICE, RedCharDeviceClass)) #define RED_IS_CHAR_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), RED_TYPE_CHAR_DEVICE)) #define RED_IS_CHAR_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), RED_TYPE_CHAR_DEVICE)) #define RED_CHAR_DEVICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), RED_TYPE_CHAR_DEVICE, RedCharDeviceClass)) /* SpiceCharDeviceState is public API, but internally we use RedCharDevice */ typedef struct SpiceCharDeviceState RedCharDevice; typedef struct RedCharDeviceClass RedCharDeviceClass; typedef struct RedCharDevicePrivate RedCharDevicePrivate; /* 'SpiceCharDeviceState' name is used for consistency with what spice-char.h exports */ struct SpiceCharDeviceState { GObject parent; RedCharDevicePrivate *priv; }; struct RedCharDeviceClass { GObjectClass parent_class; /* * Messages that are addressed to the client can be queued in case we have * multiple clients and some of them don't have enough tokens. */ /* reads from the device till reaching a msg that should be sent to the client, * or till the reading fails */ PipeItem* (*read_one_msg_from_device)(SpiceCharDeviceInstance *sin, void *opaque); /* after this call, the message is unreferenced */ void (*send_msg_to_client)(PipeItem *msg, RedClient *client, void *opaque); /* The cb is called when a predefined number of write buffers were consumed by the * device */ void (*send_tokens_to_client)(RedClient *client, uint32_t tokens, void *opaque); /* The cb is called when a server (self) message that was addressed to the device, * has been completely written to it */ void (*on_free_self_token)(void *opaque); /* This cb is called if it is recommanded that a client will be removed * due to slow flow or due to some other error. * The called instance should disconnect the client, or at least the corresponding channel */ void (*remove_client)(RedClient *client, void *opaque); }; GType red_char_device_get_type(void) G_GNUC_CONST; /* * Shared code for char devices, mainly for flow control. * * How to use the api: * ================== * device attached: create new object instantiating a RedCharDevice child class * device detached: call red_char_device_destroy/reset * * client connected and associated with a device: red_char_device__add * client disconnected: red_char_device__remove * * Writing to the device * --------------------- * Write the data into RedCharDeviceWriteBuffer: * call red_char_device_buffer_get in order to get an appropriate buffer. * call red_char_device_buffer_add in order to push the buffer to the write queue. * If you choose not to push the buffer to the device, call * red_char_device_buffer_release * * reading from the device * ----------------------- * The callback read_one_msg_from_device (see below) should be implemented * (using sif->read). * When the device is ready, this callback is called, and is expected to * return one message which is addressed to the client, or NULL if the read * hasn't completed. * * calls triggered from the device (qemu): * -------------------------------------- * red_char_device_start * red_char_device_stop * red_char_device_wakeup (for reading from the device) */ /* refcounting is used to protect the char_dev from being deallocated in * case red_char_device_destroy has been called * during a callback, and we might still access the char_dev afterwards. */ /* * Note about multiple-clients: * Multiclients are currently not supported in any of the character devices: * spicevmc does not allow more than one client (and at least for usb, it should stay this way). * smartcard code is not compatible with more than one reader. * The server and guest agent code doesn't distinguish messages from different clients. * In addition, its current flow control code (e.g., tokens handling) is wrong and doesn't * take into account the different clients. * * Nonetheless, the following code introduces some support for multiple-clients: * We track the number of tokens for all the clients, and we read from the device * if one of the clients have enough tokens. For the clients that don't have tokens, * we queue the messages, till they receive tokens, or till a timeout. * * TODO: * At least for the agent, not all the messages from the device will be directed to all * the clients (e.g., copy from guest to a specific client). Thus, support for * client-specific-messages should be added. * In addition, we should have support for clients that are being connected * in the middle of a message transfer from the agent to the clients. * * */ /* buffer that is used for writing to the device */ typedef struct RedCharDeviceWriteBuffer { RingItem link; int origin; RedClient *client; /* The client that sent the message to the device. NULL if the server created the message */ uint8_t *buf; uint32_t buf_size; uint32_t buf_used; uint32_t token_price; uint32_t refs; } RedCharDeviceWriteBuffer; void red_char_device_reset_dev_instance(RedCharDevice *dev, SpiceCharDeviceInstance *sin); void red_char_device_destroy(RedCharDevice *dev); void *red_char_device_opaque_get(RedCharDevice *dev); /* only one client is supported */ void red_char_device_migrate_data_marshall(RedCharDevice *dev, SpiceMarshaller *m); void red_char_device_migrate_data_marshall_empty(SpiceMarshaller *m); int red_char_device_restore(RedCharDevice *dev, SpiceMigrateDataCharDevice *mig_data); /* * Resets write/read queues, and moves that state to being stopped. * This routine is a workaround for a bad tokens management in the vdagent * protocol: * The client tokens' are set only once, when the main channel is initialized. * Instead, it would have been more appropriate to reset them upon AGEN_CONNECT. * The client tokens are tracked as part of the RedCharDeviceClient. Thus, * in order to be backwartd compatible with the client, we need to track the tokens * event when the agent is detached. We don't destroy the char_device state, and * instead we just reset it. * In addition, there is a misshandling of AGENT_TOKENS message in spice-gtk: it * overrides the amount of tokens, instead of adding the given amount. * * todo: change AGENT_CONNECT msg to contain tokens count. */ void red_char_device_reset(RedCharDevice *dev); /* max_send_queue_size = how many messages we can read from the device and enqueue for this client, * when we have tokens for other clients and no tokens for this one */ int red_char_device_client_add(RedCharDevice *dev, RedClient *client, int do_flow_control, uint32_t max_send_queue_size, uint32_t num_client_tokens, uint32_t num_send_tokens, int wait_for_migrate_data); void red_char_device_client_remove(RedCharDevice *dev, RedClient *client); int red_char_device_client_exists(RedCharDevice *dev, RedClient *client); void red_char_device_start(RedCharDevice *dev); void red_char_device_stop(RedCharDevice *dev); SpiceServer* red_char_device_get_server(RedCharDevice *dev); /** Read from device **/ void red_char_device_wakeup(RedCharDevice *dev); void red_char_device_send_to_client_tokens_add(RedCharDevice *dev, RedClient *client, uint32_t tokens); void red_char_device_send_to_client_tokens_set(RedCharDevice *dev, RedClient *client, uint32_t tokens); /** Write to device **/ RedCharDeviceWriteBuffer *red_char_device_write_buffer_get(RedCharDevice *dev, RedClient *client, int size); RedCharDeviceWriteBuffer *red_char_device_write_buffer_get_server_no_token( RedCharDevice *dev, int size); /* Either add the buffer to the write queue or release it */ void red_char_device_write_buffer_add(RedCharDevice *dev, RedCharDeviceWriteBuffer *write_buf); void red_char_device_write_buffer_release(RedCharDevice *dev, RedCharDeviceWriteBuffer *write_buf); /* api for specific char devices */ RedCharDevice *spicevmc_device_connect(RedsState *reds, SpiceCharDeviceInstance *sin, uint8_t channel_type); void spicevmc_device_disconnect(RedsState *reds, SpiceCharDeviceInstance *char_device); SpiceCharDeviceInterface *spice_char_device_get_interface(SpiceCharDeviceInstance *instance); #endif // CHAR_DEVICE_H_