1pub use foundation_api;
5pub use worker::*;
6
7pub mod messages;
8mod worker;
9
10use messages::*;
11use server::{CheckedConn, CheckedPermissions, MessageAllowed};
12
13#[macro_export]
14macro_rules! use_api {
15 () => {
16 mod quantum_link_permissions {
17 use quantum_link::messages::*;
18 #[derive(Clone, Default, server::Permissions)]
19 #[server_name = "os/quantum-link"]
20 pub struct QuantumLinkPermissions;
21 }
22 type QuantumLinkApi = quantum_link::QuantumLinkApi<quantum_link_permissions::QuantumLinkPermissions>;
23 type QlStatus = quantum_link::QlStatus<quantum_link_permissions::QuantumLinkPermissions>;
24 };
25}
26
27#[macro_export]
28macro_rules! use_prestart_api {
29 () => {
30 mod quantum_link_prestart_permissions {
31 use quantum_link::messages::StartWithoutFilesystem;
32 #[derive(Clone, Default, server::Permissions)]
33 #[server_name = "os/ql-prestart"]
34 pub struct QuantumLinkPrestartPermissions;
35 }
36 use quantum_link_prestart_permissions::QuantumLinkPrestartPermissions;
37 };
38}
39
40#[derive(Default)]
41pub struct QuantumLinkApi<P: CheckedPermissions> {
42 conn: CheckedConn<P>,
43}
44
45impl<P: CheckedPermissions> QuantumLinkApi<P> {
46 pub fn xid_document(&self) -> Vec<u8>
48 where
49 P: MessageAllowed<GetXidDocument>,
50 {
51 self.conn.send_blocking_archive(GetXidDocument)
52 }
53
54 pub fn subscribe_restore_magic_backup<S>(&self, context: &mut server::ServerContext<S>)
55 where
56 S: server::Server + server::ArchiveEventHandler<foundation_api::backup::RestoreMagicBackupEvent>,
57 P: MessageAllowed<SubscribeRestoreMagicBackup>,
58 {
59 self.conn.subscribe_archive_infallible(SubscribeRestoreMagicBackup, context)
60 }
61
62 pub fn subscribe_firmware_fetch<S>(&self, context: &mut server::ServerContext<S>)
63 where
64 S: server::Server + server::ArchiveEventHandler<foundation_api::firmware::FirmwareFetchEvent>,
65 P: MessageAllowed<SubscribeFirmwareFetch>,
66 {
67 self.conn.subscribe_archive_infallible(SubscribeFirmwareFetch, context)
68 }
69
70 pub fn start_firmware_update(&self, chunk_offset: Option<u64>) -> Result<(), SendMessageError>
71 where
72 P: MessageAllowed<StartFirmwareUpdate>,
73 {
74 self.conn.send_blocking_archive(StartFirmwareUpdate { chunk_offset })
75 }
76}
77
78pub fn start_quantum_link_without_filesystem<P>()
79where
80 P: CheckedPermissions,
81 P: MessageAllowed<StartWithoutFilesystem>,
82{
83 let Some(conn) =
84 server::CheckedConn::<P>::try_connect_with_timeout(std::time::Duration::from_millis(2000))
85 else {
86 log::warn!("QL prestart server was not running");
87 return;
88 };
89 conn.send_blocking_scalar(StartWithoutFilesystem);
90}
91
92#[derive(Debug, Copy, Clone, thiserror::Error, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
93pub enum SendMessageError {
94 #[error("no device paired")]
95 NoDevicePaired,
96 #[error(transparent)]
97 Bluetooth(#[from] bt::BluetoothError),
98 #[error("send message cancelled")]
99 Cancelled,
100 #[error("timed out")]
101 Timeout,
102}
103
104#[derive(Debug, Clone, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
105pub enum SecurityCheckState {
106 ReceivedChallenge,
108
109 Success,
111
112 Failed,
114
115 Error,
117}
118
119#[derive(Debug, Clone, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
120pub enum PairingEvent {
121 RequestReceived,
122 PairingComplete { device_name: String, new: bool },
123 PairingFailed,
124 Disconnected,
125}
126
127#[derive(Debug, Clone, Copy, PartialEq, Eq)]
128#[repr(C)]
129pub struct ConnectionStatus {
130 pub bt_connected: bool,
131 pub ql_paired: bool,
132 pub live: bool,
133}
134
135impl server::FromScalar<1> for ConnectionStatus {
136 fn from_scalar([value]: [u32; 1]) -> Self {
137 Self { bt_connected: (value & 0x1) != 0, ql_paired: (value & 0x2) != 0, live: (value & 0x4) != 0 }
138 }
139}
140
141impl server::AsScalar<1> for ConnectionStatus {
142 fn as_scalar(&self) -> [u32; 1] {
143 let mut value = 0u32;
144 if self.bt_connected {
145 value |= 0x1;
146 }
147 if self.ql_paired {
148 value |= 0x2;
149 }
150 if self.live {
151 value |= 0x4;
152 }
153 [value]
154 }
155}