Skip to main content

linux_info/network/
modem_manager.rs

1//! Connect to the ModemManager
2
3use std::sync::Arc;
4use std::time::Duration;
5
6use dbus::arg::{PropMap, RefArg};
7use dbus::blocking::stdintf::org_freedesktop_dbus::ObjectManager;
8use dbus::blocking::{Connection, Proxy};
9use dbus::{Error, Path};
10
11use mmdbus::modem::Modem as ModemAccess;
12use mmdbus::modem_modem3gpp::ModemModem3gpp;
13use mmdbus::modem_signal::ModemSignal;
14use mmdbus::sim::Sim as SimTrait;
15
16const DBUS_NAME: &str = "org.freedesktop.ModemManager1";
17const DBUS_PATH: &str = "/org/freedesktop/ModemManager1";
18const TIMEOUT: Duration = Duration::from_secs(2);
19
20#[derive(Clone)]
21struct Dbus {
22	conn: Arc<Connection>,
23}
24
25impl Dbus {
26	fn connect() -> Result<Self, Error> {
27		Connection::new_system()
28			.map(Arc::new)
29			.map(|conn| Self { conn })
30	}
31
32	fn proxy<'a, 'b>(
33		&'b self,
34		path: impl Into<Path<'a>>,
35	) -> Proxy<'a, &'b Connection> {
36		self.conn.with_proxy(DBUS_NAME, path, TIMEOUT)
37	}
38}
39
40#[derive(Clone)]
41pub struct ModemManager {
42	dbus: Dbus,
43}
44
45impl ModemManager {
46	pub fn connect() -> Result<Self, Error> {
47		Dbus::connect().map(|dbus| Self { dbus })
48	}
49
50	pub fn modems(&self) -> Result<Vec<Modem>, Error> {
51		let objects = self.dbus.proxy(DBUS_PATH).get_managed_objects()?;
52		let modems = objects
53			.into_iter()
54			.map(|(path, _)| Modem {
55				dbus: self.dbus.clone(),
56				path,
57			})
58			.collect();
59
60		Ok(modems)
61	}
62}
63
64pub struct Modem {
65	dbus: Dbus,
66	path: Path<'static>,
67}
68
69impl Modem {
70	/// The equipment manufacturer, as reported by the modem.
71	pub fn manufacturer(&self) -> Result<String, Error> {
72		self.dbus.proxy(&self.path).manufacturer()
73	}
74
75	/// The equipment model, as reported by the modem.
76	pub fn model(&self) -> Result<String, Error> {
77		self.dbus.proxy(&self.path).model()
78	}
79
80	/// The description of the carrier-specific configuration (MCFG) in use by
81	/// the modem.
82	pub fn carrier_configuration(&self) -> Result<String, Error> {
83		self.dbus.proxy(&self.path).carrier_configuration()
84	}
85
86	/// The physical modem device reference (ie, USB, PCI, PCMCIA device),
87	/// which may be dependent upon the operating system.
88	///
89	/// In Linux for example, this points to a sysfs path of the usb_device
90	/// object.
91	///
92	/// This value may also be set by the user using the MM_ID_PHYSDEV_UID udev
93	/// tag (e.g. binding the tag to a specific sysfs path).
94	pub fn device(&self) -> Result<String, Error> {
95		self.dbus.proxy(&self.path).device()
96	}
97
98	/// Overall state of the modem, given as a MMModemState value.
99	///
100	/// If the device's state cannot be determined, MM_MODEM_STATE_UNKNOWN will
101	/// be reported.
102	pub fn state(&self) -> Result<ModemState, Error> {
103		self.dbus.proxy(&self.path).state().map(Into::into)
104	}
105
106	/// The current network access technologies used by the device to
107	/// communicate with the network.
108	///
109	/// If the device's access technology cannot be determined, Unknown will be
110	/// reported.
111	pub fn access_techs(&self) -> Result<ModemAccessTechs, Error> {
112		self.dbus
113			.proxy(&self.path)
114			.access_technologies()
115			.map(Into::into)
116	}
117
118	/// Signal quality in percent (0 - 100) of the dominant access technology
119	/// the device is using to communicate with the network. Always 0 for
120	/// POTS devices.
121	/// The additional boolean value indicates if the quality value given was
122	/// recently taken.
123	pub fn signal_quality(&self) -> Result<(u32, bool), Error> {
124		self.dbus.proxy(&self.path).signal_quality()
125	}
126
127	/// This property exposes the supported mode combinations, given as an array
128	/// of unsigned integer pairs, where:
129	///
130	/// The first integer is a bitmask of MMModemMode values, specifying the
131	/// allowed modes.
132	///
133	/// The second integer is a single MMModemMode, which specifies the
134	/// preferred access technology, among the ones defined in the allowed
135	/// modes.
136	pub fn supported_modes(
137		&self,
138	) -> Result<Vec<(ModemMode, ModemMode)>, Error> {
139		self.dbus
140			.proxy(&self.path)
141			.supported_modes()
142			.map(|v| v.into_iter().map(|(a, b)| (a.into(), b.into())).collect())
143	}
144
145	/// A pair of MMModemMode values, where the first one is a bitmask
146	/// specifying the access technologies (eg 2G/3G/4G) the device is
147	/// currently allowed to use when connecting to a network, and the second
148	/// one is the preferred mode of those specified as allowed.
149	pub fn current_modes(&self) -> Result<(ModemMode, ModemMode), Error> {
150		self.dbus
151			.proxy(&self.path)
152			.current_modes()
153			.map(|(a, b)| (a.into(), b.into()))
154	}
155
156	/// Set the access technologies (e.g. 2G/3G/4G preference) the device is
157	/// currently allowed to use when connecting to a network.
158	///
159	/// The given combination should be supported by the modem, as specified
160	/// in the "SupportedModes" property.
161	///
162	/// A pair of MMModemMode values, where the first one is a bitmask of
163	/// allowed modes, and the second one the preferred mode, if any.
164	pub fn set_current_modes(
165		&self,
166		(allowed, preferred): (ModemMode, ModemMode),
167	) -> Result<(), Error> {
168		self.dbus
169			.proxy(&self.path)
170			.set_current_modes((allowed.into(), preferred.into()))
171	}
172
173	///  List of MMModemBand values, specifying the radio frequency and
174	/// technology bands supported by the device.
175	///
176	/// For POTS devices, only the MM_MODEM_BAND_ANY mode will be returned.
177	pub fn supported_bands(&self) -> Result<Vec<ModemBand>, Error> {
178		self.dbus
179			.proxy(&self.path)
180			.supported_bands()
181			.map(|v| v.into_iter().map(Into::into).collect())
182	}
183
184	/// List of MMModemBand values, specifying the radio frequency and
185	/// technology bands the device is currently using when connecting to a
186	/// network.
187	///
188	/// It must be a subset of "SupportedBands".
189	pub fn current_bands(&self) -> Result<Vec<ModemBand>, Error> {
190		self.dbus
191			.proxy(&self.path)
192			.current_bands()
193			.map(|v| v.into_iter().map(Into::into).collect())
194	}
195
196	/// Set the radio frequency and technology bands the device is currently
197	/// allowed to use when connecting to a network.
198	///
199	/// List of MMModemBand values, to specify the bands to be used.
200	pub fn set_current_bands(&self, bands: &[ModemBand]) -> Result<(), Error> {
201		self.dbus
202			.proxy(&self.path)
203			.set_current_bands(bands.into_iter().map(|b| *b as u32).collect())
204	}
205
206	pub fn signal_rate(&self) -> Result<u32, Error> {
207		self.dbus.proxy(&self.path).rate()
208	}
209
210	pub fn signal_setup(&self, rate: u32) -> Result<(), Error> {
211		self.dbus.proxy(&self.path).setup(rate)
212	}
213
214	/// Available signal information for the CDMA1x access technology.
215	pub fn signal_cdma(&self) -> Result<SignalCdma, Error> {
216		let data = self.dbus.proxy(&self.path).cdma()?;
217		SignalCdma::from_prop_map(data)
218			.ok_or_else(|| Error::new_failed("cdma not found"))
219	}
220
221	/// Available signal information for the CDMA EV-DO access technology.
222	pub fn signal_evdo(&self) -> Result<SignalEvdo, Error> {
223		let data = self.dbus.proxy(&self.path).evdo()?;
224		SignalEvdo::from_prop_map(data)
225			.ok_or_else(|| Error::new_failed("evdo not found"))
226	}
227
228	/// Available signal information for the GSM/GPRS access technology.
229	pub fn signal_gsm(&self) -> Result<SignalGsm, Error> {
230		let data = self.dbus.proxy(&self.path).gsm()?;
231		SignalGsm::from_prop_map(data)
232			.ok_or_else(|| Error::new_failed("gsm not found"))
233	}
234
235	/// Available signal information for the UMTS (WCDMA) access technology.
236	pub fn signal_umts(&self) -> Result<SignalUmts, Error> {
237		let data = self.dbus.proxy(&self.path).umts()?;
238		SignalUmts::from_prop_map(data)
239			.ok_or_else(|| Error::new_failed("umts not found"))
240	}
241
242	/// Available signal information for the LTE access technology.
243	pub fn signal_lte(&self) -> Result<SignalLte, Error> {
244		let data = self.dbus.proxy(&self.path).lte()?;
245		SignalLte::from_prop_map(data)
246			.ok_or_else(|| Error::new_failed("lte not found"))
247	}
248
249	/// Available signal information for the 5G access technology.
250	pub fn signal_nr5g(&self) -> Result<SignalNr5g, Error> {
251		let data = self.dbus.proxy(&self.path).nr5g()?;
252		SignalNr5g::from_prop_map(data)
253			.ok_or_else(|| Error::new_failed("nr5g not found"))
254	}
255
256	/// List of numbers (e.g. MSISDN in 3GPP) being currently handled by this
257	/// modem.
258	pub fn own_numbers(&self) -> Result<Vec<String>, Error> {
259		self.dbus.proxy(&self.path).own_numbers()
260	}
261
262	/// The IMEI of the device.
263	///
264	/// ## Note
265	/// This interface will only be available once the modem is ready to be
266	/// registered in the cellular network. 3GPP devices will require a valid
267	/// unlocked SIM card before any of the features in the interface can be
268	/// used.
269	pub fn imei(&self) -> Result<String, Error> {
270		self.dbus.proxy(&self.path).imei()
271	}
272
273	/// A MMModem3gppRegistrationState value specifying the mobile
274	/// registration status as defined in 3GPP TS 27.007 section 10.1.19.
275	///
276	/// ## Note
277	/// This interface will only be available once the modem is ready to be
278	/// registered in the cellular network. 3GPP devices will require a valid
279	/// unlocked SIM card before any of the features in the interface can be
280	/// used.
281	pub fn registration_state(&self) -> Result<RegistrationState, Error> {
282		ModemModem3gpp::registration_state(&self.dbus.proxy(&self.path))
283			.map(Into::into)
284	}
285
286	///  Code of the operator to which the mobile is currently registered.
287	///
288	/// Returned in the format "MCCMNC", where MCC is the three-digit ITU
289	/// E.212 Mobile Country Code and MNC is the two- or three-digit GSM
290	/// Mobile Network Code. e.g. e"31026" or "310260".
291	///
292	/// If the MCC and MNC are not known or the mobile is not registered
293	/// to a mobile network, this property will be a zero-length (blank)
294	/// string.
295	///
296	/// ## Note
297	/// This interface will only be available once the modem is ready to be
298	/// registered in the cellular network. 3GPP devices will require a valid
299	/// unlocked SIM card before any of the features in the interface can be
300	/// used.
301	pub fn operator_code(&self) -> Result<String, Error> {
302		ModemModem3gpp::operator_code(&self.dbus.proxy(&self.path))
303	}
304
305	/// Name of the operator to which the mobile is currently registered.
306	///
307	/// If the operator name is not known or the mobile is not registered to a
308	/// mobile network, this property will be an empty string.
309	///
310	/// ## Note
311	/// This interface will only be available once the modem is ready to be
312	/// registered in the cellular network. 3GPP devices will require a valid
313	/// unlocked SIM card before any of the features in the interface can be
314	/// used.
315	pub fn operator_name(&self) -> Result<String, Error> {
316		ModemModem3gpp::operator_name(&self.dbus.proxy(&self.path))
317	}
318
319	/// This SIM object is the one used for network registration and data
320	/// connection setup.
321	pub fn sim(&self) -> Result<Sim, Error> {
322		Ok(Sim {
323			path: self.dbus.proxy(&self.path).sim()?,
324			dbus: self.dbus.clone(),
325		})
326	}
327}
328
329pub struct Sim {
330	dbus: Dbus,
331	path: Path<'static>,
332}
333
334impl Sim {
335	/// The ICCID of the SIM card.
336	///
337	/// This may be available before the PIN has been entered depending on the
338	/// device itself.
339	pub fn identifier(&self) -> Result<String, Error> {
340		self.dbus.proxy(&self.path).sim_identifier()
341	}
342
343	/// The IMSI of the SIM card, if any.
344	pub fn imsi(&self) -> Result<String, Error> {
345		self.dbus.proxy(&self.path).imsi()
346	}
347
348	/// The EID of the SIM card, if any.
349	pub fn eid(&self) -> Result<String, Error> {
350		self.dbus.proxy(&self.path).eid()
351	}
352
353	/// The name of the network operator, as given by the SIM card, if known.
354	pub fn operator_name(&self) -> Result<String, Error> {
355		SimTrait::operator_name(&self.dbus.proxy(&self.path))
356	}
357}
358
359#[repr(i32)]
360#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
361#[cfg_attr(
362	feature = "serde",
363	derive(serde1::Serialize, serde1::Deserialize),
364	serde(crate = "serde1")
365)]
366#[non_exhaustive]
367pub enum ModemState {
368	/// The modem is unusable.
369	Failed = -1,
370	/// State unknown or not reportable.
371	Unknown = 0,
372	/// The modem is currently being initialized.
373	Initializing = 1,
374	/// The modem needs to be unlocked.
375	Locked = 2,
376	/// The modem is not enabled and is powered down.
377	Disabled = 3,
378	/// The modem is currently transitioning to the MM_MODEM_STATE_DISABLED
379	/// state.
380	Disabling = 4,
381	/// The modem is currently transitioning to the MM_MODEM_STATE_ENABLED
382	/// state.
383	Enabling = 5,
384	/// The modem is enabled and powered on but not registered with a network
385	/// provider and not available for data connections.
386	Enabled = 6,
387	/// The modem is searching for a network provider to register with.
388	Searching = 7,
389	/// The modem is registered with a network provider, and data connections
390	/// and messaging may be available for use.
391	Registered = 8,
392	/// The modem is disconnecting and deactivating the last active packet data
393	/// bearer. This state will not be entered if more than one packet data
394	/// bearer is active and one of the active bearers is deactivated.
395	Disconnecting = 9,
396	/// The modem is activating and connecting the first packet data bearer.
397	/// Subsequent bearer activations when another bearer is already active
398	/// do not cause this state to be entered.
399	Connecting = 10,
400	/// One or more packet data bearers is active and connected.
401	Connected = 11,
402}
403
404impl From<i32> for ModemState {
405	fn from(num: i32) -> Self {
406		if num < -1 || num > 11 {
407			Self::Unknown
408		} else {
409			unsafe { *(&num as *const i32 as *const Self) }
410		}
411	}
412}
413
414#[repr(u32)]
415#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
416#[cfg_attr(
417	feature = "serde",
418	derive(serde1::Serialize, serde1::Deserialize),
419	serde(crate = "serde1")
420)]
421#[non_exhaustive]
422/// Describes various access technologies that a device uses when registered
423/// with or connected to a network.
424pub enum ModemAccessTech {
425	/// The access technology used is unknown.
426	Unknown = 0,
427	/// Analog wireline telephone.
428	Pots = 1 << 0,
429	/// GSM.
430	Gsm = 1 << 1,
431	/// Compact GSM.
432	GsmCompact = 1 << 2,
433	/// GPRS.
434	Gprs = 1 << 3,
435	/// EDGE (ETSI 27.007: "GSM w/EGPRS").
436	Edge = 1 << 4,
437	/// UMTS (ETSI 27.007: "UTRAN").
438	Umts = 1 << 5,
439	/// HSDPA (ETSI 27.007: "UTRAN w/HSDPA").
440	Hsdpa = 1 << 6,
441	/// HSUPA (ETSI 27.007: "UTRAN w/HSUPA").
442	Hsupa = 1 << 7,
443	/// HSPA (ETSI 27.007: "UTRAN w/HSDPA and HSUPA").
444	Hspa = 1 << 8,
445	/// HSPA+ (ETSI 27.007: "UTRAN w/HSPA+").
446	HspaPlus = 1 << 9,
447	/// CDMA2000 1xRTT.
448	T1xRtt = 1 << 10,
449	/// CDMA2000 EVDO revision 0.
450	Evdo0 = 1 << 11,
451	/// CDMA2000 EVDO revision A.
452	EvdoA = 1 << 12,
453	/// CDMA2000 EVDO revision B.
454	EvdoB = 1 << 13,
455	/// LTE (ETSI 27.007: "E-UTRAN")
456	Lte = 1 << 14,
457	/// 5GNR (ETSI 27.007: "NG-RAN"). Since 1.14.
458	T5Gnr = 1 << 15,
459	/// Cat-M (ETSI 23.401: LTE Category M1/M2). Since 1.20.
460	LteCatM = 1 << 16,
461	/// NB IoT (ETSI 23.401: LTE Category NB1/NB2). Since 1.20.
462	LteNbIoT = 1 << 17,
463	/// Mask specifying all access technologies.
464	Any = u32::MAX,
465}
466
467impl ModemAccessTech {
468	/// All access technologies except Unknown and Any
469	const ALL: &'static [ModemAccessTech] = &[
470		ModemAccessTech::Pots,
471		ModemAccessTech::Gsm,
472		ModemAccessTech::GsmCompact,
473		ModemAccessTech::Gprs,
474		ModemAccessTech::Edge,
475		ModemAccessTech::Umts,
476		ModemAccessTech::Hsdpa,
477		ModemAccessTech::Hsupa,
478		ModemAccessTech::Hspa,
479		ModemAccessTech::HspaPlus,
480		ModemAccessTech::T1xRtt,
481		ModemAccessTech::Evdo0,
482		ModemAccessTech::EvdoA,
483		ModemAccessTech::EvdoB,
484		ModemAccessTech::Lte,
485		ModemAccessTech::T5Gnr,
486		ModemAccessTech::LteCatM,
487		ModemAccessTech::LteNbIoT,
488	];
489}
490
491/// A list of modem Access Technologies
492#[derive(Debug, Clone, Copy, PartialEq, Eq)]
493pub struct ModemAccessTechs(u32);
494
495impl ModemAccessTechs {
496	/// Returns true if the access technology is unkwon
497	pub fn is_unknown(&self) -> bool {
498		self.0 == ModemAccessTech::Unknown as u32
499	}
500
501	/// Returns true if the access technology might be anything.
502	pub fn is_any(&self) -> bool {
503		self.0 == ModemAccessTech::Any as u32
504	}
505
506	pub fn iter<'a>(&'a self) -> impl Iterator<Item = ModemAccessTech> + 'a {
507		let is_unknown = self.is_unknown();
508		let is_any = self.is_any();
509		let allow_others = !is_unknown && !is_any;
510
511		// types cannot be dynamic with an if
512		// so we do some hackery
513		//
514		// maybe it would be better to move everything to a new struct
515
516		let unknown_iter =
517			is_unknown.then(|| ModemAccessTech::Unknown).into_iter();
518		let any_iter = is_any.then(|| ModemAccessTech::Any).into_iter();
519
520		let other_iter = ModemAccessTech::ALL
521			.into_iter()
522			.map(|v| *v)
523			.filter(move |t| allow_others && self.0 & *t as u32 > 0);
524
525		unknown_iter.chain(any_iter).chain(other_iter)
526	}
527}
528
529impl From<u32> for ModemAccessTechs {
530	fn from(num: u32) -> Self {
531		Self(num)
532	}
533}
534
535impl From<ModemAccessTechs> for u32 {
536	fn from(a: ModemAccessTechs) -> Self {
537		a.0
538	}
539}
540
541const MODE_NONE: u32 = 0;
542/// CSD, GSM, and other circuit-switched technologies.
543const MODE_CS: u32 = 1 << 0;
544/// GPRS, EDGE.
545const MODE_2G: u32 = 1 << 1;
546/// UMTS, HSxPA.
547const MODE_3G: u32 = 1 << 2;
548/// LTE.
549const MODE_4G: u32 = 1 << 3;
550/// 5GNR
551const MODE_5G: u32 = 1 << 4;
552/// Any mode can be used (only this value allowed for POTS modems).
553const MODE_ANY: u32 = u32::MAX;
554
555// not sure if i like it this way?
556#[derive(Debug, Clone, Copy, PartialEq, Eq)]
557pub struct ModemMode(u32);
558
559impl ModemMode {
560	/// Creates a new ModemNode where no mode is allowed.
561	pub fn new() -> Self {
562		ModemMode(MODE_NONE)
563	}
564
565	/// Any Mode is allowed, only allowed for POTS modems.
566	pub fn is_any(&self) -> bool {
567		self.0 == MODE_ANY
568	}
569
570	/// Set the mode to Any.
571	pub fn set_any(&mut self) {
572		self.0 = MODE_ANY;
573	}
574
575	/// No Mode is allowed.
576	pub fn is_none(&self) -> bool {
577		self.0 == MODE_NONE
578	}
579
580	/// CSD, GSM, and other circuit-switched technologies.
581	pub fn has_cs(&self) -> bool {
582		self.0 & MODE_CS > 0
583	}
584
585	/// Sets the CS mode (CSD, GSM, and other circuit-switched technologies).
586	pub fn set_cs(&mut self) {
587		self.0 |= MODE_CS;
588	}
589
590	/// GPRS, EDGE.
591	pub fn has_2g(&self) -> bool {
592		self.0 & MODE_2G > 0
593	}
594
595	/// Sets the 2g mode (GPRS, EDGE).
596	pub fn set_2g(&mut self) {
597		self.0 |= MODE_2G;
598	}
599
600	/// UMTS, HSxPA.
601	pub fn has_3g(&self) -> bool {
602		self.0 & MODE_3G > 0
603	}
604
605	/// Sets the 3g mode (UMTS, HSxPA).
606	pub fn set_3g(&mut self) {
607		self.0 |= MODE_3G;
608	}
609
610	/// LTE.
611	pub fn has_4g(&self) -> bool {
612		self.0 & MODE_4G > 0
613	}
614
615	/// Sets the 4g mode (LTE).
616	pub fn set_4g(&mut self) {
617		self.0 |= MODE_4G;
618	}
619
620	/// 5GNR
621	pub fn has_5g(&self) -> bool {
622		self.0 & MODE_5G > 0
623	}
624
625	/// Sets the 5g mode (5GNR).
626	pub fn set_5g(&mut self) {
627		self.0 |= MODE_5G;
628	}
629}
630
631impl From<u32> for ModemMode {
632	fn from(num: u32) -> Self {
633		Self(num)
634	}
635}
636
637impl From<ModemMode> for u32 {
638	fn from(mode: ModemMode) -> Self {
639		mode.0
640	}
641}
642
643macro_rules! modem_band {
644	($($var:ident = $expr:expr),*) => (
645		#[repr(u32)]
646		#[derive(Debug, Clone, Copy, PartialEq, Eq)]
647		#[cfg_attr(
648			feature = "serde",
649			derive(serde1::Serialize, serde1::Deserialize),
650			serde(crate = "serde1")
651		)]
652		#[non_exhaustive]
653		pub enum ModemBand {
654			$($var = $expr),*
655		}
656
657		impl From<u32> for ModemBand {
658			fn from(num: u32) -> Self {
659				match num {
660					$($expr => Self::$var),*,
661					_ => Self::Unknown
662				}
663			}
664		}
665
666		impl From<ModemBand> for u32 {
667			fn from(b: ModemBand) -> Self {
668				b as u32
669			}
670		}
671	)
672}
673
674modem_band! {
675	Unknown = 0,
676	/* GSM/UMTS bands */
677	Egsm = 1,
678	Dcs = 2,
679	Pcs = 3,
680	G850 = 4,
681	Utran1 = 5,
682	Utran3 = 6,
683	Utran4 = 7,
684	Utran6 = 8,
685	Utran5 = 9,
686	Utran8 = 10,
687	Utran9 = 11,
688	Utran2 = 12,
689	Utran7 = 13,
690	G450 = 14,
691	G480 = 15,
692	G750 = 16,
693	G380 = 17,
694	G410 = 18,
695	G710 = 19,
696	G810 = 20,
697	/* LTE bands */
698	Eutran1 = 31,
699	Eutran2 = 32,
700	Eutran3 = 33,
701	Eutran4 = 34,
702	Eutran5 = 35,
703	Eutran6 = 36,
704	Eutran7 = 37,
705	Eutran8 = 38,
706	Eutran9 = 39,
707	Eutran10 = 40,
708	Eutran11 = 41,
709	Eutran12 = 42,
710	Eutran13 = 43,
711	Eutran14 = 44,
712	Eutran17 = 47,
713	Eutran18 = 48,
714	Eutran19 = 49,
715	Eutran20 = 50,
716	Eutran21 = 51,
717	Eutran22 = 52,
718	Eutran23 = 53,
719	Eutran24 = 54,
720	Eutran25 = 55,
721	Eutran26 = 56,
722	Eutran27 = 57,
723	Eutran28 = 58,
724	Eutran29 = 59,
725	Eutran30 = 60,
726	Eutran31 = 61,
727	Eutran32 = 62,
728	Eutran33 = 63,
729	Eutran34 = 64,
730	Eutran35 = 65,
731	Eutran36 = 66,
732	Eutran37 = 67,
733	Eutran38 = 68,
734	Eutran39 = 69,
735	Eutran40 = 70,
736	Eutran41 = 71,
737	Eutran42 = 72,
738	Eutran43 = 73,
739	Eutran44 = 74,
740	Eutran45 = 75,
741	Eutran46 = 76,
742	Eutran47 = 77,
743	Eutran48 = 78,
744	Eutran49 = 79,
745	Eutran50 = 80,
746	Eutran51 = 81,
747	Eutran52 = 82,
748	Eutran53 = 83,
749	Eutran54 = 84,
750	Eutran55 = 85,
751	Eutran56 = 86,
752	Eutran57 = 87,
753	Eutran58 = 88,
754	Eutran59 = 89,
755	Eutran60 = 90,
756	Eutran61 = 91,
757	Eutran62 = 92,
758	Eutran63 = 93,
759	Eutran64 = 94,
760	Eutran65 = 95,
761	Eutran66 = 96,
762	Eutran67 = 97,
763	Eutran68 = 98,
764	Eutran69 = 99,
765	Eutran70 = 100,
766	Eutran71 = 101,
767	/* CDMA Band Classes (see 3GPP2 C.S0057-C) */
768	CdmaBc0 = 128,
769	CdmaBc1 = 129,
770	CdmaBc2 = 130,
771	CdmaBc3 = 131,
772	CdmaBc4 = 132,
773	CdmaBc5 = 134,
774	CdmaBc6 = 135,
775	CdmaBc7 = 136,
776	CdmaBc8 = 137,
777	CdmaBc9 = 138,
778	CdmaBc10 = 139,
779	CdmaBc11 = 140,
780	CdmaBc12 = 141,
781	CdmaBc13 = 142,
782	CdmaBc14 = 143,
783	CdmaBc15 = 144,
784	CdmaBc16 = 145,
785	CdmaBc17 = 146,
786	CdmaBc18 = 147,
787	CdmaBc19 = 148,
788	/* Additional UMTS bands:
789	*  15-18 reserved
790	*  23-24 reserved
791	*  27-31 reserved
792	*/
793	Utran10 = 210,
794	Utran11 = 211,
795	Utran12 = 212,
796	Utran13 = 213,
797	Utran14 = 214,
798	Utran19 = 219,
799	Utran20 = 220,
800	Utran21 = 221,
801	Utran22 = 222,
802	Utran25 = 225,
803	Utran26 = 226,
804	Utran32 = 232,
805	/* All/Any */
806	Any = 256
807}
808
809#[derive(Debug, Clone, Copy, PartialEq)]
810#[cfg_attr(
811	feature = "serde",
812	derive(serde1::Serialize, serde1::Deserialize),
813	serde(crate = "serde1", rename = "camelCase")
814)]
815pub struct SignalCdma {
816	/// The CDMA1x RSSI (Received Signal Strength Indication), in dBm
817	pub rssi: f64,
818	/// The CDMA1x Ec/Io, in dBm
819	pub ecio: f64,
820}
821
822impl SignalCdma {
823	fn from_prop_map(prop: PropMap) -> Option<Self> {
824		Some(Self {
825			rssi: prop.get("rssi")?.as_f64()?,
826			ecio: prop.get("ecio")?.as_f64()?,
827		})
828	}
829}
830
831#[derive(Debug, Clone, Copy, PartialEq)]
832#[cfg_attr(
833	feature = "serde",
834	derive(serde1::Serialize, serde1::Deserialize),
835	serde(crate = "serde1", rename = "camelCase")
836)]
837pub struct SignalEvdo {
838	/// The CDMA EV-DO RSSI (Received Signal Strength Indication), in dBm
839	pub rssi: f64,
840	/// The CDMA EV-DO Ec/Io, in dBm
841	pub ecio: f64,
842	/// CDMA EV-DO SINR level, in dB
843	pub sinr: f64,
844	/// The CDMA EV-DO Io, in dBm
845	pub io: f64,
846}
847
848impl SignalEvdo {
849	fn from_prop_map(prop: PropMap) -> Option<Self> {
850		Some(Self {
851			rssi: prop.get("rssi")?.as_f64()?,
852			ecio: prop.get("ecio")?.as_f64()?,
853			sinr: prop.get("sinr")?.as_f64()?,
854			io: prop.get("io")?.as_f64()?,
855		})
856	}
857}
858
859#[derive(Debug, Clone, Copy, PartialEq)]
860#[cfg_attr(
861	feature = "serde",
862	derive(serde1::Serialize, serde1::Deserialize),
863	serde(crate = "serde1", rename = "camelCase")
864)]
865pub struct SignalGsm {
866	/// The GSM RSSI (Received Signal Strength Indication), in dBm
867	pub rssi: f64,
868}
869
870impl SignalGsm {
871	fn from_prop_map(prop: PropMap) -> Option<Self> {
872		Some(Self {
873			rssi: prop.get("rssi")?.as_f64()?,
874		})
875	}
876}
877
878#[derive(Debug, Clone, Copy, PartialEq)]
879#[cfg_attr(
880	feature = "serde",
881	derive(serde1::Serialize, serde1::Deserialize),
882	serde(crate = "serde1", rename = "camelCase")
883)]
884pub struct SignalUmts {
885	/// The UMTS RSSI (Received Signal Strength Indication), in dBm
886	pub rssi: f64,
887	/// The UMTS RSCP (Received Signal Code Power), in dBm
888	///
889	/// If zero, the value is probably missing
890	pub rscp: f64,
891	/// The UMTS Ec/Io, in dB
892	pub ecio: f64,
893}
894
895impl SignalUmts {
896	fn from_prop_map(prop: PropMap) -> Option<Self> {
897		Some(Self {
898			rssi: prop.get("rssi")?.as_f64()?,
899			// it seems in my tests rscp does not get returned
900			rscp: prop.get("rscp").and_then(|v| v.as_f64()).unwrap_or(0f64),
901			ecio: prop.get("ecio")?.as_f64()?,
902		})
903	}
904}
905
906#[derive(Debug, Clone, Copy, PartialEq)]
907#[cfg_attr(
908	feature = "serde",
909	derive(serde1::Serialize, serde1::Deserialize),
910	serde(crate = "serde1", rename = "camelCase")
911)]
912pub struct SignalLte {
913	/// The LTE RSSI (Received Signal Strength Indication), in dBm
914	pub rssi: f64,
915	/// The LTE RSRQ (Reference Signal Received Quality), in dB
916	pub rsrq: f64,
917	/// The LTE RSRP (Reference Signal Received Power), in dBm
918	pub rsrp: f64,
919	/// The LTE S/R ratio, in dB
920	pub snr: f64,
921}
922
923impl SignalLte {
924	fn from_prop_map(prop: PropMap) -> Option<Self> {
925		Some(Self {
926			rssi: prop.get("rssi")?.as_f64()?,
927			rsrq: prop.get("rsrq")?.as_f64()?,
928			rsrp: prop.get("rsrp")?.as_f64()?,
929			snr: prop.get("snr")?.as_f64()?,
930		})
931	}
932}
933
934#[derive(Debug, Clone, Copy, PartialEq)]
935#[cfg_attr(
936	feature = "serde",
937	derive(serde1::Serialize, serde1::Deserialize),
938	serde(crate = "serde1", rename = "camelCase")
939)]
940pub struct SignalNr5g {
941	pub rsrq: f64,
942	pub rsrp: f64,
943	pub snr: f64,
944}
945
946impl SignalNr5g {
947	fn from_prop_map(prop: PropMap) -> Option<Self> {
948		Some(Self {
949			rsrq: prop.get("rsrq")?.as_f64()?,
950			rsrp: prop.get("rsrp")?.as_f64()?,
951			snr: prop.get("snr")?.as_f64()?,
952		})
953	}
954}
955
956#[repr(u32)]
957#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
958#[cfg_attr(
959	feature = "serde",
960	derive(serde1::Serialize, serde1::Deserialize),
961	serde(crate = "serde1")
962)]
963#[non_exhaustive]
964pub enum RegistrationState {
965	/// Not registered, not searching for new operator to register.
966	Idle = 0,
967	/// Registered on home network.
968	Home = 1,
969	/// Not registered, searching for new operator to register with.
970	Searching = 2,
971	/// Registration denied.
972	Denied = 3,
973	/// Unknown registration status.
974	Unknown = 4,
975	/// Registered on a roaming network.
976	Roaming = 5,
977}
978
979impl From<u32> for RegistrationState {
980	fn from(num: u32) -> Self {
981		if num > 5 {
982			Self::Unknown
983		} else {
984			unsafe { *(&num as *const u32 as *const Self) }
985		}
986	}
987}