use super::bredr::{L2cap, L2capError};
use super::Bluetooth;
use crate::timer::Timer;
pub const PSM_SDP: u16 = 0x0001;
const PDU_SSA_REQUEST: u8 = 0x06;
const PDU_SSA_RESPONSE: u8 = 0x07;
const PDU_ERROR_RESPONSE: u8 = 0x01;
pub const HID_SERVICE_CLASS: u16 = 0x1124;
pub const ATTR_HID_DESCRIPTOR_LIST: u16 = 0x0206;
pub const HID_DESCRIPTOR_TYPE_REPORT: u8 = 0x22;
const DE_UINT: u8 = 1;
const DE_UUID: u8 = 3;
const DE_STRING: u8 = 4;
const DE_SEQUENCE: u8 = 6;
const MAX_ATTR_BYTES: u16 = 0x0190;
const OPEN_TIMEOUT_MS: u32 = 8_000;
const RESPONSE_TIMEOUT_MS: u32 = 5_000;
const RESPONSE_BUF: usize = 512;
const ATTR_ACC: usize = 1024;
const CONT_MAX: usize = 17;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum SdpError {
L2cap(L2capError),
Timeout,
Protocol,
NotFound,
TooLong,
}
impl From<L2capError> for SdpError {
fn from(e: L2capError) -> Self {
SdpError::L2cap(e)
}
}
pub fn read_report_descriptor(
bt: &mut Bluetooth,
handle: u16,
timer: &Timer,
out: &mut [u8],
) -> Result<usize, SdpError> {
let mut l2 = L2cap::new(handle);
let cid = l2.open(bt, PSM_SDP, timer, OPEN_TIMEOUT_MS)?;
let result = query_descriptor(&mut l2, bt, cid, timer, out);
let _ = l2.close(bt, cid, timer);
result
}
fn query_descriptor(
l2: &mut L2cap,
bt: &mut Bluetooth,
cid: u16,
timer: &Timer,
out: &mut [u8],
) -> Result<usize, SdpError> {
let mut attr = [0u8; ATTR_ACC];
let mut attr_len = 0usize;
let mut cont = [0u8; CONT_MAX];
let mut cont_len = 0usize;
let mut tid: u16 = 1;
let mut resp = [0u8; RESPONSE_BUF];
loop {
let mut req = [0u8; 48];
let req_len = build_request(tid, &cont[..cont_len], &mut req);
l2.send(bt, cid, &req[..req_len])?;
let n = loop {
match l2.recv(bt, timer, RESPONSE_TIMEOUT_MS, &mut resp)? {
Some((c, n)) if c == cid => break n,
Some(_) => continue,
None => return Err(SdpError::Timeout),
}
};
let pdu = &resp[..n];
if pdu.first() == Some(&PDU_ERROR_RESPONSE) {
return Err(SdpError::Protocol);
}
if pdu.len() < 7 || pdu[0] != PDU_SSA_RESPONSE {
return Err(SdpError::Protocol);
}
let list_len = u16::from_be_bytes([pdu[5], pdu[6]]) as usize;
let list_start = 7;
let list_end = list_start + list_len;
if list_end >= pdu.len() {
return Err(SdpError::Protocol);
}
if attr_len + list_len > attr.len() {
return Err(SdpError::TooLong);
}
attr[attr_len..attr_len + list_len].copy_from_slice(&pdu[list_start..list_end]);
attr_len += list_len;
let cs_len = pdu[list_end] as usize;
if cs_len == 0 || list_end + 1 + cs_len > pdu.len() || cs_len > cont.len() {
break;
}
cont[..cs_len].copy_from_slice(&pdu[list_end + 1..list_end + 1 + cs_len]);
cont_len = cs_len;
tid = tid.wrapping_add(1);
}
extract_report_descriptor(&attr[..attr_len], out).ok_or(SdpError::NotFound)
}
fn build_request(tid: u16, cont: &[u8], buf: &mut [u8]) -> usize {
let hid = HID_SERVICE_CLASS.to_be_bytes();
let attr = ATTR_HID_DESCRIPTOR_LIST.to_be_bytes();
let max = MAX_ATTR_BYTES.to_be_bytes();
let mut params = [0u8; 32];
let mut p = 0;
params[p] = 0x35;
params[p + 1] = 0x03;
params[p + 2] = 0x19;
params[p + 3] = hid[0];
params[p + 4] = hid[1];
p += 5;
params[p] = max[0];
params[p + 1] = max[1];
p += 2;
params[p] = 0x35;
params[p + 1] = 0x03;
params[p + 2] = 0x09;
params[p + 3] = attr[0];
params[p + 4] = attr[1];
p += 5;
params[p] = cont.len() as u8;
p += 1;
params[p..p + cont.len()].copy_from_slice(cont);
p += cont.len();
buf[0] = PDU_SSA_REQUEST;
buf[1..3].copy_from_slice(&tid.to_be_bytes());
buf[3..5].copy_from_slice(&(p as u16).to_be_bytes());
buf[5..5 + p].copy_from_slice(¶ms[..p]);
5 + p
}
fn de_header(data: &[u8], i: usize) -> Option<(u8, usize, usize, usize)> {
let b = *data.get(i)?;
let de_type = b >> 3;
let size_index = b & 0x07;
let (val_len, extra) = match size_index {
0 => (if de_type == 0 { 0 } else { 1 }, 0),
1 => (2, 0),
2 => (4, 0),
3 => (8, 0),
4 => (16, 0),
5 => (*data.get(i + 1)? as usize, 1),
6 => (
u16::from_be_bytes([*data.get(i + 1)?, *data.get(i + 2)?]) as usize,
2,
),
7 => (
u32::from_be_bytes([
*data.get(i + 1)?,
*data.get(i + 2)?,
*data.get(i + 3)?,
*data.get(i + 4)?,
]) as usize,
4,
),
_ => return None,
};
let val_start = i + 1 + extra;
let next = val_start + val_len;
if next > data.len() {
return None;
}
Some((de_type, val_start, val_len, next))
}
fn extract_report_descriptor(attr_lists: &[u8], out: &mut [u8]) -> Option<usize> {
let (t, vs, vl, _) = de_header(attr_lists, 0)?;
if t != DE_SEQUENCE {
return None;
}
let services = &attr_lists[vs..vs + vl];
let mut i = 0;
while i < services.len() {
let (st, svs, svl, snext) = de_header(services, i)?;
i = snext;
if st != DE_SEQUENCE {
continue;
}
let record = &services[svs..svs + svl];
let mut j = 0;
while j < record.len() {
let (idt, ids, idl, idnext) = de_header(record, j)?;
if idt != DE_UINT || idl != 2 {
j = idnext;
continue;
}
let attr_id = u16::from_be_bytes([record[ids], record[ids + 1]]);
let (_vt, vvs, vvl, vnext) = de_header(record, idnext)?;
if attr_id == ATTR_HID_DESCRIPTOR_LIST {
if let Some(n) = find_report(&record[vvs..vvs + vvl], out) {
return Some(n);
}
}
j = vnext;
}
}
None
}
fn find_report(list: &[u8], out: &mut [u8]) -> Option<usize> {
let mut i = 0;
while i < list.len() {
let (et, evs, evl, enext) = de_header(list, i)?;
i = enext;
if et != DE_SEQUENCE {
continue;
}
let entry = &list[evs..evs + evl];
let (tt, tvs, tvl, tnext) = de_header(entry, 0)?;
if tt != DE_UINT || tvl < 1 {
continue;
}
let dtype = entry[tvs];
let (dt, dvs, dvl, _) = de_header(entry, tnext)?;
if dtype == HID_DESCRIPTOR_TYPE_REPORT && (dt == DE_STRING || dt == DE_UUID) {
let n = dvl.min(out.len());
out[..n].copy_from_slice(&entry[dvs..dvs + n]);
return Some(n);
}
}
None
}