Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ Python, and never detaches the `uvcvideo` driver.
| model | USB id | status |
|-------|--------|--------|
| Insta360 Link 2 | `2e1a:4c04` | validated on real hardware |
| Insta360 Link 2 Pro | `2e1a:4c06` | validated on real hardware (detection, `tracking on`/`off`, pan/tilt/zoom; hardware test suite passes) |
| Insta360 Link 2C | `2e1a:4c03` | image controls and zoom; experimental digital framing; no motorized pan/tilt |
| Insta360 Link (original) | `2e1a:4c01` | recognised, **not tested** |

Expand Down
3 changes: 2 additions & 1 deletion contrib/99-insta360-link.rules
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
# Install: sudo cp contrib/99-insta360-link.rules /etc/udev/rules.d/
# sudo udevadm control --reload && sudo udevadm trigger
#
# Insta360 Link 2 (validated); Link 2C and original Link (recognised, untested).
# Insta360 Link 2 and Link 2 Pro (validated); Link 2C and original Link (recognised, untested).
SUBSYSTEM=="video4linux", ATTRS{idVendor}=="2e1a", ATTRS{idProduct}=="4c03", TAG+="uaccess", GROUP="video", MODE="0660"
SUBSYSTEM=="video4linux", ATTRS{idVendor}=="2e1a", ATTRS{idProduct}=="4c04", TAG+="uaccess", GROUP="video", MODE="0660"
SUBSYSTEM=="video4linux", ATTRS{idVendor}=="2e1a", ATTRS{idProduct}=="4c06", TAG+="uaccess", GROUP="video", MODE="0660"
SUBSYSTEM=="video4linux", ATTRS{idVendor}=="2e1a", ATTRS{idProduct}=="4c01", TAG+="uaccess", GROUP="video", MODE="0660"
33 changes: 29 additions & 4 deletions src/camera/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ pub const INSTA360_VID: u16 = 0x2e1a;
pub const LINK2_PID: u16 = 0x4c04;
/// USB product id of the Insta360 Link 2C.
pub const LINK2C_PID: u16 = 0x4c03;
/// USB product id of the Insta360 Link 2 Pro (validated on real hardware).
pub const LINK2_PRO_PID: u16 = 0x4c06;
/// USB product id of the original Insta360 Link (recognised, **not** tested).
pub const LINK_PID: u16 = 0x4c01;

Expand All @@ -20,6 +22,10 @@ pub enum Model {
/// Insta360 Link 2C — shares the Link 2 command protocol.
#[serde(rename = "link2c")]
Link2C,
/// Insta360 Link 2 Pro — shares the Link 2 command protocol and has a
/// motorized gimbal; hardware-validated.
#[serde(rename = "link2pro")]
Link2Pro,
/// Original Insta360 Link — recognised by VID/PID only; untested.
Link,
}
Expand All @@ -33,6 +39,7 @@ impl Model {
match pid {
LINK2_PID => Some(Model::Link2),
LINK2C_PID => Some(Model::Link2C),
LINK2_PRO_PID => Some(Model::Link2Pro),
LINK_PID => Some(Model::Link),
_ => None,
}
Expand All @@ -43,23 +50,24 @@ impl Model {
match self {
Model::Link2 => "Insta360 Link 2",
Model::Link2C => "Insta360 Link 2C",
Model::Link2Pro => "Insta360 Link 2 Pro",
Model::Link => "Insta360 Link",
}
}

/// Whether pan/tilt controls represent a physical gimbal.
pub fn has_gimbal(&self) -> bool {
matches!(self, Model::Link2 | Model::Link)
matches!(self, Model::Link2 | Model::Link2Pro | Model::Link)
}

/// Whether this model uses the Link 2 vendor command protocol.
pub fn supports_link2_protocol(&self) -> bool {
matches!(self, Model::Link2 | Model::Link2C)
matches!(self, Model::Link2 | Model::Link2C | Model::Link2Pro)
}

/// Whether this model has been validated against physical hardware.
pub fn is_tested(&self) -> bool {
matches!(self, Model::Link2)
matches!(self, Model::Link2 | Model::Link2Pro)
}
}

Expand All @@ -77,8 +85,10 @@ mod tests {
fn matches_known_ids() {
assert_eq!(Model::from_usb_ids(0x2e1a, 0x4c04), Some(Model::Link2));
assert_eq!(Model::from_usb_ids(0x2e1a, 0x4c03), Some(Model::Link2C));
assert_eq!(Model::from_usb_ids(0x2e1a, 0x4c06), Some(Model::Link2Pro));
assert_eq!(Model::from_usb_ids(0x2e1a, 0x4c01), Some(Model::Link));
assert_eq!(Model::from_usb_ids(0x046d, 0x4c03), None);
assert_eq!(Model::from_usb_ids(0x046d, 0x4c06), None);
assert_eq!(Model::from_usb_ids(0x2e1a, 0x0000), None);
assert_eq!(Model::from_usb_ids(0x046d, 0x4c04), None);
}
Expand All @@ -100,8 +110,23 @@ mod tests {
}

#[test]
fn only_link2_is_tested() {
fn link2_pro_shares_link2_protocol_and_has_gimbal() {
assert!(Model::Link2Pro.supports_link2_protocol());
assert!(Model::Link2Pro.has_gimbal());
assert!(Model::Link2Pro.is_tested());
assert_eq!(Model::Link2Pro.name(), "Insta360 Link 2 Pro");
assert_eq!(Model::Link2Pro.to_string(), "Insta360 Link 2 Pro");
assert_eq!(
serde_json::to_string(&Model::Link2Pro).unwrap(),
"\"link2pro\""
);
}

#[test]
fn only_link2_and_link2_pro_are_tested() {
assert!(Model::Link2.is_tested());
assert!(Model::Link2Pro.is_tested());
assert!(!Model::Link2C.is_tested());
assert!(!Model::Link.is_tested());
assert_eq!(Model::Link2.name(), "Insta360 Link 2");
}
Expand Down
2 changes: 1 addition & 1 deletion tests/hardware.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//! Opt-in hardware tests against a physically connected Insta360 Link 2 or 2C.
//! Opt-in hardware tests against a physically connected Insta360 Link 2, 2 Pro or 2C.
//!
//! These are never run by default:
//!
Expand Down
Loading