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 tracing::{debug, error, info, warn};
|
||||||
use winit::{
|
use winit::{
|
||||||
event_loop::EventLoopBuilder,
|
event_loop::EventLoopBuilder,
|
||||||
|
platform::x11::EventLoopBuilderExtX11,
|
||||||
platform::unix::EventLoopBuilderExtUnix,
|
platform::unix::EventLoopBuilderExtUnix,
|
||||||
event::Event,
|
event::Event,
|
||||||
};
|
};
|
||||||
@ -79,14 +80,14 @@ impl LesavkaClientApp {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
std::thread::spawn(move || {
|
std::thread::spawn(move || {
|
||||||
let el = EventLoopBuilder::default()
|
let el = EventLoopBuilder::<()>::new()
|
||||||
.with_any_thread(true)
|
.with_any_thread(true)
|
||||||
.build()
|
.build()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let win0 = MonitorWindow::new(0, &el).expect("win0");
|
let win0 = MonitorWindow::new(0, &el).expect("win0");
|
||||||
let win1 = MonitorWindow::new(1, &el).expect("win1");
|
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() {
|
while let Ok(pkt) = video_rx.try_recv() {
|
||||||
match pkt.id {
|
match pkt.id {
|
||||||
0 => win0.push_packet(pkt),
|
0 => win0.push_packet(pkt),
|
||||||
|
|||||||
@ -29,18 +29,34 @@ echo "==> 2b. Predictable /dev names for each capture card"
|
|||||||
# ATTRS{serial}=="1200655409098", ATTR{index}=="0", \
|
# ATTRS{serial}=="1200655409098", ATTR{index}=="0", \
|
||||||
# SYMLINK+="lesavka_l_eye"
|
# SYMLINK+="lesavka_l_eye"
|
||||||
# RULES
|
# 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
|
# probe all v4l2 devices, keep only the two GC311 capture cards
|
||||||
SUBSYSTEM=="video4linux", ATTRS{idVendor}=="07ca", ATTRS{idProduct}=="3311", \
|
mapfile -t TAGS < <(
|
||||||
ATTRS{index}=="0", ENV{ID_PATH_TAG}=="usb-platform-1a400000.xhci-usb-0_1_4_1_0", \
|
for v in /dev/video*; do
|
||||||
SYMLINK+="lesavka_r_eye"
|
if udevadm info -q property -n "$v" | grep -q 'ID_VENDOR_ID=07ca'; then
|
||||||
RULES
|
# 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 control --reload
|
||||||
sudo udevadm trigger --subsystem-match=video4linux
|
sudo udevadm trigger --subsystem-match=video4linux
|
||||||
udevadm settle
|
udevadm settle
|
||||||
|
|||||||
@ -11,7 +11,7 @@ use tracing::{info, trace};
|
|||||||
use tracing_subscriber::{fmt, EnvFilter};
|
use tracing_subscriber::{fmt, EnvFilter};
|
||||||
use udev::{MonitorBuilder};
|
use udev::{MonitorBuilder};
|
||||||
|
|
||||||
use usb_gadget::UsbGadgetManager;
|
use usb_gadget::UsbGadget;
|
||||||
use lesavka_server::{video, usb_reset};
|
use lesavka_server::{video, usb_reset};
|
||||||
|
|
||||||
use lesavka_common::lesavka::{
|
use lesavka_common::lesavka::{
|
||||||
@ -77,11 +77,11 @@ fn list_gc311_devices() -> anyhow::Result<Vec<String>> {
|
|||||||
struct Handler {
|
struct Handler {
|
||||||
kb: Arc<Mutex<tokio::fs::File>>,
|
kb: Arc<Mutex<tokio::fs::File>>,
|
||||||
ms: Arc<Mutex<tokio::fs::File>>,
|
ms: Arc<Mutex<tokio::fs::File>>,
|
||||||
gadget: UsbGadgetManager,
|
gadget: UsbGadget,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Handler {
|
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 kb = OpenOptions::new().write(true).open("/dev/hidg0").await?;
|
||||||
let ms = OpenOptions::new().write(true)
|
let ms = OpenOptions::new().write(true)
|
||||||
.custom_flags(libc::O_NONBLOCK)
|
.custom_flags(libc::O_NONBLOCK)
|
||||||
@ -191,10 +191,10 @@ async fn main() -> anyhow::Result<()> {
|
|||||||
/* auto‑cycle task */
|
/* auto‑cycle task */
|
||||||
// tokio::spawn(async { monitor_gc311_disconnect().await.ok(); });
|
// tokio::spawn(async { monitor_gc311_disconnect().await.ok(); });
|
||||||
|
|
||||||
let gadget = UsbGadgetManager::new("lesavka");
|
let gadget = UsbGadget::new("lesavka");
|
||||||
gadget.cycle().ok();
|
gadget.cycle().ok();
|
||||||
|
|
||||||
let handler = Handler::make(gadget.clone())?;
|
let handler = Handler::make(gadget.clone()).await?;
|
||||||
|
|
||||||
tokio::spawn({
|
tokio::spawn({
|
||||||
let gadget = gadget.clone();
|
let gadget = gadget.clone();
|
||||||
|
|||||||
@ -7,32 +7,28 @@ pub struct UsbGadget {
|
|||||||
udc_file: &'static str,
|
udc_file: &'static str,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UsbGadgetManager {
|
impl UsbGadget {
|
||||||
pub fn new(gadget_name: &'static str) -> Self {
|
pub fn new(name: &'static str) -> Self {
|
||||||
// /sys/kernel/config/usb_gadget/<name>/UDC
|
// /sys/kernel/config/usb_gadget/<name>/UDC
|
||||||
Self { udc_file: Box::leak(
|
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.
|
/// Force the host to re‑enumerate our HID gadget.
|
||||||
pub fn cycle(&self) -> Result<()> {
|
pub fn cycle(&self) -> Result<()> {
|
||||||
// 1. detach
|
|
||||||
info!("UDC‑cycle: detaching gadget");
|
info!("UDC‑cycle: detaching gadget");
|
||||||
OpenOptions::new().write(true).open(self.udc_file)?
|
OpenOptions::new().write(true).open(self.udc_file)?.write_all(b"")?;
|
||||||
.write_all(b"")?;
|
thread::sleep(Duration::from_millis(200));
|
||||||
|
|
||||||
// 2. wait ≥ 100 ms so host sees a disconnect
|
let udc_name = std::fs::read_dir("/sys/class/udc")?
|
||||||
std::thread::sleep(Duration::from_millis(200));
|
.next()
|
||||||
|
.transpose()?
|
||||||
// 3. re‑attach to **first** UDC (dwc2)
|
.context("no UDC present")?
|
||||||
let udc = std::fs::read_dir("/sys/class/udc")?
|
|
||||||
.next().context("no UDC present")?
|
|
||||||
.file_name();
|
.file_name();
|
||||||
let name = udc.to_string_lossy();
|
|
||||||
info!("UDC‑cycle: re‑attaching to {name}");
|
|
||||||
OpenOptions::new().write(true).open(self.udc_file)?
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user