#![warn(missing_docs)]
include!(concat!(env!("OUT_DIR"), "/usb_ids.cg.rs"));
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct UsbId<const ID: u8, T> {
id: T,
name: &'static str,
}
impl<const ID: u8, T: Copy> UsbId<ID, T> {
pub fn id(&self) -> T {
self.id
}
pub fn name(&self) -> &'static str {
self.name
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct UsbIdWithChildren<T: Copy, C: 'static> {
id: T,
name: &'static str,
children: &'static [C],
}
impl<T: Copy, C: 'static> UsbIdWithChildren<T, C> {
pub fn id(&self) -> T {
self.id
}
pub fn name(&self) -> &'static str {
self.name
}
fn children(&self) -> impl Iterator<Item = &'static C> {
self.children.iter()
}
}
pub struct Vendors;
impl Vendors {
pub fn iter() -> impl Iterator<Item = &'static Vendor> {
USB_IDS.values()
}
}
pub struct Classes;
impl Classes {
pub fn iter() -> impl Iterator<Item = &'static Class> {
USB_CLASSES.values()
}
}
pub struct Languages;
impl Languages {
pub fn iter() -> impl Iterator<Item = &'static Language> {
USB_LANGS.values()
}
}
pub struct HidUsagePages;
impl HidUsagePages {
pub fn iter() -> impl Iterator<Item = &'static HidUsagePage> {
USB_HUTS.values()
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Vendor {
id: u16,
name: &'static str,
devices: &'static [Device],
}
impl Vendor {
pub fn id(&self) -> u16 {
self.id
}
pub fn name(&self) -> &'static str {
self.name
}
pub fn devices(&self) -> impl Iterator<Item = &'static Device> {
self.devices.iter()
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Device {
vendor_id: u16,
id: u16,
name: &'static str,
interfaces: &'static [Interface],
}
impl Device {
pub fn from_vid_pid(vid: u16, pid: u16) -> Option<&'static Device> {
let vendor = Vendor::from_id(vid);
vendor.and_then(|v| v.devices().find(|d| d.id == pid))
}
pub fn vendor(&self) -> &'static Vendor {
USB_IDS.get(&self.vendor_id).unwrap()
}
pub fn as_vid_pid(&self) -> (u16, u16) {
(self.vendor_id, self.id)
}
pub fn id(&self) -> u16 {
self.id
}
pub fn name(&self) -> &'static str {
self.name
}
pub fn interfaces(&self) -> impl Iterator<Item = &'static Interface> {
self.interfaces.iter()
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Interface {
id: u8,
name: &'static str,
}
impl Interface {
pub fn id(&self) -> u8 {
self.id
}
pub fn name(&self) -> &'static str {
self.name
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Class {
id: u8,
name: &'static str,
sub_classes: &'static [SubClass],
}
impl Class {
pub fn id(&self) -> u8 {
self.id
}
pub fn name(&self) -> &'static str {
self.name
}
pub fn sub_classes(&self) -> impl Iterator<Item = &'static SubClass> {
self.sub_classes.iter()
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct SubClass {
class_id: u8,
id: u8,
name: &'static str,
protocols: &'static [Protocol],
}
impl SubClass {
pub fn from_cid_scid(class_id: u8, id: u8) -> Option<&'static Self> {
let class = Class::from_id(class_id);
class.and_then(|c| c.sub_classes().find(|s| s.id == id))
}
pub fn class(&self) -> &'static Class {
USB_CLASSES.get(&self.class_id).unwrap()
}
pub fn as_cid_scid(&self) -> (u8, u8) {
(self.class_id, self.id)
}
pub fn id(&self) -> u8 {
self.id
}
pub fn name(&self) -> &'static str {
self.name
}
pub fn protocols(&self) -> impl Iterator<Item = &'static Protocol> {
self.protocols.iter()
}
}
const PROTOCOL_TAG: u8 = 0;
const AT_TAG: u8 = 1;
const HID_TAG: u8 = 2;
const HID_TYPE_TAG: u8 = 3;
const HID_USAGE_TAG: u8 = 4;
const BIAS_TAG: u8 = 5;
const PHY_TAG: u8 = 6;
const DIALECT_TAG: u8 = 7;
const HCC_TAG: u8 = 8;
const VT_TAG: u8 = 9;
pub type Protocol = UsbId<PROTOCOL_TAG, u8>;
impl Protocol {
pub fn from_cid_scid_pid(class_id: u8, subclass_id: u8, id: u8) -> Option<&'static Self> {
let subclass = SubClass::from_cid_scid(class_id, subclass_id);
subclass.and_then(|s| s.protocols().find(|p| p.id == id))
}
}
pub type AudioTerminal = UsbId<AT_TAG, u16>;
pub type Hid = UsbId<HID_TAG, u8>;
pub type HidItemType = UsbId<HID_TYPE_TAG, u8>;
pub type HidUsagePage = UsbIdWithChildren<u8, HidUsage>;
impl HidUsagePage {
pub fn usages(&self) -> impl Iterator<Item = &'static HidUsage> {
self.children()
}
}
pub type HidUsage = UsbId<HID_USAGE_TAG, u16>;
impl HidUsage {
pub fn from_pageid_uid(page_id: u8, id: u16) -> Option<&'static Self> {
let page = HidUsagePage::from_id(page_id)?;
page.children().find(|u| u.id() == id)
}
}
pub type Bias = UsbId<BIAS_TAG, u8>;
pub type Phy = UsbId<PHY_TAG, u8>;
pub type Language = UsbIdWithChildren<u16, Dialect>;
impl Language {
pub fn dialects(&self) -> impl Iterator<Item = &'static Dialect> {
self.children()
}
}
pub type Dialect = UsbId<DIALECT_TAG, u8>;
impl Dialect {
pub fn from_lid_did(language_id: u16, id: u8) -> Option<&'static Self> {
let language = Language::from_id(language_id)?;
language.children().find(|d| d.id() == id)
}
}
pub type HidCountryCode = UsbId<HCC_TAG, u8>;
pub type VideoTerminal = UsbId<VT_TAG, u16>;
pub trait FromId<T> {
fn from_id(id: T) -> Option<&'static Self>;
}
impl FromId<u16> for Vendor {
fn from_id(id: u16) -> Option<&'static Self> {
USB_IDS.get(&id)
}
}
impl FromId<u8> for Class {
fn from_id(id: u8) -> Option<&'static Self> {
USB_CLASSES.get(&id)
}
}
impl FromId<u16> for AudioTerminal {
fn from_id(id: u16) -> Option<&'static Self> {
USB_AUDIO_TERMINALS.get(&id)
}
}
impl FromId<u8> for Hid {
fn from_id(id: u8) -> Option<&'static Self> {
USB_HID_IDS.get(&id)
}
}
impl FromId<u8> for HidItemType {
fn from_id(id: u8) -> Option<&'static Self> {
USB_HID_R_TYPES.get(&id)
}
}
impl FromId<u8> for HidUsagePage {
fn from_id(id: u8) -> Option<&'static Self> {
USB_HUTS.get(&id)
}
}
impl FromId<u8> for Bias {
fn from_id(id: u8) -> Option<&'static Self> {
USB_BIASES.get(&id)
}
}
impl FromId<u8> for Phy {
fn from_id(id: u8) -> Option<&'static Self> {
USB_PHYS.get(&id)
}
}
impl FromId<u16> for Language {
fn from_id(id: u16) -> Option<&'static Self> {
USB_LANGS.get(&id)
}
}
impl FromId<u8> for HidCountryCode {
fn from_id(id: u8) -> Option<&'static Self> {
USB_HID_CCS.get(&id)
}
}
impl FromId<u16> for VideoTerminal {
fn from_id(id: u16) -> Option<&'static Self> {
USB_VIDEO_TERMINALS.get(&id)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_from_id() {
let vendor = Vendor::from_id(0x1d6b).unwrap();
assert_eq!(vendor.name(), "Linux Foundation");
assert_eq!(vendor.id(), 0x1d6b);
}
#[test]
fn test_vendor_devices() {
let vendor = Vendor::from_id(0x1d6b).unwrap();
for device in vendor.devices() {
assert_eq!(device.vendor(), vendor);
assert!(!device.name().is_empty());
}
}
#[test]
fn test_from_vid_pid() {
let device = Device::from_vid_pid(0x1d6b, 0x0003).unwrap();
assert_eq!(device.name(), "3.0 root hub");
let (vid, pid) = device.as_vid_pid();
assert_eq!(vid, device.vendor().id());
assert_eq!(pid, device.id());
let device2 = Device::from_vid_pid(vid, pid).unwrap();
assert_eq!(device, device2);
let last_device = Device::from_vid_pid(0xffee, 0x0100).unwrap();
assert_eq!(
last_device.name(),
"Card Reader Controller RTS5101/RTS5111/RTS5116"
);
}
#[test]
fn test_class_from_id() {
let class = Class::from_id(0x03).unwrap();
assert_eq!(class.name(), "Human Interface Device");
assert_eq!(class.id(), 0x03);
}
#[test]
fn test_subclass_from_cid_scid() {
let subclass = SubClass::from_cid_scid(0x03, 0x01).unwrap();
assert_eq!(subclass.name(), "Boot Interface Subclass");
assert_eq!(subclass.id(), 0x01);
}
#[test]
fn test_protocol_from_cid_scid_pid() {
let protocol = Protocol::from_cid_scid_pid(0x03, 0x01, 0x01).unwrap();
assert_eq!(protocol.name(), "Keyboard");
assert_eq!(protocol.id(), 0x01);
let protocol = Protocol::from_cid_scid_pid(0x07, 0x01, 0x03).unwrap();
assert_eq!(protocol.name(), "IEEE 1284.4 compatible bidirectional");
assert_eq!(protocol.id(), 0x03);
let protocol = Protocol::from_cid_scid_pid(0xff, 0xff, 0xff).unwrap();
assert_eq!(protocol.name(), "Vendor Specific Protocol");
assert_eq!(protocol.id(), 0xff);
}
#[test]
fn test_at_from_id() {
let at = AudioTerminal::from_id(0x0713).unwrap();
assert_eq!(at.name(), "Synthesizer");
assert_eq!(at.id(), 0x0713);
}
#[test]
fn test_hid_from_id() {
let hid = Hid::from_id(0x23).unwrap();
assert_eq!(hid.name(), "Physical");
assert_eq!(hid.id(), 0x23);
}
#[test]
fn test_hid_type_from_id() {
let hid_type = HidItemType::from_id(0xc0).unwrap();
assert_eq!(hid_type.name(), "End Collection");
assert_eq!(hid_type.id(), 0xc0);
}
#[test]
fn test_bias_from_id() {
let bias = Bias::from_id(0x04).unwrap();
assert_eq!(bias.name(), "Either Hand");
assert_eq!(bias.id(), 0x04);
}
#[test]
fn test_phy_from_id() {
let phy = Phy::from_id(0x27).unwrap();
assert_eq!(phy.name(), "Cheek");
assert_eq!(phy.id(), 0x27);
}
#[test]
fn test_hid_usages_from_id() {
let hid_usage_page = HidUsagePage::from_id(0x0d).unwrap();
assert_eq!(hid_usage_page.name(), "Digitizer");
assert_eq!(hid_usage_page.id(), 0x0d);
let hid_usage = HidUsage::from_pageid_uid(0x0d, 0x01).unwrap();
assert_eq!(hid_usage.name(), "Digitizer");
assert_eq!(hid_usage.id(), 0x01);
}
#[test]
fn test_language_from_id() {
let language = Language::from_id(0x0007).unwrap();
assert_eq!(language.name(), "German");
assert_eq!(language.id(), 0x0007);
let dialect = language.dialects().find(|d| d.id() == 0x02).unwrap();
assert_eq!(dialect.name(), "Swiss");
assert_eq!(dialect.id(), 0x02);
}
#[test]
fn test_hid_country_code_from_id() {
let hid_country_code = HidCountryCode::from_id(0x29).unwrap();
assert_eq!(hid_country_code.name(), "Switzerland");
assert_eq!(hid_country_code.id(), 0x29);
let hid_country_code = HidCountryCode::from_id(0x00).unwrap();
assert_eq!(hid_country_code.name(), "Not supported");
}
#[test]
fn test_video_terminal_from_id() {
let video_terminal = VideoTerminal::from_id(0x0100).unwrap();
assert_eq!(video_terminal.name(), "USB Vendor Specific");
assert_eq!(video_terminal.id(), 0x0100);
let video_terminal = VideoTerminal::from_id(0x0403).unwrap();
assert_eq!(video_terminal.name(), "Component Video");
}
}