rs-matter/matter/src/mdns.rs

118 lines
3.5 KiB
Rust
Raw Normal View History

use std::sync::{Arc, Mutex, Once};
use crate::{
error::Error,
sys::{sys_publish_service, SysMdnsService},
transport::udp::MATTER_PORT,
};
#[derive(Default)]
/// The mDNS service handler
pub struct MdnsInner {
/// Vendor ID
vid: u16,
/// Product ID
pid: u16,
/// Discriminator
discriminator: u16,
2023-01-10 13:14:20 +01:00
/// Device name
device_name: String,
}
pub struct Mdns {
inner: Mutex<MdnsInner>,
}
2023-01-10 13:14:20 +01:00
const SHORT_DISCRIMINATOR_MASK: u16 = 0xF00;
const SHORT_DISCRIMINATOR_SHIFT: u16 = 8;
static mut G_MDNS: Option<Arc<Mdns>> = None;
static INIT: Once = Once::new();
pub enum ServiceMode {
Commissioned,
Commissionable,
}
impl Mdns {
fn new() -> Self {
Self {
inner: Mutex::new(MdnsInner {
..Default::default()
}),
}
}
/// Get a handle to the globally unique mDNS instance
pub fn get() -> Result<Arc<Self>, Error> {
unsafe {
INIT.call_once(|| {
G_MDNS = Some(Arc::new(Mdns::new()));
});
Ok(G_MDNS.as_ref().ok_or(Error::Invalid)?.clone())
}
}
/// Set mDNS service specific values
/// Values like vid, pid, discriminator etc
// TODO: More things like device-type etc can be added here
2023-01-10 13:14:20 +01:00
pub fn set_values(&self, vid: u16, pid: u16, discriminator: u16, device_name: &str) {
let mut inner = self.inner.lock().unwrap();
inner.vid = vid;
inner.pid = pid;
inner.discriminator = discriminator;
2023-01-10 13:14:20 +01:00
inner.device_name = device_name.chars().take(32).collect();
}
/// Publish a mDNS service
/// name - is the service name (comma separated subtypes may follow)
/// mode - the current service mode
2023-01-10 13:14:20 +01:00
#[allow(clippy::needless_pass_by_value)]
pub fn publish_service(&self, name: &str, mode: ServiceMode) -> Result<SysMdnsService, Error> {
match mode {
ServiceMode::Commissioned => {
sys_publish_service(name, "_matter._tcp", MATTER_PORT, &[])
}
ServiceMode::Commissionable => {
let inner = self.inner.lock().unwrap();
2023-01-10 13:14:20 +01:00
let short = compute_short_discriminator(inner.discriminator);
let serv_type = format!("_matterc._udp,_S{},_L{}", short, inner.discriminator);
let str_discriminator = format!("{}", inner.discriminator);
2023-01-10 13:14:20 +01:00
let txt_kvs = [
["D", &str_discriminator],
["CM", "1"],
["DN", &inner.device_name],
["VP", &format!("{}+{}", inner.vid, inner.pid)],
["SII", "5000"], /* Sleepy Idle Interval */
["SAI", "300"], /* Sleepy Active Interval */
["T", "1"], /* TCP supported */
["PH", "33"], /* Pairing Hint */
["PI", ""], /* Pairing Instruction */
];
sys_publish_service(name, &serv_type, MATTER_PORT, &txt_kvs)
}
}
}
}
2023-01-10 13:14:20 +01:00
fn compute_short_discriminator(discriminator: u16) -> u16 {
(discriminator & SHORT_DISCRIMINATOR_MASK) >> SHORT_DISCRIMINATOR_SHIFT
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn can_compute_short_discriminator() {
let discriminator: u16 = 0b0000_1111_0000_0000;
let short = compute_short_discriminator(discriminator);
assert_eq!(short, 0b1111);
let discriminator: u16 = 840;
let short = compute_short_discriminator(discriminator);
assert_eq!(short, 3);
}
}