use super::Driver;
use crate::bus::HostBus;
use crate::descriptor;
use crate::types::{ConnectionSpeed, DeviceAddress, SetupPacket, TransferType};
use crate::{ControlError, PipeId, UsbHost};
use core::num::NonZeroU8;
use usb_device::{
control::{Recipient, RequestType},
UsbDirection,
};
pub struct KbdDriver<const MAX_DEVICES: usize = 8> {
devices: [Option<KbdDevice>; MAX_DEVICES],
event: Option<KbdEvent>,
}
#[derive(Copy, Clone)]
struct KbdDevice {
device_address: DeviceAddress,
inner: KbdDeviceInner,
}
#[derive(Copy, Clone)]
enum KbdDeviceInner {
Pending(PendingKbdDevice),
Configured(ConfiguredKbdDevice),
}
impl KbdDeviceInner {
fn pending() -> Self {
KbdDeviceInner::Pending(PendingKbdDevice {
config: None,
interface: None,
endpoint: None,
interval: None,
})
}
}
#[derive(Copy, Clone)]
struct PendingKbdDevice {
config: Option<u8>,
interface: Option<u8>,
endpoint: Option<u8>,
interval: Option<u8>,
}
#[derive(Copy, Clone)]
struct ConfiguredKbdDevice {
interface: u8,
control_pipe: PipeId,
interrupt_pipe: PipeId,
output_report: u8,
}
impl PendingKbdDevice {
fn supported_config(&self) -> Option<u8> {
self.interface
.and_then(|_| self.endpoint)
.and_then(|_| self.interval)
.and_then(|_| self.config)
}
}
#[derive(Copy, Clone, defmt::Format)]
#[repr(packed)]
pub struct InputReport {
pub modifier_status: ModifierStatus,
_reserved: u8,
pub keypress: [Option<NonZeroU8>; 6],
}
impl InputReport {
pub fn pressed_keys(&self) -> impl Iterator<Item = u8> + '_ {
self.keypress
.iter()
.filter_map(|opt| *opt)
.map(|code| code.into())
}
}
impl<'a> TryFrom<&'a [u8]> for &'a InputReport {
type Error = ();
fn try_from(value: &'a [u8]) -> Result<Self, Self::Error> {
if value.len() == 8 && core::mem::size_of::<InputReport>() == 8 {
Ok(unsafe { &*(value as *const _ as *const InputReport) })
} else {
Err(())
}
}
}
#[derive(Debug, Copy, Clone, defmt::Format)]
pub struct ModifierStatus(u8);
impl ModifierStatus {
pub fn left_ctrl(&self) -> bool {
self.0 & 1 == 1
}
pub fn left_shift(&self) -> bool {
(self.0 >> 1) & 1 == 1
}
pub fn left_alt(&self) -> bool {
(self.0 >> 2) & 1 == 1
}
pub fn left_gui(&self) -> bool {
(self.0 >> 3) & 1 == 1
}
pub fn right_ctrl(&self) -> bool {
(self.0 >> 4) & 1 == 1
}
pub fn right_shift(&self) -> bool {
(self.0 >> 5) & 1 == 1
}
pub fn right_alt(&self) -> bool {
(self.0 >> 6) & 1 == 1
}
pub fn right_gui(&self) -> bool {
(self.0 >> 7) & 1 == 1
}
}
#[derive(Copy, Clone, defmt::Format)]
pub enum KbdEvent {
DeviceAdded(DeviceAddress),
DeviceRemoved(DeviceAddress),
InputChanged(DeviceAddress, InputReport),
ControlComplete(DeviceAddress),
}
#[derive(Copy, Clone)]
#[repr(u8)]
pub enum KbdLed {
NumLock = 0,
CapsLock = 1,
ScrollLock = 2,
Compose = 3,
Kana = 4,
}
#[derive(Copy, Clone)]
pub enum KbdError {
ControlError(ControlError),
UnknownDevice,
}
impl From<ControlError> for KbdError {
fn from(e: ControlError) -> Self {
KbdError::ControlError(e)
}
}
impl<const MAX_DEVICES: usize> KbdDriver<MAX_DEVICES> {
pub fn new() -> Self {
Self {
devices: [None; MAX_DEVICES],
event: None,
}
}
pub fn take_event(&mut self) -> Option<KbdEvent> {
self.event.take()
}
pub fn set_idle<B: HostBus>(
&mut self,
dev_addr: DeviceAddress,
latency: u8,
host: &mut UsbHost<B>,
) -> Result<(), KbdError> {
if let Some(device) = self.find_configured_device(dev_addr) {
host.control_out(
Some(dev_addr),
Some(device.control_pipe),
SetupPacket::new(
UsbDirection::Out,
RequestType::Class,
Recipient::Interface,
0x0a, (latency as u16) << 8,
device.interface as u16,
0,
),
&[],
)?;
Ok(())
} else {
Err(KbdError::UnknownDevice)
}
}
pub fn set_led<B: HostBus>(
&mut self,
dev_addr: DeviceAddress,
led: KbdLed,
on: bool,
host: &mut UsbHost<B>,
) -> Result<(), KbdError> {
if let Some(device) = self.find_configured_device(dev_addr) {
if on {
device.output_report |= 1 << (led as u8);
} else {
device.output_report &= !(1 << (led as u8));
}
host.control_out(
Some(dev_addr),
Some(device.control_pipe),
SetupPacket::new(
UsbDirection::Out,
RequestType::Class,
Recipient::Interface,
0x09, 2 << 8, 0,
1,
),
&[device.output_report],
)?;
Ok(())
} else {
Err(KbdError::UnknownDevice)
}
}
fn find_device_slot(
&mut self,
device_address: DeviceAddress,
) -> Option<&mut Option<KbdDevice>> {
self.devices.iter_mut().find(|dev| {
if let Some(dev) = dev {
dev.device_address == device_address
} else {
false
}
})
}
fn find_device(&mut self, device_address: DeviceAddress) -> Option<&mut KbdDevice> {
if let Some(Some(device)) = self.find_device_slot(device_address) {
Some(device)
} else {
None
}
}
fn find_pending_device(
&mut self,
device_address: DeviceAddress,
) -> Option<&mut PendingKbdDevice> {
match self.find_device(device_address) {
Some(KbdDevice {
inner: KbdDeviceInner::Pending(pending_device),
..
}) => Some(pending_device),
_ => None,
}
}
fn find_configured_device(
&mut self,
device_address: DeviceAddress,
) -> Option<&mut ConfiguredKbdDevice> {
match self.find_device(device_address) {
Some(KbdDevice {
inner: KbdDeviceInner::Configured(device),
..
}) => Some(device),
_ => None,
}
}
fn remove_device(&mut self, device_address: DeviceAddress) {
if let Some(slot) = self.find_device_slot(device_address) {
slot.take();
}
}
}
impl<B: HostBus> Driver<B> for KbdDriver {
fn attached(&mut self, device_address: DeviceAddress, _connection_speed: ConnectionSpeed) {
if let Some(slot) = self.devices.iter_mut().find(|dev| dev.is_none()) {
slot.replace(KbdDevice {
device_address,
inner: KbdDeviceInner::pending(),
});
} else {
}
}
fn detached(&mut self, device_address: DeviceAddress) {
if let Some(slot) = self.find_device_slot(device_address) {
if let Some(KbdDevice {
inner: KbdDeviceInner::Configured(_),
..
}) = slot.take()
{
self.event = Some(KbdEvent::DeviceRemoved(device_address));
}
}
}
fn descriptor(&mut self, device_address: DeviceAddress, descriptor_type: u8, data: &[u8]) {
if let Some(device) = self.find_pending_device(device_address) {
if descriptor_type == descriptor::TYPE_CONFIGURATION as u8 {
if device.interface.is_none() {
if let Ok((_, config)) = descriptor::parse::configuration_descriptor(data) {
device.config = Some(config.value);
}
}
} else if descriptor_type == descriptor::TYPE_INTERFACE {
if let Ok((_, interface)) = descriptor::parse::interface_descriptor(data) {
if interface.interface_class == 0x03 && interface.interface_sub_class == 0x01 && interface.interface_protocol == 0x01
{
device.interface = Some(interface.interface_number);
}
}
} else if descriptor_type == descriptor::TYPE_ENDPOINT {
if device.interface.is_some() && device.endpoint.is_none() {
if let Ok((_, endpoint)) = descriptor::parse::endpoint_descriptor(data) {
if endpoint.address.direction() == UsbDirection::In
&& endpoint.attributes.transfer_type() == TransferType::Interrupt
{
device.endpoint = Some(endpoint.address.number());
device.interval = Some(endpoint.interval);
}
}
}
}
}
}
fn configure(&mut self, device_address: DeviceAddress) -> Option<u8> {
let config = self
.find_pending_device(device_address)
.and_then(|device| device.supported_config());
if config.is_none() {
self.remove_device(device_address);
}
config
}
fn configured(&mut self, device_address: DeviceAddress, value: u8, host: &mut UsbHost<B>) {
let configured_device = if let Some(device) = self.find_pending_device(device_address) {
if let Some(config) = device.supported_config() {
if value != config {
None
} else {
let interface = device.interface.unwrap();
let control_pipe = host.create_control_pipe(device_address);
let interrupt_pipe = host.create_interrupt_pipe(
device_address,
device.endpoint.unwrap(),
UsbDirection::In,
8,
device.interval.unwrap(),
);
self.event = Some(KbdEvent::DeviceAdded(device_address));
match (control_pipe, interrupt_pipe) {
(Some(control_pipe), Some(interrupt_pipe)) => Some(ConfiguredKbdDevice {
interface,
control_pipe,
interrupt_pipe,
output_report: 0,
}),
_ => None,
}
}
} else {
None
}
} else {
None
};
if let Some(configured_device) = configured_device {
self.find_device_slot(device_address)
.unwrap()
.replace(KbdDevice {
device_address,
inner: KbdDeviceInner::Configured(configured_device),
});
} else {
self.remove_device(device_address);
}
}
fn completed_control(
&mut self,
dev_addr: DeviceAddress,
_pipe_id: PipeId,
_data: Option<&[u8]>,
) {
self.event = Some(KbdEvent::ControlComplete(dev_addr));
}
fn completed_in(&mut self, device_address: DeviceAddress, pipe: PipeId, data: &[u8]) {
if let Some(device) = self.find_configured_device(device_address) {
if pipe == device.interrupt_pipe {
let converted: Result<&InputReport, _> = data.try_into();
if let Ok(input_report) = converted {
self.event = Some(KbdEvent::InputChanged(device_address, *input_report));
}
}
}
}
fn completed_out(
&mut self,
_device_address: DeviceAddress,
_pipe_id: PipeId,
_data: &mut [u8],
) {
}
}