use std::time::Duration;
use nusb::transfer::RequestBuffer;
use tokio::time::timeout;
use crate::error::{Error, Result};
use crate::packet;
use crate::protocol::{EqBand, EqBandParams, FilterType, encode_gain, encode_q};
const VID: u16 = 0x352F;
const PID: u16 = 0x0104;
const QUERY_TIMEOUT: Duration = Duration::from_millis(2000);
pub struct PD200X {
interface: nusb::Interface,
iface_num: u8,
ep_in: u8,
ep_out: Option<u8>,
}
impl PD200X {
pub async fn open() -> Result<Self> {
let device_info = nusb::list_devices()?
.find(|d| d.vendor_id() == VID && d.product_id() == PID)
.ok_or(Error::DeviceNotFound)?;
let device = device_info.open()?;
let (iface_num, ep_in, ep_out) = find_hid_interface(&device)?;
let interface = device.detach_and_claim_interface(iface_num)?;
Ok(PD200X { interface, iface_num, ep_in, ep_out })
}
async fn send_raw(&self, data: [u8; 65]) -> Result<()> {
if let Some(ep) = self.ep_out {
self.interface
.interrupt_out(ep, data.to_vec())
.await
.into_result()?;
} else {
use nusb::transfer::{ControlOut, ControlType, Recipient};
self.interface
.control_out(ControlOut {
control_type: ControlType::Class,
recipient: Recipient::Interface,
request: 0x09, value: 0x0300, index: self.iface_num as u16,
data: &data,
})
.await
.into_result()?;
}
Ok(())
}
async fn recv_one(&self) -> Result<[u8; 64]> {
let mut queue = self.interface.interrupt_in_queue(self.ep_in);
queue.submit(RequestBuffer::new(64));
let completion = queue.next_complete().await;
completion.status.map_err(Error::Transfer)?;
completion.data.try_into().map_err(|_| Error::BadResponse)
}
pub async fn send_command(&self, cmd: u16, value: u16) -> Result<()> {
self.send_raw(packet::build_command_packet(cmd, value)).await
}
pub async fn query(&self, cmd: u16) -> Result<u16> {
self.send_raw(packet::build_query_packet(cmd)).await?;
timeout(QUERY_TIMEOUT, async {
loop {
let raw = self.recv_one().await?;
if let Ok(resp) = packet::parse_response(&raw) {
if resp.command == cmd {
return Ok::<u16, Error>(resp.value);
}
}
}
})
.await
.map_err(|_| Error::Timeout)?
}
pub(crate) async fn send_eq(&self, band: EqBand, params: &EqBandParams) -> Result<()> {
self.send_raw(packet::build_eq_packet(eq_pairs(band, params))).await
}
}
fn find_hid_interface(device: &nusb::Device) -> Result<(u8, u8, Option<u8>)> {
let config = device.active_configuration().map_err(|_| Error::DeviceNotFound)?;
for iface in config.interfaces() {
for alt in iface.alt_settings() {
if alt.class() != 0x03 {
continue;
}
let mut ep_in = None;
let mut ep_out = None;
for ep in alt.endpoints() {
use nusb::transfer::EndpointType;
if ep.transfer_type() != EndpointType::Interrupt {
continue;
}
if ep.address() & 0x80 != 0 {
ep_in = Some(ep.address());
} else {
ep_out = Some(ep.address());
}
}
if let Some(ep_in) = ep_in {
return Ok((iface.interface_number(), ep_in, ep_out));
}
}
}
Err(Error::DeviceNotFound)
}
fn eq_pairs(band: EqBand, params: &EqBandParams) -> [(u16, u16); 5] {
let base = band.base_cmd();
let enabled = params.enabled as u16;
let freq = params.frequency;
match band {
EqBand::Hpf => [
(base, enabled),
(base + 1, FilterType::HighPass as u16),
(base + 2, freq),
(base + 3, 2000),
(base + 4, 100),
],
EqBand::Lpf => [
(base, enabled),
(base + 1, FilterType::LowPass as u16),
(base + 2, freq),
(base + 3, 2000),
(base + 4, 100),
],
_ => [
(base, enabled),
(base + 1, params.filter_type as u16),
(base + 2, freq),
(base + 3, encode_gain(params.gain_db)),
(base + 4, encode_q(params.q)),
],
}
}