1、qemu添加spicevmc前端时会创建vmc通道。
-chardev 'spicevmc,id=usbredirchardev0,name=usbredir'
red::shared_ptr<RedCharDevice>
spicevmc_device_connect(RedsState *reds, SpiceCharDeviceInstance *sin, uint8_t channel_type)
{
auto channel(red_vmc_channel_new(reds, channel_type)); //创建RedVmcChannel通道
if (!channel) {
return red::shared_ptr<RedCharDevice>();
}
auto dev = red::make_shared<RedCharDeviceSpiceVmc>(sin, reds, channel.get()); //设备与通道绑定
channel->chardev_sin = sin;
return dev;
}
2、RedVmcChannel中的rcc成员变量说明Channel与client是1对1的关系。
struct RedVmcChannel: public RedChannel
{
RedVmcChannel(RedsState *reds, uint32_t type, uint32_t id);
~RedVmcChannel() override;
void on_connect(RedClient *client, RedStream *stream, int migration, RedChannelCapabilities *caps) override;
VmcChannelClient *rcc;
RedCharDevice *chardev; /* weak */
SpiceCharDeviceInstance *chardev_sin;
red::shared_ptr<RedVmcPipeItem> pipe_item;
RedCharDeviceWriteBuffer *recv_from_client_buf;
uint8_t port_opened;
uint32_t queued_data;
RedStatCounter in_data;
RedStatCounter in_compressed;
RedStatCounter in_decompressed;
RedStatCounter out_data;
RedStatCounter out_compressed;
RedStatCounter out_uncompressed;
};
3、usbredir客户端连接上,创建 VmcChannelClient。
void RedVmcChannel::on_connect(RedClient *client, RedStream *stream, int migration,
RedChannelCapabilities *caps)
{
RedVmcChannel *vmc_channel;
SpiceCharDeviceInstance *sin;
SpiceCharDeviceInterface *sif;
vmc_channel = this;
sin = vmc_channel->chardev_sin;
if (rcc) {
red_channel_warning(this, "channel client (%p) already connected, refusing second connection", rcc);
// TODO: notify client in advance about the in use channel using
// SPICE_MSG_MAIN_CHANNEL_IN_USE (for example)
red_stream_free(stream);
return;
}
rcc = vmc_channel_client_create(this, client, stream, caps);
if (!rcc) {
return;
}
vmc_channel->queued_data = 0;
rcc->ack_zero_messages_window();
if (strcmp(sin->subtype, "port") == 0) {
spicevmc_port_send_init(rcc);
}
if (!vmc_channel->chardev->client_add(reinterpret_cast<RedCharDeviceClientOpaque *>(client), FALSE, 0, ~0, ~0, rcc->is_waiting_for_migrate_data())) {
spice_warning("failed to add client to spicevmc");
rcc->disconnect();
return;
}
sif = spice_char_device_get_interface(sin);
if (sif->state) {
sif->state(sin, 1);
}
}
4、处理VmcChannelClient客户端消息,写入chardev设备。
bool VmcChannelClient::handle_message(uint16_t type, uint32_t size, void *msg)
{
/* NOTE: *msg free by g_free() (when cb to VmcChannelClient::release_recv_buf
* with the compressed msg type) */
RedVmcChannel *channel;
SpiceCharDeviceInterface *sif;
channel = get_channel();
sif = spice_char_device_get_interface(channel->chardev_sin);
switch (type) {
case SPICE_MSGC_SPICEVMC_DATA:
spice_assert(channel->recv_from_client_buf->buf == msg);
stat_inc_counter(channel->in_data, size);
channel->recv_from_client_buf->buf_used = size;
channel->chardev->write_buffer_add(channel->recv_from_client_buf);
channel->recv_from_client_buf = nullptr;
break;
case SPICE_MSGC_SPICEVMC_COMPRESSED_DATA:
return handle_compressed_msg(channel, this, static_cast<SpiceMsgCompressedData *>(msg));
break;
case SPICE_MSGC_PORT_EVENT:
if (size != sizeof(uint8_t)) {
spice_warning("bad port event message size");
return FALSE;
}
if (sif->base.minor_version >= 2 && sif->event != nullptr)
sif->event(channel->chardev_sin, *static_cast<uint8_t *>(msg));
break;
default:
return RedChannelClient::handle_message(type, size, msg);
}
return TRUE;
}
5、数据添加到队列中。
void RedCharDevice::write_buffer_add(RedCharDeviceWriteBuffer *write_buf)
{
/* caller shouldn't add buffers for client that was removed */
if (write_buf->priv->origin == WRITE_BUFFER_ORIGIN_CLIENT &&
!red_char_device_client_find(this, write_buf->priv->client)) {
g_warning("client not found: this %p client %p", this, write_buf->priv->client);
red_char_device_write_buffer_unref(write_buf);
return;
}
g_queue_push_head(&priv->write_queue, write_buf);
write_to_device();
}
6、最终写入qemu的spicevmc前端。
int RedCharDevice::write_to_device()
{
SpiceCharDeviceInterface *sif;
int total = 0;
int n;
if (!priv->running || priv->wait_for_migrate_data || !priv->sin) {
return 0;
}
/* protect against recursion with red_char_device_wakeup */
if (priv->during_write_to_device++ > 0) {
return 0;
}
red::shared_ptr<RedCharDevice> hold_dev(this);
if (priv->write_to_dev_timer) {
red_timer_cancel(priv->write_to_dev_timer);
}
sif = spice_char_device_get_interface(priv->sin);
while (priv->running) {
uint32_t write_len;
if (!priv->cur_write_buf) {
priv->cur_write_buf =
static_cast<RedCharDeviceWriteBuffer *>(g_queue_pop_tail(&priv->write_queue));
if (!priv->cur_write_buf)
break;
priv->cur_write_buf_pos = priv->cur_write_buf->buf;
}
write_len = priv->cur_write_buf->buf + priv->cur_write_buf->buf_used - priv->cur_write_buf_pos;
n = sif->write(priv->sin, priv->cur_write_buf_pos, write_len);
if (n <= 0) {
if (priv->during_write_to_device > 1) {
priv->during_write_to_device = 1;
continue; /* a wakeup might have been called during the write - make sure it doesn't get lost */
}
break;
}
total += n;
write_len -= n;
if (!write_len) {
write_buffer_release(&priv->cur_write_buf);
continue;
}
priv->cur_write_buf_pos += n;
}
/* retry writing as long as the write queue is not empty */
if (priv->running) {
if (priv->cur_write_buf) {
if (priv->write_to_dev_timer) {
red_timer_start(priv->write_to_dev_timer,
CHAR_DEVICE_WRITE_TO_TIMEOUT);
}
} else {
spice_assert(g_queue_is_empty(&priv->write_queue));
}
priv->active = priv->active || total;
}
priv->during_write_to_device = 0;
return total;
}
7、spice vmc前端注册。
static SpiceCharDeviceInterface vmc_interface = {
.base.type = SPICE_INTERFACE_CHAR_DEVICE,
.base.description = "spice virtual channel char device",
.base.major_version = SPICE_INTERFACE_CHAR_DEVICE_MAJOR,
.base.minor_version = SPICE_INTERFACE_CHAR_DEVICE_MINOR,
.state = vmc_state,
.write = vmc_write,
.read = vmc_read,
.event = vmc_event,
.flags = SPICE_CHAR_DEVICE_NOTIFY_WRITABLE,
};
8、spice vmc前端写函数实现。
static int vmc_write(SpiceCharDeviceInstance *sin, const uint8_t *buf, int len)
{
SpiceChardev *scd = container_of(sin, SpiceChardev, sin);
Chardev *chr = CHARDEV(scd);
ssize_t out = 0;
ssize_t last_out;
uint8_t* p = (uint8_t*)buf;
while (len > 0) {
int can_write = qemu_chr_be_can_write(chr);
last_out = MIN(len, can_write);
if (last_out <= 0) {
break;
}
qemu_chr_be_write(chr, p, last_out);
out += last_out;
len -= last_out;
p += last_out;
}
trace_spice_vmc_write(out, len + out);
return out;
}
9、调用qemu char前端模块的写。
void qemu_chr_be_write(Chardev *s, uint8_t *buf, int len)
{
if (qemu_chr_replay(s)) {
if (replay_mode == REPLAY_MODE_PLAY) {
return;
}
replay_chr_be_write(s, buf, len);
} else {
qemu_chr_be_write_impl(s, buf, len);
}
}
10、判断是否有后端设备连接并发送给后端设备
void qemu_chr_be_write_impl(Chardev *s, uint8_t *buf, int len)
{
CharBackend *be = s->be;
if (be && be->chr_read) {
be->chr_read(be->opaque, buf, len);
}
}