/* -*- 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_