use super::l2cap::{self, Reassembler};
use super::{AclData, Bluetooth, Error, Event};
use crate::timer::Timer;
const CID_SIGNALING: u16 = 0x0001;
pub const PSM_HID_CONTROL: u16 = 0x0011;
pub const PSM_HID_INTERRUPT: u16 = 0x0013;
pub const HIDP_INPUT_REPORT: u8 = 0xa1;
const SIG_COMMAND_REJECT: u8 = 0x01;
const SIG_CONNECTION_REQ: u8 = 0x02;
const SIG_CONNECTION_RSP: u8 = 0x03;
const SIG_CONFIG_REQ: u8 = 0x04;
const SIG_CONFIG_RSP: u8 = 0x05;
const SIG_DISCONNECTION_REQ: u8 = 0x06;
const SIG_DISCONNECTION_RSP: u8 = 0x07;
const SIG_INFO_REQ: u8 = 0x0a;
const SIG_INFO_RSP: u8 = 0x0b;
const L2CAP_SUCCESS: u16 = 0x0000;
const L2CAP_PENDING: u16 = 0x0001;
const CONFIG_OPT_MTU: u8 = 0x01;
const LOCAL_MTU: u16 = 0x00c0;
const INFO_TYPE_EXTENDED_FEATURES: u16 = 0x0002;
const INFO_RESULT_SUCCESS: u16 = 0x0000;
const INFO_RESULT_NOT_SUPPORTED: u16 = 0x0001;
const LOCAL_CID_CONTROL: u16 = 0x0040;
const LOCAL_CID_INTERRUPT: u16 = 0x0041;
const CH_CONTROL: usize = 0;
const CH_INTERRUPT: usize = 1;
const SIG_BUF: usize = 64;
const POLL_SLICE_MS: u32 = 500;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum HidError {
Hci(Error),
Timeout,
Disconnected(u8),
Rejected,
}
impl From<Error> for HidError {
fn from(e: Error) -> Self {
HidError::Hci(e)
}
}
#[derive(Clone, Copy)]
struct Channel {
psm: u16,
local_cid: u16,
remote_cid: u16,
conn_requested: bool,
connected: bool,
config_rsp_received: bool,
config_req_answered: bool,
}
impl Channel {
const fn new(psm: u16, local_cid: u16) -> Self {
Self {
psm,
local_cid,
remote_cid: 0,
conn_requested: false,
connected: false,
config_rsp_received: false,
config_req_answered: false,
}
}
fn is_open(&self) -> bool {
self.connected && self.config_rsp_received && self.config_req_answered
}
}
pub struct HidHost {
handle: u16,
reasm: Reassembler,
channels: [Channel; 2],
ident: u8,
}
impl HidHost {
pub fn new(connection_handle: u16) -> Self {
Self {
handle: connection_handle,
reasm: Reassembler::new(),
channels: [
Channel::new(PSM_HID_CONTROL, LOCAL_CID_CONTROL),
Channel::new(PSM_HID_INTERRUPT, LOCAL_CID_INTERRUPT),
],
ident: 1,
}
}
pub fn open(
&mut self,
bt: &mut Bluetooth,
timer: &Timer,
timeout_ms: u32,
) -> Result<(), HidError> {
self.request_connection(bt, CH_CONTROL)?;
let deadline = timer.now_micros() + (timeout_ms as u64) * 1000;
loop {
if self.channels[CH_INTERRUPT].is_open() {
return Ok(());
}
if self.channels[CH_CONTROL].is_open()
&& !self.channels[CH_INTERRUPT].conn_requested
&& !self.channels[CH_INTERRUPT].connected
{
self.request_connection(bt, CH_INTERRUPT)?;
}
let now = timer.now_micros();
if now >= deadline {
return Err(HidError::Timeout);
}
let remaining = (((deadline - now) / 1000) as u32 + 1).min(POLL_SLICE_MS);
match bt.poll(timer, remaining)? {
Some(Event::Acl(acl)) => self.on_acl(bt, &acl, timer)?,
Some(Event::Disconnected { reason, .. }) => {
return Err(HidError::Disconnected(reason))
}
Some(_) | None => {}
}
}
}
pub fn next_report(
&mut self,
bt: &mut Bluetooth,
timer: &Timer,
timeout_ms: u32,
out: &mut [u8],
) -> Result<Option<usize>, HidError> {
let deadline = timer.now_micros() + (timeout_ms as u64) * 1000;
loop {
let now = timer.now_micros();
if now >= deadline {
return Ok(None);
}
let remaining = (((deadline - now) / 1000) as u32 + 1).min(POLL_SLICE_MS);
match bt.poll(timer, remaining)? {
Some(Event::Acl(acl)) => {
if let Some(n) = self.on_acl_report(bt, &acl, timer, out)? {
return Ok(Some(n));
}
}
Some(Event::Disconnected { reason, .. }) => {
return Err(HidError::Disconnected(reason))
}
Some(_) | None => {}
}
}
}
fn on_acl(&mut self, bt: &mut Bluetooth, acl: &AclData, timer: &Timer) -> Result<(), HidError> {
if acl.handle != self.handle {
return Ok(());
}
let mut frame = [0u8; l2cap::MAX_PAYLOAD];
let (cid, len) = match self.reasm.feed(acl) {
Some(pdu) => {
let n = pdu.payload.len().min(frame.len());
frame[..n].copy_from_slice(&pdu.payload[..n]);
(pdu.cid, n)
}
None => return Ok(()),
};
if cid == CID_SIGNALING {
self.handle_signaling(bt, &frame[..len], timer)?;
}
Ok(())
}
fn on_acl_report(
&mut self,
bt: &mut Bluetooth,
acl: &AclData,
timer: &Timer,
out: &mut [u8],
) -> Result<Option<usize>, HidError> {
if acl.handle != self.handle {
return Ok(None);
}
let mut frame = [0u8; l2cap::MAX_PAYLOAD];
let (cid, len) = match self.reasm.feed(acl) {
Some(pdu) => {
let n = pdu.payload.len().min(frame.len());
frame[..n].copy_from_slice(&pdu.payload[..n]);
(pdu.cid, n)
}
None => return Ok(None),
};
let payload = &frame[..len];
if cid == CID_SIGNALING {
self.handle_signaling(bt, payload, timer)?;
return Ok(None);
}
if cid == self.channels[CH_INTERRUPT].local_cid
&& payload.first() == Some(&HIDP_INPUT_REPORT)
&& payload.len() > 1
{
let report = &payload[1..];
let n = report.len().min(out.len());
out[..n].copy_from_slice(&report[..n]);
return Ok(Some(n));
}
Ok(None)
}
fn request_connection(&mut self, bt: &mut Bluetooth, ch: usize) -> Result<(), HidError> {
let ident = self.next_ident();
let psm = self.channels[ch].psm;
let scid = self.channels[ch].local_cid;
let mut data = [0u8; 4];
data[0..2].copy_from_slice(&psm.to_le_bytes());
data[2..4].copy_from_slice(&scid.to_le_bytes());
self.channels[ch].conn_requested = true;
self.send_sig(bt, SIG_CONNECTION_REQ, ident, &data)
}
fn send_config_req(&mut self, bt: &mut Bluetooth, ch: usize) -> Result<(), HidError> {
let ident = self.next_ident();
let dcid = self.channels[ch].remote_cid;
let mut data = [0u8; 8];
data[0..2].copy_from_slice(&dcid.to_le_bytes());
data[4] = CONFIG_OPT_MTU;
data[5] = 0x02;
data[6..8].copy_from_slice(&LOCAL_MTU.to_le_bytes());
self.send_sig(bt, SIG_CONFIG_REQ, ident, &data)
}
fn handle_signaling(
&mut self,
bt: &mut Bluetooth,
frame: &[u8],
timer: &Timer,
) -> Result<(), HidError> {
let _ = timer;
let mut i = 0;
while i + 4 <= frame.len() {
let code = frame[i];
let ident = frame[i + 1];
let len = u16::from_le_bytes([frame[i + 2], frame[i + 3]]) as usize;
let start = i + 4;
let end = start + len;
if end > frame.len() {
break;
}
let data = &frame[start..end];
match code {
SIG_CONNECTION_REQ => self.on_connection_req(bt, ident, data)?,
SIG_CONNECTION_RSP => self.on_connection_rsp(bt, data)?,
SIG_CONFIG_REQ => self.on_config_req(bt, ident, data)?,
SIG_CONFIG_RSP => self.on_config_rsp(data),
SIG_DISCONNECTION_REQ => self.on_disconnection_req(bt, ident, data)?,
SIG_INFO_REQ => self.on_info_req(bt, ident, data)?,
SIG_COMMAND_REJECT => {}
_ => {}
}
i = end;
}
Ok(())
}
fn on_connection_req(
&mut self,
bt: &mut Bluetooth,
ident: u8,
data: &[u8],
) -> Result<(), HidError> {
if data.len() < 4 {
return Ok(());
}
let psm = u16::from_le_bytes([data[0], data[1]]);
let peer_scid = u16::from_le_bytes([data[2], data[3]]);
let Some(ch) = self.channel_for_psm(psm) else {
return Ok(());
};
self.channels[ch].remote_cid = peer_scid;
self.channels[ch].connected = true;
let dcid = self.channels[ch].local_cid;
let mut rsp = [0u8; 8];
rsp[0..2].copy_from_slice(&dcid.to_le_bytes());
rsp[2..4].copy_from_slice(&peer_scid.to_le_bytes());
rsp[4..6].copy_from_slice(&L2CAP_SUCCESS.to_le_bytes());
self.send_sig(bt, SIG_CONNECTION_RSP, ident, &rsp)?;
self.send_config_req(bt, ch)
}
fn on_connection_rsp(&mut self, bt: &mut Bluetooth, data: &[u8]) -> Result<(), HidError> {
if data.len() < 8 {
return Ok(());
}
let dcid = u16::from_le_bytes([data[0], data[1]]);
let scid = u16::from_le_bytes([data[2], data[3]]);
let result = u16::from_le_bytes([data[4], data[5]]);
let Some(ch) = self.channel_for_local_cid(scid) else {
return Ok(());
};
match result {
L2CAP_SUCCESS => {
self.channels[ch].remote_cid = dcid;
self.channels[ch].connected = true;
self.send_config_req(bt, ch)
}
L2CAP_PENDING => Ok(()),
_ => Err(HidError::Rejected),
}
}
fn on_config_req(
&mut self,
bt: &mut Bluetooth,
ident: u8,
data: &[u8],
) -> Result<(), HidError> {
if data.len() < 4 {
return Ok(());
}
let dcid = u16::from_le_bytes([data[0], data[1]]);
let options = &data[4..];
let Some(ch) = self.channel_for_local_cid(dcid) else {
return Ok(());
};
let remote_cid = self.channels[ch].remote_cid;
let mut rsp = [0u8; SIG_BUF];
rsp[0..2].copy_from_slice(&remote_cid.to_le_bytes());
rsp[4..6].copy_from_slice(&L2CAP_SUCCESS.to_le_bytes());
let opt_len = options.len().min(rsp.len() - 6);
rsp[6..6 + opt_len].copy_from_slice(&options[..opt_len]);
self.channels[ch].config_req_answered = true;
self.send_sig(bt, SIG_CONFIG_RSP, ident, &rsp[..6 + opt_len])
}
fn on_config_rsp(&mut self, data: &[u8]) {
if data.len() < 6 {
return;
}
let scid = u16::from_le_bytes([data[0], data[1]]);
let result = u16::from_le_bytes([data[4], data[5]]);
if result == L2CAP_SUCCESS {
if let Some(ch) = self.channel_for_local_cid(scid) {
self.channels[ch].config_rsp_received = true;
}
}
}
fn on_disconnection_req(
&mut self,
bt: &mut Bluetooth,
ident: u8,
data: &[u8],
) -> Result<(), HidError> {
if data.len() < 4 {
return Ok(());
}
self.send_sig(bt, SIG_DISCONNECTION_RSP, ident, &data[..4])
}
fn on_info_req(&mut self, bt: &mut Bluetooth, ident: u8, data: &[u8]) -> Result<(), HidError> {
if data.len() < 2 {
return Ok(());
}
let info_type = u16::from_le_bytes([data[0], data[1]]);
let mut rsp = [0u8; 12];
rsp[0..2].copy_from_slice(&info_type.to_le_bytes());
let n = if info_type == INFO_TYPE_EXTENDED_FEATURES {
rsp[2..4].copy_from_slice(&INFO_RESULT_SUCCESS.to_le_bytes());
8
} else {
rsp[2..4].copy_from_slice(&INFO_RESULT_NOT_SUPPORTED.to_le_bytes());
4
};
self.send_sig(bt, SIG_INFO_RSP, ident, &rsp[..n])
}
fn send_sig(
&mut self,
bt: &mut Bluetooth,
code: u8,
ident: u8,
data: &[u8],
) -> Result<(), HidError> {
let mut buf = [0u8; SIG_BUF];
buf[0] = code;
buf[1] = ident;
buf[2..4].copy_from_slice(&(data.len() as u16).to_le_bytes());
let end = 4 + data.len();
buf[4..end].copy_from_slice(data);
l2cap::send(bt, self.handle, CID_SIGNALING, &buf[..end])?;
Ok(())
}
fn next_ident(&mut self) -> u8 {
let id = self.ident;
self.ident = self.ident.wrapping_add(1);
if self.ident == 0 {
self.ident = 1;
}
id
}
fn channel_for_psm(&self, psm: u16) -> Option<usize> {
self.channels.iter().position(|c| c.psm == psm)
}
fn channel_for_local_cid(&self, cid: u16) -> Option<usize> {
self.channels.iter().position(|c| c.local_cid == cid)
}
}