| // Copyright 2018 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #![deny(warnings)] |
| #![feature(async_await, await_macro, futures_api)] |
| |
| use { |
| crate::client::QmiClient, |
| crate::errors::QmuxError, |
| crate::transport::QmiTransport, |
| failure::{Error, ResultExt}, |
| fidl::endpoints::{ClientEnd, RequestStream, ServerEnd, ServiceMarker}, |
| fidl_fuchsia_telephony_ril::*, |
| fuchsia_app::server::ServicesServer, |
| fuchsia_async as fasync, |
| fuchsia_syslog::{self as syslog, macros::*}, |
| fuchsia_zircon as zx, |
| futures::lock::Mutex, |
| futures::{TryFutureExt, TryStreamExt}, |
| qmi_protocol::QmiResult, |
| qmi_protocol::*, |
| std::sync::Arc, |
| }; |
| |
| mod client; |
| mod errors; |
| mod transport; |
| |
| type QmiModemPtr = Arc<Mutex<QmiModem>>; |
| |
| pub struct QmiModem { |
| inner: Option<Arc<QmiTransport>>, |
| } |
| |
| impl QmiModem { |
| pub fn new() -> Self { |
| QmiModem { inner: None } |
| } |
| |
| pub fn new_with_transport(transport: Arc<QmiTransport>) -> Self { |
| QmiModem { |
| inner: Some(transport), |
| } |
| } |
| |
| pub fn connected(&self) -> bool { |
| // TODO add aditional logic for checking transport_channel open |
| self.inner.is_some() |
| } |
| |
| pub fn connect_transport(&mut self, chan: zx::Channel) -> bool { |
| fx_log_info!("Connecting the transport"); |
| if self.connected() { |
| fx_log_err!("Attempted to connect more than one transport"); |
| return false; |
| } |
| match fasync::Channel::from_channel(chan) { |
| Ok(chan) => { |
| if chan.is_closed() { |
| fx_log_err!("The transport channel is not open"); |
| return false; |
| } |
| self.inner = Some(Arc::new(QmiTransport::new(chan))); |
| true |
| } |
| Err(_) => { |
| fx_log_err!("Failed to convert a zircon channel to a fasync one"); |
| false |
| } |
| } |
| } |
| |
| pub async fn create_client(&self) -> Result<QmiClient, Error> { |
| fx_log_info!("Client connecting..."); |
| if let Some(ref inner) = self.inner { |
| let transport_inner = inner.clone(); |
| let client = QmiClient::new(transport_inner); |
| Ok(client) |
| } else { |
| Err(QmuxError::NoTransport.into()) |
| } |
| } |
| } |
| |
| type ClientPtr = Arc<Mutex<Option<QmiClient>>>; |
| |
| /// needs to be called before all requests that use a client |
| async fn setup_client<'a>(modem: QmiModemPtr, client_ptr: ClientPtr) { |
| let mut client_lock = await!(client_ptr.lock()); |
| if client_lock.is_none() { |
| let modem_lock = await!(modem.lock()); |
| match await!(modem_lock.create_client()) { |
| Ok(alloced_client) => *client_lock = Some(alloced_client), |
| Err(e) => { |
| fx_log_err!("Failed to allocated a client: {}", e); |
| } |
| } |
| }; |
| } |
| |
| struct FrilService; |
| impl FrilService { |
| pub fn spawn(modem: QmiModemPtr, chan: fasync::Channel) { |
| let client = Arc::new(Mutex::new(None)); |
| let server = RadioInterfaceLayerRequestStream::from_channel(chan) |
| .try_for_each(move |req| Self::handle_request(modem.clone(), client.clone(), req)) |
| .unwrap_or_else(|e| fx_log_err!("Error running {:?}", e)); |
| fasync::spawn(server); |
| } |
| |
| async fn handle_request( |
| modem: QmiModemPtr, client_ptr: ClientPtr, request: RadioInterfaceLayerRequest, |
| ) -> Result<(), fidl::Error> { |
| // TODO(bwb) after component model v2, switch to on channel setup and |
| // deprecated ConnectTransport method |
| match request { |
| RadioInterfaceLayerRequest::ConnectTransport { .. } => (), // does not need a client setup |
| _ => await!(setup_client(modem.clone(), client_ptr.clone())), |
| } |
| |
| match request { |
| RadioInterfaceLayerRequest::ConnectTransport { channel, responder } => { |
| let mut lock = await!(modem.lock()); |
| let status = lock.connect_transport(channel); |
| fx_log_info!("Connecting the service to the transport driver: {}", status); |
| if let Ok(client) = await!(lock.create_client()) { |
| let resp802: QmiResult<WDA::SetDataFormatResp> = |
| await!(client.send_msg(WDA::SetDataFormatReq::new(None, Some(0x01)))) |
| .unwrap(); |
| fx_log_info!("802 Resp: {:?}", resp802); |
| } |
| return responder.send(status); |
| } |
| RadioInterfaceLayerRequest::GetNetworkSettings { responder } => { |
| match *await!(client_ptr.lock()) { |
| Some(ref mut client) => { |
| // TODO find out how to structure this u32 in a readable way |
| let resp: QmiResult<WDS::GetCurrentSettingsResp> = |
| await!(client.send_msg(WDS::GetCurrentSettingsReq::new(58160))) |
| .unwrap(); |
| match resp { |
| Ok(packet) => responder.send( |
| &mut GetNetworkSettingsReturn::Settings(NetworkSettings { |
| ip_v4_addr: packet.ipv4_addr.unwrap(), |
| ip_v4_dns: packet.ipv4_dns.unwrap(), |
| ip_v4_subnet: packet.ipv4_subnet.unwrap(), |
| ip_v4_gateway: packet.ipv4_gateway.unwrap(), |
| mtu: packet.mtu.unwrap(), |
| }), |
| )?, |
| Err(e) => { |
| fx_log_err!("Error network: {:?}", e); |
| // TODO different error |
| responder |
| .send(&mut GetNetworkSettingsReturn::Error(RilError::NoRadio))? |
| } |
| } |
| } |
| None => { |
| responder.send(&mut GetNetworkSettingsReturn::Error(RilError::NoRadio))? |
| } |
| } |
| } |
| RadioInterfaceLayerRequest::StartNetwork { apn, responder } => { |
| match *await!(client_ptr.lock()) { |
| Some(ref mut client) => { |
| let resp: QmiResult<WDS::StartNetworkInterfaceResp> = |
| await!(client |
| .send_msg(WDS::StartNetworkInterfaceReq::new(Some(apn), Some(4)))) |
| .unwrap(); |
| match resp { |
| Ok(packet) => { |
| let (server_chan, client_chan) = zx::Channel::create().unwrap(); |
| let server_end = |
| ServerEnd::<NetworkConnectionMarker>::new(server_chan.into()); |
| client.data_conn = Some(client::Connection { |
| pkt_handle: packet.packet_data_handle, |
| conn: server_end, |
| }); |
| let client_end = |
| ClientEnd::<NetworkConnectionMarker>::new(client_chan.into()); |
| responder.send(&mut StartNetworkReturn::Conn(client_end))? |
| } |
| Err(e) => { |
| fx_log_info!("error network: {:?}", e); |
| // TODO different error |
| responder.send(&mut StartNetworkReturn::Error(RilError::NoRadio))? |
| } |
| } |
| } |
| None => responder.send(&mut StartNetworkReturn::Error(RilError::NoRadio))?, |
| } |
| } |
| RadioInterfaceLayerRequest::GetDeviceIdentity { responder } => { |
| match *await!(client_ptr.lock()) { |
| Some(ref mut client) => { |
| let resp: QmiResult< |
| DMS::GetDeviceSerialNumbersResp, |
| > = await!(client.send_msg(DMS::GetDeviceSerialNumbersReq::new())).unwrap(); |
| responder.send(&mut GetDeviceIdentityReturn::Imei(resp.unwrap().imei))? |
| } |
| None => { |
| responder.send(&mut GetDeviceIdentityReturn::Error(RilError::NoRadio))? |
| } |
| } |
| } |
| RadioInterfaceLayerRequest::RadioPowerStatus { responder } => { |
| match *await!(client_ptr.lock()) { |
| Some(ref mut client) => { |
| let resp: DMS::GetOperatingModeResp = |
| await!(client.send_msg(DMS::GetOperatingModeReq::new())) |
| .unwrap() |
| .unwrap(); |
| if resp.operating_mode == 0x00 { |
| responder |
| .send(&mut RadioPowerStatusReturn::Result(RadioPowerState::On))? |
| } else { |
| responder |
| .send(&mut RadioPowerStatusReturn::Result(RadioPowerState::Off))? |
| } |
| } |
| None => { |
| responder.send(&mut RadioPowerStatusReturn::Error(RilError::NoRadio))? |
| } |
| } |
| } |
| } |
| Ok(()) |
| } |
| } |
| |
| fn main() -> Result<(), Error> { |
| syslog::init_with_tags(&["ril-qmi"]).expect("Can't init logger"); |
| fx_log_info!("Starting ril-qmi..."); |
| |
| let mut executor = fasync::Executor::new().context("Error creating executor")?; |
| |
| let modem = Arc::new(Mutex::new(QmiModem::new())); |
| |
| let server = ServicesServer::new() |
| .add_service(( |
| RadioInterfaceLayerMarker::NAME, |
| move |chan: fasync::Channel| { |
| fx_log_info!("New client connecting to the Fuchsia RIL"); |
| FrilService::spawn(modem.clone(), chan) |
| }, |
| )) |
| .start() |
| .context("Error starting QMI modem service")?; |
| |
| executor.run_singlethreaded(server) |
| } |