updated client/server for video
This commit is contained in:
parent
57b6da0895
commit
a968218f60
@ -10,6 +10,7 @@ use tonic::Request;
|
||||
use tracing::{debug, error, info, warn};
|
||||
use winit::{
|
||||
event_loop::EventLoopBuilder,
|
||||
platform::x11::EventLoopBuilderExtX11,
|
||||
platform::unix::EventLoopBuilderExtUnix,
|
||||
event::Event,
|
||||
};
|
||||
@ -79,14 +80,14 @@ impl LesavkaClientApp {
|
||||
.unwrap();
|
||||
|
||||
std::thread::spawn(move || {
|
||||
let el = EventLoopBuilder::default()
|
||||
let el = EventLoopBuilder::<()>::new()
|
||||
.with_any_thread(true)
|
||||
.build()
|
||||
.unwrap();
|
||||
let win0 = MonitorWindow::new(0, &el).expect("win0");
|
||||
let win1 = MonitorWindow::new(1, &el).expect("win1");
|
||||
|
||||
el.run(move |_: Event<()>, _el| {
|
||||
el.run(move |_: Event<'_, ()>, _| {
|
||||
while let Ok(pkt) = video_rx.try_recv() {
|
||||
match pkt.id {
|
||||
0 => win0.push_packet(pkt),
|
||||
|
||||
@ -29,18 +29,34 @@ echo "==> 2b. Predictable /dev names for each capture card"
|
||||
# ATTRS{serial}=="1200655409098", ATTR{index}=="0", \
|
||||
# SYMLINK+="lesavka_l_eye"
|
||||
# RULES
|
||||
sudo tee /etc/udev/rules.d/85-gc311.rules >/dev/null <<'RULES'
|
||||
# LEFT eye – GC311 on hub‑port 1‑3
|
||||
SUBSYSTEM=="video4linux", ATTRS{idVendor}=="07ca", ATTRS{idProduct}=="3311", \
|
||||
ATTRS{index}=="0", ENV{ID_PATH_TAG}=="usb-platform-1a400000.xhci-usb-0_1_3_1_0", \
|
||||
SYMLINK+="lesavka_l_eye"
|
||||
|
||||
# RIGHT eye – GC311 on hub‑port 1‑4
|
||||
SUBSYSTEM=="video4linux", ATTRS{idVendor}=="07ca", ATTRS{idProduct}=="3311", \
|
||||
ATTRS{index}=="0", ENV{ID_PATH_TAG}=="usb-platform-1a400000.xhci-usb-0_1_4_1_0", \
|
||||
SYMLINK+="lesavka_r_eye"
|
||||
RULES
|
||||
# probe all v4l2 devices, keep only the two GC311 capture cards
|
||||
mapfile -t TAGS < <(
|
||||
for v in /dev/video*; do
|
||||
if udevadm info -q property -n "$v" | grep -q 'ID_VENDOR_ID=07ca'; then
|
||||
# extract the stable port token (ID_PATH_TAG=…)
|
||||
udevadm info -q property -n "$v" |
|
||||
awk -F= '/^ID_PATH_TAG=/{print $2}'
|
||||
fi
|
||||
done | sort
|
||||
)
|
||||
|
||||
if [ "${#TAGS[@]}" -ne 2 ]; then
|
||||
echo "❌ Exactly two GC311 devices must be attached!" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
LEFT_TAG=${TAGS[0]}
|
||||
RIGHT_TAG=${TAGS[1]}
|
||||
|
||||
cat <<EOF | sudo tee /etc/udev/rules.d/85-gc311.rules
|
||||
# auto‑generated by install‑server.sh – DO NOT EDIT
|
||||
SUBSYSTEM=="video4linux", ATTRS{idVendor}=="07ca", ATTRS{idProduct}=="3311", \\
|
||||
ATTRS{index}=="0", ENV{ID_PATH_TAG}=="$LEFT_TAG", SYMLINK+="lesavka_l_eye"
|
||||
|
||||
SUBSYSTEM=="video4linux", ATTRS{idVendor}=="07ca", ATTRS{idProduct}=="3311", \\
|
||||
ATTRS{index}=="0", ENV{ID_PATH_TAG}=="$RIGHT_TAG", SYMLINK+="lesavka_r_eye"
|
||||
EOF
|
||||
sudo udevadm control --reload
|
||||
sudo udevadm trigger --subsystem-match=video4linux
|
||||
udevadm settle
|
||||
|
||||
@ -11,7 +11,7 @@ use tracing::{info, trace};
|
||||
use tracing_subscriber::{fmt, EnvFilter};
|
||||
use udev::{MonitorBuilder};
|
||||
|
||||
use usb_gadget::UsbGadgetManager;
|
||||
use usb_gadget::UsbGadget;
|
||||
use lesavka_server::{video, usb_reset};
|
||||
|
||||
use lesavka_common::lesavka::{
|
||||
@ -77,11 +77,11 @@ fn list_gc311_devices() -> anyhow::Result<Vec<String>> {
|
||||
struct Handler {
|
||||
kb: Arc<Mutex<tokio::fs::File>>,
|
||||
ms: Arc<Mutex<tokio::fs::File>>,
|
||||
gadget: UsbGadgetManager,
|
||||
gadget: UsbGadget,
|
||||
}
|
||||
|
||||
impl Handler {
|
||||
fn make(gadget: UsbGadget) -> anyhow::Result<Self> {
|
||||
async fn make(gadget: UsbGadget) -> anyhow::Result<Self> {
|
||||
let kb = OpenOptions::new().write(true).open("/dev/hidg0").await?;
|
||||
let ms = OpenOptions::new().write(true)
|
||||
.custom_flags(libc::O_NONBLOCK)
|
||||
@ -191,10 +191,10 @@ async fn main() -> anyhow::Result<()> {
|
||||
/* auto‑cycle task */
|
||||
// tokio::spawn(async { monitor_gc311_disconnect().await.ok(); });
|
||||
|
||||
let gadget = UsbGadgetManager::new("lesavka");
|
||||
let gadget = UsbGadget::new("lesavka");
|
||||
gadget.cycle().ok();
|
||||
|
||||
let handler = Handler::make(gadget.clone())?;
|
||||
let handler = Handler::make(gadget.clone()).await?;
|
||||
|
||||
tokio::spawn({
|
||||
let gadget = gadget.clone();
|
||||
|
||||
@ -7,32 +7,28 @@ pub struct UsbGadget {
|
||||
udc_file: &'static str,
|
||||
}
|
||||
|
||||
impl UsbGadgetManager {
|
||||
pub fn new(gadget_name: &'static str) -> Self {
|
||||
impl UsbGadget {
|
||||
pub fn new(name: &'static str) -> Self {
|
||||
// /sys/kernel/config/usb_gadget/<name>/UDC
|
||||
Self { udc_file: Box::leak(
|
||||
format!("/sys/kernel/config/usb_gadget/{gadget_name}/UDC").into_boxed_str())
|
||||
format!("/sys/kernel/config/usb_gadget/{name}/UDC").into_boxed_str())
|
||||
}
|
||||
}
|
||||
|
||||
/// Force the host to re‑enumerate our HID gadget.
|
||||
pub fn cycle(&self) -> Result<()> {
|
||||
// 1. detach
|
||||
info!("UDC‑cycle: detaching gadget");
|
||||
OpenOptions::new().write(true).open(self.udc_file)?
|
||||
.write_all(b"")?;
|
||||
OpenOptions::new().write(true).open(self.udc_file)?.write_all(b"")?;
|
||||
thread::sleep(Duration::from_millis(200));
|
||||
|
||||
// 2. wait ≥ 100 ms so host sees a disconnect
|
||||
std::thread::sleep(Duration::from_millis(200));
|
||||
|
||||
// 3. re‑attach to **first** UDC (dwc2)
|
||||
let udc = std::fs::read_dir("/sys/class/udc")?
|
||||
.next().context("no UDC present")?
|
||||
let udc_name = std::fs::read_dir("/sys/class/udc")?
|
||||
.next()
|
||||
.transpose()?
|
||||
.context("no UDC present")?
|
||||
.file_name();
|
||||
let name = udc.to_string_lossy();
|
||||
info!("UDC‑cycle: re‑attaching to {name}");
|
||||
OpenOptions::new().write(true).open(self.udc_file)?
|
||||
.write_all(name.as_bytes())?;
|
||||
.write_all(udc_name.to_str().unwrap().as_bytes())?;
|
||||
info!("USB‑gadget cycled");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user