blob: d464ba1f0115ebe59a378b9f65f6d43b18dcf1f1 [file] [log] [blame]
// 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.
use std::pin::{Unpin, Pin};
use bytes::{BufMut, BytesMut};
use crate::transport::{ClientId, SvcId};
use crate::transport::{QmiResponse, QmiTransport};
use std::collections::HashMap;
use fuchsia_syslog::macros::*;
use qmi_protocol::QmiResult;
use crate::errors::QmuxError;
use qmi_protocol::{Decodable, Encodable};
use parking_lot::RwLock;
use std::ops::Deref;
use std::fmt::Debug;
use std::sync::Arc;
#[derive(Debug)]
pub struct ClientSvcMap(RwLock<HashMap<SvcId, ClientId>>);
impl Default for ClientSvcMap {
fn default() -> Self {
let mut m = HashMap::new();
// this allows the client to request svc -> client
m.insert(SvcId(0), ClientId(0));
ClientSvcMap(RwLock::new(m))
}
}
impl Deref for ClientSvcMap {
type Target = RwLock<HashMap<SvcId, ClientId>>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[derive(Debug)]
pub struct QmiClient {
inner: Arc<QmiTransport>,
clients: ClientSvcMap,
}
impl Unpin for QmiClient {}
impl QmiClient {
pub fn new(inner: Arc<QmiTransport>) -> Self {
QmiClient {
inner: inner,
clients: ClientSvcMap::default(),
}
}
/// Send a QMI message and allocate the client IDs for the service
/// if they have not yet been
pub async fn send_msg<'a, E: Encodable + 'a, D: Decodable + Debug>(
&'a self, msg: E,
) -> Result<QmiResult<D>, QmuxError> {
let svc_id = SvcId(msg.svc_id());
let mut request_id = false;
{
let mut map = self.clients.read();
// allocate a client id for this service
if map.get(&svc_id).is_none() {
request_id = true;
}
}
if request_id {
use qmi_protocol::CTL::{GetClientIdReq, GetClientIdResp};
fx_log_info!("allocating a client ID for service: {}", svc_id.0);
let resp: QmiResult<GetClientIdResp> = await!(self.send_msg_actual(GetClientIdReq::new(svc_id.0)))?;
let client_id_resp = resp.unwrap(); // TODO from trait for QmiError to QmuxError
let mut map = self.clients.write();
assert_eq!(client_id_resp.svc_type, svc_id.0);
map.insert(svc_id, ClientId(client_id_resp.client_id));
}
Ok(await!(self.send_msg_actual(msg))?)
}
fn get_client_id(&self, svc_id: SvcId) -> ClientId {
let clients = self.clients.read();
if let Some(id) = clients.get(&svc_id) {
return *id;
}
panic!("Precondition of calling get_client_id is to have verified an ID is allocated");
}
/// Send a QMI message without checking if a client ID has been allocated for the service
async fn send_msg_actual<'a, E: Encodable + 'a, D: Decodable + Debug>(
&'a self, msg: E,
) -> Result<QmiResult<D>, QmuxError> {
fx_log_info!("Sending a structured QMI message");
let svc_id = SvcId(msg.svc_id());
let client_id = self.get_client_id(svc_id);
let tx_id = self
.inner
.register_interest(svc_id, client_id);
let mut msg_buf = BytesMut::new();
let (payload_bytes, payload_len) = msg.to_bytes();
// QMI header
msg_buf.put_u8(0x01); // magic QMI number
// 2 bytes total length
msg_buf.put_u16_le(payload_len
+ 3 /* flags */
+ 2 /* length byte length */
// additional length is bytes not captured in the payload length
// They cannot be calculated there because multi-payload SDUs may
// exist
+ 1 /* sdu control flag */
+ msg.transaction_id_len() as u16,
);
// 1 byte control flag
msg_buf.put_u8(0x00);
// 1 byte svc flag
msg_buf.put_u8(svc_id.0);
// 1 byte client id
msg_buf.put_u8(client_id.0);
// SDU
// 1 byte control flag
msg_buf.put_u8(0x00);
// 1 or 2 byte transaction ID
match msg.transaction_id_len() {
1 => msg_buf.put_u8(tx_id.0 as u8), // we know it's one byte
2 => msg_buf.put_u16_le(tx_id.0),
_ => panic!(
"Unknown transaction ID length. Please add client support or fix the message \
definitions"
),
}
// add the payload to the buffer
msg_buf.extend(payload_bytes);
let bytes = msg_buf.freeze();
// eprintln!("byte payload {:X?}", bytes.as_ref());
if let Some(ref transport) = self.inner.transport_channel {
if transport.is_closed() {
fx_log_err!("Transport channel to modem is closed");
}
transport.write(bytes.as_ref(), &mut Vec::new()).map_err(QmuxError::ClientWrite)?
}
let resp = await!(QmiResponse {
client_id: client_id,
svc_id: svc_id,
tx_id: tx_id,
transport: Some(self.inner.clone())
})?;
// eprintln!("response {:?}", resp);
let buf = std::io::Cursor::new(resp.bytes());
let decoded = D::from_bytes(buf);
eprintln!("decoded: {:?}", decoded);
Ok(decoded)
}
}