linux_info/network/
modem_manager.rs

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