Skip to main content

huawei_modem/cmd/
network.rs

1//! Functions for dealing with registration on a GSM network (signal quality, PIN, etc.)
2use crate::{HuaweiModem};
3use crate::at::*;
4use crate::errors::*;
5use futures::Future;
6use crate::util::HuaweiFromPrimitive;
7
8/// The current registration state of the modem (from `AT+CREG`).
9/// 
10/// Modems have to be 'registered' (i.e. connected to) a given cellular network to be able to do
11/// anything useful (text, call, etc.). Therefore, checking the registration state can be useful to
12/// figure out why your modem isn't working.
13#[repr(u8)]
14#[derive(Fail, Debug, FromPrimitive, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
15pub enum RegistrationState {
16    /// Not registered, and not searching for a new operator at present.
17    #[fail(display = "Not registered; not searching for a new operator")]
18    NotRegisteredAndDisabled = 0,
19    /// Registered, and on our 'home' network (i.e. not roaming)
20    #[fail(display = "Registered; on home network")]
21    RegisteredHome = 1,
22    /// Not registered, and searching for a new operator.
23    #[fail(display = "Not registered; searching for a new operator")]
24    NotRegisteredSearching = 2,
25    /// Registration denied.
26    #[fail(display = "Registration denied")]
27    RegistrationDenied = 3,
28    /// Registration state unknown.
29    #[fail(display = "Unknown registration state")]
30    Unknown = 4,
31    /// Reigstered, and on a 'roaming' network.
32    #[fail(display = "Registered; roaming")]
33    RegisteredRoaming = 5
34}
35impl RegistrationState {
36    /// If the `RegistrationState` is either `RegisteredHome` or `RegisteredRoaming`, returns
37    /// `true`. Otherwise, returns `false`.
38    pub fn is_registered(&self) -> bool {
39        use self::RegistrationState::*;
40
41        match *self {
42            RegisteredHome => true,
43            RegisteredRoaming => true,
44            _ => false
45        }
46    }
47}
48/// The current modem operation mode (from `AT+CFUN`).
49///
50/// Presumably, this is useful for power saving or something. Note that not all state transitions
51/// are necessarily allowed by the modem - in particular, it looks like going from offline to
52/// online is not allowed on some modems, presumably requiring a restart. Consulting your modem
53/// manual may be advisable.
54#[repr(u8)]
55#[derive(Fail, Debug, FromPrimitive, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
56pub enum ModemOperationMode {
57    /// Minimum functionality possible.
58    ///
59    /// In this mode, RF is disabled, but the SIM card is still powered.
60    #[fail(display = "Minimum functionality; disable RF but keep SIM power")]
61    MinimumFunctionality = 0,
62    /// Online mode.
63    #[fail(display = "Online mode")]
64    OnlineMode = 1,
65    /// Offline mode.
66    #[fail(display = "Offline mode")]
67    OfflineMode = 4,
68    /// FTM mode.
69    ///
70    /// I have zero clue what this is.
71    #[fail(display = "FTM mode")]
72    FtmMode = 5,
73    /// Restart the modem.
74    #[fail(display = "Restart modem")]
75    Restart = 6,
76    /// Disable RF.
77    #[fail(display = "Disable RF")]
78    DisableRf = 7
79}
80/// The PIN state of the modem (from `AT+CPIN`).
81///
82/// If the SIM is locked with a PIN, you must enter it before using the modem.
83#[repr(u8)]
84#[derive(Fail, Debug, Copy, Clone, PartialEq, Eq)]
85pub enum PinState {
86    /// Ready - not pending for any password.
87    #[fail(display = "Ready; no passwords required")]
88    Ready,
89    /// Waiting for a SIM PIN to be entered.
90    #[fail(display = "SIM PIN required")]
91    SimPin,
92    /// Waiting for a SIM PUK to be given (i.e. the SIM PIN is blocked)
93    #[fail(display = "SIM PUK required")]
94    SimPuk,
95    /// Waiting for a SIM PIN2 to be entered.
96    #[fail(display = "SIM PIN2 required")]
97    SimPin2,
98    /// Waiting for a SIM PUK2 to be given (i.e. the SIM PIN2 is blocked)
99    #[fail(display = "SIM PUK2 required")]
100    SimPuk2
101}
102impl PinState {
103    // FIXME: `crate` because this should ideally use `TryFrom` if public
104    pub(crate) fn from_string(st: &str) -> HuaweiResult<Self> {
105        let r = match st {
106            "READY" => PinState::Ready,
107            "SIM PIN" => PinState::SimPin,
108            "SIM PUK" => PinState::SimPuk,
109            "SIM PIN2" => PinState::SimPin2,
110            "SIM PUK2" => PinState::SimPuk2,
111            oth => return Err(HuaweiError::ValueOutOfRange(
112                    AtValue::Unknown(oth.into())
113            ))
114        };
115        Ok(r)
116    }
117}
118/// Get the modem's current registration state (`AT+CREG`).
119pub fn get_registration(modem: &mut HuaweiModem) -> impl Future<Item = RegistrationState, Error = HuaweiError> {
120    modem.send_raw(AtCommand::Read { param: "+CREG".into() })
121        .and_then(|pkt| {
122            let reg = pkt.extract_named_response("+CREG")?
123                .get_array()?
124                .get(1)
125                .ok_or(HuaweiError::TypeMismatch)?
126                .get_integer()?;
127            let regst = RegistrationState::from_integer(*reg)?;
128            Ok(regst)
129        })
130}
131/// Get the modem's current operation mode (`AT+CFUN`).
132pub fn get_operation_mode(modem: &mut HuaweiModem) -> impl Future<Item = ModemOperationMode, Error = HuaweiError> {
133    modem.send_raw(AtCommand::Read { param: "+CFUN".into() })
134        .and_then(|pkt| {
135            let rpl = pkt.extract_named_response("+CFUN")?
136                .get_integer()?;
137            Ok(ModemOperationMode::from_integer(*rpl)?)
138        })
139}
140/// Get the modem's current PIN state (`AT+CPIN`).
141pub fn get_pin_state(modem: &mut HuaweiModem) -> impl Future<Item = PinState, Error = HuaweiError> {
142    modem.send_raw(AtCommand::Read { param: "+CPIN".into() })
143        .and_then(|pkt| {
144            let rpl = pkt.extract_named_response("+CPIN")?
145                .get_unknown()?;
146            Ok(PinState::from_string(rpl)?)
147        })
148}
149/// Input the given `pin`, in order to unlock a locked PIN.
150pub fn input_pin(modem: &mut HuaweiModem, pin: String) -> impl Future<Item = (), Error = HuaweiError> {
151    modem.send_raw(AtCommand::Equals { 
152        param: "+CPIN".into(),
153        value: AtValue::String(pin)
154    }).and_then(|pkt| {
155        pkt.assert_ok()?;
156        Ok(())
157    })
158}
159/// Signal quality, as returned from the modem (`AT+CSQ`).
160///
161/// The exact values of this `struct` may vary based on your modem type. Consult your modem manual
162/// for more information.
163#[derive(Debug, Copy, Clone, PartialEq, Eq)]
164pub struct SignalQuality {
165    /// Recieved Signal Strength Indication (RSSI) value.
166    ///
167    /// At least on some modems, this value may have the following meanings:
168    ///
169    /// - 0 → less than or equal to -113 dBm 
170    /// - 1 → -111 dBm
171    /// - 2-30 → -109 to -53 dBm
172    /// - 31 → greater than or equal to -51 dBm
173    /// - 99 → unknown or undetectable.
174    pub rssi: u32,
175    /// Channel bit error rate, in percent.
176    ///
177    /// On some modems, this is permanently 99 (i.e. unsupported).
178    pub ber: u32
179}
180/// Get the modem's current signal quality (`AT+CSQ`).
181pub fn get_signal_quality(modem: &mut HuaweiModem) -> impl Future<Item = SignalQuality, Error = HuaweiError> {
182    modem.send_raw(AtCommand::Execute { command: "+CSQ".into() })
183        .and_then(|pkt| {
184            let rpl = pkt.extract_named_response("+CSQ")?
185                .get_array()?;
186            let rssi = rpl.get(0)
187                .ok_or(HuaweiError::TypeMismatch)?
188                .get_integer()?;
189            let ber = rpl.get(1)
190                .ok_or(HuaweiError::TypeMismatch)?
191                .get_integer()?;
192            Ok(SignalQuality { rssi: *rssi, ber: *ber })
193        })
194}