use crate::{Result, UsbError};
use crate::bus::{UsbBus, InterfaceNumber, StringIndex};
use crate::device;
use crate::endpoint::{Endpoint, EndpointDirection};
#[allow(missing_docs)]
pub mod descriptor_type {
pub const DEVICE: u8 = 1;
pub const CONFIGURATION: u8 = 2;
pub const STRING: u8 = 3;
pub const INTERFACE: u8 = 4;
pub const ENDPOINT: u8 = 5;
pub const IAD: u8 = 11;
pub const BOS: u8 = 15;
pub const CAPABILITY: u8 = 16;
}
pub mod lang_id {
pub const ENGLISH_US: u16 = 0x0409;
}
#[allow(missing_docs)]
pub mod capability_type {
pub const WIRELESS_USB: u8 = 1;
pub const USB_2_0_EXTENSION: u8 = 2;
pub const SS_USB_DEVICE: u8 = 3;
pub const CONTAINER_ID: u8 = 4;
pub const PLATFORM: u8 = 5;
}
pub struct DescriptorWriter<'a> {
buf: &'a mut [u8],
position: usize,
num_interfaces_mark: Option<usize>,
num_endpoints_mark: Option<usize>,
write_iads: bool,
}
impl DescriptorWriter<'_> {
pub(crate) fn new(buf: &mut [u8]) -> DescriptorWriter<'_> {
DescriptorWriter {
buf,
position: 0,
num_interfaces_mark: None,
num_endpoints_mark: None,
write_iads: false,
}
}
pub fn position(&self) -> usize {
self.position
}
pub fn write(&mut self, descriptor_type: u8, descriptor: &[u8]) -> Result<()> {
let length = descriptor.len();
if (self.position + 2 + length) > self.buf.len() || (length + 2) > 255 {
return Err(UsbError::BufferOverflow);
}
self.buf[self.position] = (length + 2) as u8;
self.buf[self.position + 1] = descriptor_type;
let start = self.position + 2;
self.buf[start..start + length].copy_from_slice(descriptor);
self.position = start + length;
Ok(())
}
pub(crate) fn device(&mut self, config: &device::Config) -> Result<()> {
self.write(
descriptor_type::DEVICE,
&[
0x10, 0x02,
config.device_class,
config.device_sub_class,
config.device_protocol,
config.max_packet_size_0,
config.vendor_id as u8, (config.vendor_id >> 8) as u8,
config.product_id as u8, (config.product_id >> 8) as u8,
config.device_release as u8, (config.device_release >> 8) as u8,
config.manufacturer.map_or(0, |_| 1),
config.product.map_or(0, |_| 2),
config.serial_number.map_or(0, |_| 3),
1,
])
}
pub(crate) fn configuration(&mut self, config: &device::Config) -> Result<()> {
self.num_interfaces_mark = Some(self.position + 4);
self.write_iads = config.composite_with_iads;
self.write(
descriptor_type::CONFIGURATION,
&[
0, 0,
0,
device::CONFIGURATION_VALUE,
0,
0x80
| if config.self_powered { 0x40 } else { 0x00 }
| if config.supports_remote_wakeup { 0x20 } else { 0x00 },
config.max_power
])
}
pub(crate) fn end_class(&mut self) {
self.num_endpoints_mark = None;
}
pub(crate) fn end_configuration(&mut self) {
let position = self.position as u16;
self.buf[2..4].copy_from_slice(&position.to_le_bytes());
}
pub fn iad(&mut self, first_interface: InterfaceNumber, interface_count: u8,
function_class: u8, function_sub_class: u8, function_protocol: u8) -> Result<()>
{
if !self.write_iads {
return Ok(());
}
self.write(
descriptor_type::IAD,
&[
first_interface.into(),
interface_count,
function_class,
function_sub_class,
function_protocol,
0
])?;
Ok(())
}
pub fn interface(&mut self, number: InterfaceNumber,
interface_class: u8, interface_sub_class: u8, interface_protocol: u8) -> Result<()>
{
self.interface_alt(
number,
device::DEFAULT_ALTERNATE_SETTING,
interface_class,
interface_sub_class,
interface_protocol,
None,
)
}
pub fn interface_alt(
&mut self,
number: InterfaceNumber,
alternate_setting: u8,
interface_class: u8,
interface_sub_class: u8,
interface_protocol: u8,
interface_string: Option<StringIndex>,
) -> Result<()> {
if alternate_setting == device::DEFAULT_ALTERNATE_SETTING {
match self.num_interfaces_mark {
Some(mark) => self.buf[mark] += 1,
None => return Err(UsbError::InvalidState),
};
}
let str_index = interface_string.map_or(0, Into::into);
self.num_endpoints_mark = Some(self.position + 4);
self.write(
descriptor_type::INTERFACE,
&[
number.into(),
alternate_setting,
0,
interface_class,
interface_sub_class,
interface_protocol,
str_index,
],
)?;
Ok(())
}
pub fn endpoint<'e, B: UsbBus, D: EndpointDirection>(&mut self, endpoint: &Endpoint<'e, B, D>)
-> Result<()>
{
match self.num_endpoints_mark {
Some(mark) => self.buf[mark] += 1,
None => return Err(UsbError::InvalidState),
};
let mps = endpoint.max_packet_size();
self.write(
descriptor_type::ENDPOINT,
&[
endpoint.address().into(),
endpoint.ep_type() as u8,
mps as u8, (mps >> 8) as u8,
endpoint.interval(),
])?;
Ok(())
}
pub(crate) fn string(&mut self, string: &str) -> Result<()> {
let mut pos = self.position;
if pos + 2 > self.buf.len() {
return Err(UsbError::BufferOverflow);
}
self.buf[pos] = 0;
self.buf[pos + 1] = descriptor_type::STRING;
pos += 2;
for c in string.encode_utf16() {
if pos >= self.buf.len() {
return Err(UsbError::BufferOverflow);
}
self.buf[pos..pos + 2].copy_from_slice(&c.to_le_bytes());
pos += 2;
}
self.buf[self.position] = (pos - self.position) as u8;
self.position = pos;
Ok(())
}
}
pub struct BosWriter<'w, 'a: 'w> {
writer: &'w mut DescriptorWriter<'a>,
num_caps_mark: Option<usize>,
}
impl<'w, 'a: 'w> BosWriter<'w, 'a> {
pub(crate) fn new(writer: &'w mut DescriptorWriter<'a>) -> Self {
Self {
writer: writer,
num_caps_mark: None,
}
}
pub(crate) fn bos(&mut self) -> Result<()> {
self.num_caps_mark= Some(self.writer.position + 4);
self.writer.write(
descriptor_type::BOS,
&[
0x00, 0x00,
0x00,
])?;
self.capability(capability_type::USB_2_0_EXTENSION, &[0; 4])?;
Ok(())
}
pub fn capability(&mut self, capability_type: u8, data: &[u8]) -> Result<()> {
match self.num_caps_mark{
Some(mark) => self.writer.buf[mark] += 1,
None => return Err(UsbError::InvalidState),
}
let mut start = self.writer.position;
let blen = data.len();
if (start + blen + 3) > self.writer.buf.len() || (blen + 3) > 255 {
return Err(UsbError::BufferOverflow);
}
self.writer.buf[start] = (blen + 3) as u8;
self.writer.buf[start+1] = descriptor_type::CAPABILITY;
self.writer.buf[start+2] = capability_type;
start += 3;
self.writer.buf[start..start+blen].copy_from_slice(data);
self.writer.position = start + blen;
Ok(())
}
pub(crate) fn end_bos(&mut self) {
self.num_caps_mark= None;
let position = self.writer.position as u16;
self.writer.buf[2..4].copy_from_slice(&position.to_le_bytes());
}
}