当前位置: 首页 > news >正文

usb重定向qemu前端处理

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);}
}

http://www.dtcms.com/a/138214.html

相关文章:

  • Java排序算法百科全书:原理、实现与实战指南
  • 如何新建一个空分支(不继承 master 或任何提交)
  • SQL刷题记录贴
  • Nodemon vs. PM2:开发与生产环境的 Node.js 部署最佳实践
  • 【AI插件开发】Notepad++ AI插件开发实践:实现对话窗口功能
  • 多 Agent 协作怎么整:从谷歌A2A到多Agent交互方案实现
  • 【hive】Hive对数据库,对表的操作(一)
  • 第六节:React Hooks进阶篇-自定义Hook设计
  • 大模型时代下全场景数据消费平台的智能BI—Quick BI深度解析
  • 【数字图像处理】图像增强
  • King3399(ubuntu文件系统)GDB/GDBServer调试配置
  • 《Cangjie Magic实战手记:用Agent DSL与MCP协议重构智能物流调度系统》——一次从技术困惑到行业落地的探索之旅
  • 当 AI 有了 “万能插头” 和 “通用语言”:MCP 与 A2A 如何重构智能体生态
  • 【JAVA】在idea新加artifact时,点击Build-Build Artifacts时,新加的artifact不能选中
  • Java NIO Java 虚拟线程(微线程)与 Go 协程的运行原理不同 为何Go 能在低配机器上承接10万 Websocket 协议连接
  • 吊顶上的灯线怎么预留?是提前到位还是后期随意拉拽?
  • AI 驱动下的后端开发架构革命:从智能协同体系
  • golang处理时间的包time一次性全面了解
  • 岚图L3智能架构发布,9大首发新技术引领电动车变革
  • git更新的bug
  • C/C++语言常见问题-智能指针、多态原理
  • ES|QL,知道吗,专为搜索而生 —— 推出评分和语义搜索
  • Elasticsearch的Java客户端库QueryBuilders查询方法大全
  • 【Amazon 工具】在MacOS本地安装 AWS CLI、kubectl、eksctl工具
  • 【Windows上配置Git环境】
  • 关于 AI驱动的智慧家居、智慧城市、智慧交通、智慧医疗和智慧生活 的详细解析,涵盖其定义、核心技术、应用场景、典型案例及未来趋势
  • AI与物联网的深度融合:开启智能生活新时代
  • 健康养生:开启活力生活的密钥
  • 极狐GitLab CI/CD 流水线计算分钟数如何管理?
  • ubuntu24.04离线安装deb格式的mysql-community-8.4.4