bt_avctp/avc/
mod.rs
1use fuchsia_async::{MonotonicInstant, TimeoutExt};
6use fuchsia_bluetooth::types::Channel;
7use futures::future::Ready;
8use futures::stream::FilterMap;
9use futures::{future, Stream, StreamExt};
10use log::{info, trace};
11use packet_encoding::{Decodable, Encodable};
12use zx::MonotonicDuration;
13
14#[cfg(test)]
15mod tests;
16
17mod types;
18
19use crate::avctp::{
20 Command as AvctpCommand, CommandStream as AvctpCommandStream, Header as AvctpHeader,
21 Packet as AvctpPacket, Peer as AvctpPeer,
22};
23use crate::{Error, Result};
24
25use self::types::BT_SIG_COMPANY_ID;
26
27pub use self::types::{CommandType, Header, OpCode, PacketType, ResponseType, SubunitType};
28
29pub type CommandStream = FilterMap<
30 AvctpCommandStream,
31 Ready<Option<Result<Command>>>,
32 fn(Result<AvctpCommand>) -> Ready<Option<Result<Command>>>,
33>;
34
35#[derive(Debug)]
37pub struct Command {
38 inner: AvctpCommand,
39 avc_header: Header,
40}
41
42impl Command {
43 pub fn avctp_header(&self) -> &AvctpHeader {
44 self.inner.header()
45 }
46
47 pub fn avc_header(&self) -> &Header {
48 &self.avc_header
49 }
50
51 pub fn body(&self) -> &[u8] {
52 &self.inner.body()[self.avc_header.encoded_len()..]
53 }
54
55 pub fn send_response(&self, response_type: ResponseType, body: &[u8]) -> Result<()> {
56 let response_header = self.avc_header.create_response(response_type)?;
57 let mut rbuf = vec![0 as u8; response_header.encoded_len()];
58 response_header.encode(&mut rbuf[..])?;
59 if body.len() > 0 {
60 rbuf.extend_from_slice(body);
61 }
62 self.inner.send_response(rbuf.as_slice())
63 }
64
65 pub fn is_vendor_dependent(&self) -> bool {
66 self.avc_header.op_code() == &OpCode::VendorDependent
67 }
68}
69
70impl TryFrom<Result<AvctpCommand>> for Command {
71 type Error = Error;
72
73 fn try_from(value: Result<AvctpCommand>) -> Result<Command> {
74 let inner = match value {
75 Err(e) => return Err(e),
76 Ok(inner) => inner,
77 };
78 let avc_header = match Header::decode(inner.body()) {
79 Err(e) => return Err(e),
80 Ok(head) => head,
81 };
82 Ok(Command { inner, avc_header })
83 }
84}
85
86#[derive(Debug, Clone, PartialEq)]
87pub struct CommandResponse(pub ResponseType, pub Vec<u8>);
88
89impl CommandResponse {
90 pub fn response_type(&self) -> ResponseType {
91 return self.0;
92 }
93
94 pub fn response(&self) -> &[u8] {
95 return self.1.as_slice();
96 }
97}
98
99impl TryFrom<AvctpPacket> for CommandResponse {
100 type Error = Error;
101
102 fn try_from(value: AvctpPacket) -> Result<CommandResponse> {
103 let buf = value.body();
104 let avc_header = Header::decode(buf)?;
105 let body = buf[avc_header.encoded_len()..].to_vec();
106 if let PacketType::Response(response_type) = avc_header.packet_type() {
107 Ok(CommandResponse(response_type, body))
108 } else {
109 Err(Error::InvalidHeader)
110 }
111 }
112}
113
114#[derive(Debug)]
117pub struct Peer {
118 inner: AvctpPeer,
120}
121
122impl Peer {
123 pub fn new(channel: Channel) -> Self {
125 Self { inner: AvctpPeer::new(channel) }
126 }
127
128 fn filter_internal_responses(
133 avct_command_result: Result<AvctpCommand>,
134 ) -> Option<Result<Command>> {
135 let cmd = match Command::try_from(avct_command_result) {
136 Ok(cmd) => cmd,
137 Err(e) => return Some(Err(e)),
138 };
139
140 let avcth = cmd.avctp_header();
142 let avch = cmd.avc_header();
143
144 match (avcth.is_single(), avch.subunit_type(), avch.op_code()) {
145 (true, Some(SubunitType::Unit), &OpCode::UnitInfo) => {
148 trace!("received UNITINFO command");
149 let mut pbuf: [u8; 5] = [0xff; 5];
152 pbuf[0] = 0x07;
154 pbuf[1] = u8::from(&SubunitType::Panel) << 3;
156 pbuf[2] = 0xff;
158 pbuf[3] = 0xff;
159 pbuf[4] = 0xff;
160 match cmd.send_response(ResponseType::ImplementedStable, &pbuf) {
161 Err(e) => Some(Err(e)),
162 Ok(_) => None,
163 }
164 }
165 (true, Some(SubunitType::Unit), &OpCode::SubUnitInfo) => {
166 trace!("received SUBUNITINFO command");
167 let mut pbuf: [u8; 5] = [0xff; 5];
170 pbuf[0] = 0b111;
172 pbuf[1] = u8::from(&SubunitType::Panel) << 3;
174 match cmd.send_response(ResponseType::ImplementedStable, &pbuf) {
175 Err(e) => Some(Err(e)),
176 Ok(_) => None,
177 }
178 }
179 (_, Some(SubunitType::Panel), &OpCode::Passthrough)
180 | (_, Some(SubunitType::Panel), &OpCode::VendorDependent) => Some(Ok(cmd)),
181 _ => {
182 info!("received invalid command");
183 match cmd.send_response(ResponseType::NotImplemented, &[]) {
184 Err(e) => Some(Err(e)),
185 Ok(_) => None,
186 }
187 }
188 }
189 }
190
191 pub fn take_command_stream(&self) -> CommandStream {
193 self.inner
194 .take_command_stream()
195 .filter_map(|avct_command| future::ready(Self::filter_internal_responses(avct_command)))
196 }
197
198 fn passthrough_command_timeout() -> MonotonicDuration {
200 const CMD_TIMER_MS: i64 = 1000;
201 MonotonicDuration::from_millis(CMD_TIMER_MS)
202 }
203
204 pub fn send_vendor_dependent_command<'a>(
207 &'a self,
208 command_type: CommandType,
209 payload: &'a [u8],
210 ) -> Result<impl Stream<Item = Result<CommandResponse>>> {
211 let avc_header = Header::new(
212 command_type,
213 u8::from(&SubunitType::Panel),
214 0,
215 OpCode::VendorDependent,
216 Some(BT_SIG_COMPANY_ID),
217 );
218
219 let avc_h_len = avc_header.encoded_len();
220 let mut buf = vec![0; avc_h_len];
221 avc_header.encode(&mut buf[..])?;
222 buf.extend_from_slice(payload);
223
224 let stream = self.inner.send_command(buf.as_slice())?;
225 let stream = stream.map(|resp| CommandResponse::try_from(resp?));
226 Ok(stream)
227 }
228
229 pub async fn send_avc_passthrough_command<'a>(
233 &'a self,
234 payload: &'a [u8],
235 ) -> Result<CommandResponse> {
236 let avc_header = Header::new(
237 CommandType::Control,
238 u8::from(&SubunitType::Panel),
239 0,
240 OpCode::Passthrough,
241 Some(BT_SIG_COMPANY_ID),
242 );
243
244 let avc_h_len = avc_header.encoded_len();
245 let mut buf = vec![0; avc_h_len];
246 avc_header.encode(&mut buf[..])?;
247 buf.extend_from_slice(payload);
248
249 let mut response_stream = self.inner.send_command(buf.as_slice())?;
250
251 let timeout = MonotonicInstant::after(Peer::passthrough_command_timeout());
252 loop {
253 if let Some(resp) = response_stream
254 .next()
255 .on_timeout(timeout, || return Some(Err(Error::Timeout)))
256 .await
257 {
258 let value = CommandResponse::try_from(resp?)?;
259 if value.0 == ResponseType::Interim {
260 continue;
261 }
262 return Ok(value);
263 } else {
264 return Err(Error::PeerDisconnected);
265 }
266 }
267 }
268}