#[repr(u16)]
enum AoaStringIndex {
Manufacturer = 0,
Model = 1,
Description = 2,
Version = 3,
Uri = 4,
SerialNumber = 5,
}
async fn send_aoa_string(device: &nusb::Device, index: u16, value: &str) {
device
.control_out(
nusb::transfer::ControlOut {
control_type: nusb::transfer::ControlType::Vendor,
recipient: nusb::transfer::Recipient::Device,
request: 52,
value: 0,
data: value.as_bytes(),
index,
},
std::time::Duration::from_millis(1000),
)
.await
.unwrap();
}
pub async fn identify_accessory(device: &nusb::Device) {
send_aoa_string(device, AoaStringIndex::Manufacturer as u16, "Android").await;
send_aoa_string(device, AoaStringIndex::Model as u16, "Android Auto").await;
send_aoa_string(device, AoaStringIndex::Description as u16, "Android Auto").await;
send_aoa_string(device, AoaStringIndex::Version as u16, "2.0.1").await;
send_aoa_string(device, AoaStringIndex::Uri as u16, "").await;
send_aoa_string(device, AoaStringIndex::SerialNumber as u16, "HU-AAAAAA").await;
}
pub async fn accessory_start(device: &nusb::Device) {
device
.control_out(
nusb::transfer::ControlOut {
control_type: nusb::transfer::ControlType::Vendor,
recipient: nusb::transfer::Recipient::Device,
request: 53,
value: 0,
data: &[],
index: 0,
},
std::time::Duration::from_millis(1000),
)
.await
.unwrap();
}
pub async fn wait_for_accessory() -> Result<nusb::Device, nusb::Error> {
loop {
tokio::time::sleep(std::time::Duration::from_millis(500)).await;
if let Ok(devices) = nusb::list_devices().await {
for info in devices {
if info.vendor_id() == 0x18d1
&& (info.product_id() == 0x2D00 || info.product_id() == 0x2D01)
{
log::info!("About to open {:?}", info);
return info.open().await;
}
}
}
log::info!("Didnt find accessory");
}
}
pub fn is_android_device(info: &nusb::DeviceInfo) -> bool {
if info.vendor_id() == 0x18D1 && matches!(info.product_id(), 0x2D00 | 0x2D01) {
return true;
}
if info
.interfaces()
.any(|i| i.class() == 0xFF && i.subclass() == 0x42 && i.protocol() == 0x01)
{
return true;
}
if info
.interfaces()
.any(|i| i.class() == 0x06 && i.subclass() == 0x01)
{
return true;
}
false
}
pub async fn get_aoa_protocol(dev: &nusb::Device) -> Option<u16> {
let result = dev
.control_in(
nusb::transfer::ControlIn {
control_type: nusb::transfer::ControlType::Vendor,
recipient: nusb::transfer::Recipient::Device,
request: 51,
value: 0,
index: 0,
length: 2,
},
std::time::Duration::from_millis(1000),
)
.await;
if let Ok(r) = result {
let version = u16::from_le_bytes([r[0], r[1]]);
if version >= 1 { Some(version) } else { None }
} else {
None
}
}
pub async fn claim_aoa_interface(device: &nusb::Device) -> nusb::Interface {
device.claim_interface(0).await.unwrap()
}
pub struct AndroidAutoUsb {
ep_in: nusb::io::EndpointRead<nusb::transfer::Bulk>,
ep_out: nusb::io::EndpointWrite<nusb::transfer::Bulk>,
}
impl AndroidAutoUsb {
pub fn new(interface: nusb::Interface) -> Option<Self> {
if let Ok(w) = interface.endpoint::<nusb::transfer::Bulk, nusb::transfer::In>(0x81) {
let a = w.reader(4096);
if let Ok(w) = interface.endpoint::<nusb::transfer::Bulk, nusb::transfer::Out>(0x1) {
let b = w.writer(4096);
return Some(Self {
ep_in: a,
ep_out: b,
});
}
}
None
}
pub fn into_split(
self,
) -> (
nusb::io::EndpointRead<nusb::transfer::Bulk>,
nusb::io::EndpointWrite<nusb::transfer::Bulk>,
) {
(self.ep_in, self.ep_out)
}
}