use alloc::{string::String, vec::Vec, string::ToString};
use crate::error::{PciError, PciResult};
use crate::types::*;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum ParsingMode {
Vendors,
Classes,
}
#[derive(Debug)]
#[allow(dead_code)]
pub struct VendorBuilder {
pub id: VendorId,
pub name: String,
pub devices: Vec<DeviceBuilder>,
}
#[derive(Debug)]
#[allow(dead_code)]
pub struct DeviceBuilder {
pub id: DeviceId,
pub name: String,
pub subsystems: Vec<SubsystemBuilder>,
}
#[derive(Debug)]
#[allow(dead_code)]
pub struct SubsystemBuilder {
pub subvendor_id: SubvendorId,
pub subdevice_id: SubdeviceId,
pub name: String,
}
#[derive(Debug)]
#[allow(dead_code)]
pub struct ClassBuilder {
pub id: DeviceClassId,
pub name: String,
pub subclasses: Vec<SubClassBuilder>,
}
#[derive(Debug)]
#[allow(dead_code)]
pub struct SubClassBuilder {
pub id: SubClassId,
pub name: String,
pub prog_interfaces: Vec<ProgInterfaceBuilder>,
}
#[derive(Debug)]
#[allow(dead_code)]
pub struct ProgInterfaceBuilder {
pub id: ProgInterfaceId,
pub name: String,
}
pub struct PciIdsParser {
vendors: Vec<VendorBuilder>,
classes: Vec<ClassBuilder>,
}
impl PciIdsParser {
pub fn new() -> Self {
Self {
vendors: Vec::new(),
classes: Vec::new(),
}
}
pub fn parse(&mut self, content: &str) -> PciResult<()> {
self.vendors.clear();
self.classes.clear();
let mut current_vendor: Option<VendorBuilder> = None;
let mut current_device: Option<DeviceBuilder> = None;
let mut current_class: Option<ClassBuilder> = None;
let mut current_subclass: Option<SubClassBuilder> = None;
let mut parsing_mode = ParsingMode::Vendors;
for (_line_num, line) in content.lines().enumerate() {
if line.trim().is_empty() || line.trim().starts_with('#') {
continue;
}
if line.trim().starts_with("C ") && count_leading_tabs(line) == 0 {
parsing_mode = ParsingMode::Classes;
self.finalize_vendor_device(&mut current_vendor, &mut current_device)?;
} else if count_leading_tabs(line) == 0 && !line.trim().starts_with("C ") && parsing_mode == ParsingMode::Classes {
if line.trim().len() >= 6 && line.trim().chars().nth(4) == Some(' ') && line.trim().chars().nth(5) == Some(' ') {
let hex_part = &line.trim()[..4];
if hex_part.chars().all(|c| c.is_ascii_hexdigit()) {
parsing_mode = ParsingMode::Vendors;
self.finalize_class_subclass(&mut current_class, &mut current_subclass)?;
}
}
}
let indentation = count_leading_tabs(line);
let trimmed = line.trim();
let result = match parsing_mode {
ParsingMode::Vendors => self.parse_vendor_section(
trimmed,
indentation,
&mut current_vendor,
&mut current_device,
),
ParsingMode::Classes => self.parse_class_section(
trimmed,
indentation,
&mut current_class,
&mut current_subclass,
),
};
if let Err(e) = result {
return Err(e);
}
}
self.finalize_vendor_device(&mut current_vendor, &mut current_device)?;
self.finalize_class_subclass(&mut current_class, &mut current_subclass)?;
Ok(())
}
fn parse_vendor_section(
&mut self,
trimmed: &str,
indentation: usize,
current_vendor: &mut Option<VendorBuilder>,
current_device: &mut Option<DeviceBuilder>,
) -> PciResult<()> {
match indentation {
0 => {
self.finalize_vendor_device(current_vendor, current_device)?;
let (id, name) = parse_vendor_line(trimmed)?;
*current_vendor = Some(VendorBuilder {
id,
name,
devices: Vec::new(),
});
}
1 => {
if let Some(device) = current_device.take() {
if let Some(ref mut vendor) = current_vendor {
vendor.devices.push(device);
}
}
let (id, name) = parse_device_line(trimmed)?;
*current_device = Some(DeviceBuilder {
id,
name,
subsystems: Vec::new(),
});
}
2 => {
if let Some(ref mut device) = current_device {
let (subvendor_id, subdevice_id, name) = parse_subsystem_line(trimmed)?;
device.subsystems.push(SubsystemBuilder {
subvendor_id,
subdevice_id,
name,
});
}
}
_ => {
return Err(PciError::InvalidIndentation);
}
}
Ok(())
}
fn parse_class_section(
&mut self,
trimmed: &str,
indentation: usize,
current_class: &mut Option<ClassBuilder>,
current_subclass: &mut Option<SubClassBuilder>,
) -> PciResult<()> {
match indentation {
0 => {
self.finalize_class_subclass(current_class, current_subclass)?;
if trimmed.starts_with("C ") {
let (id, name) = parse_class_line(trimmed)?;
*current_class = Some(ClassBuilder {
id,
name,
subclasses: Vec::new(),
});
}
}
1 => {
if let Some(subclass) = current_subclass.take() {
if let Some(ref mut class) = current_class {
class.subclasses.push(subclass);
}
}
let (id, name) = parse_subclass_line(trimmed)?;
*current_subclass = Some(SubClassBuilder {
id,
name,
prog_interfaces: Vec::new(),
});
}
2 => {
if let Some(ref mut subclass) = current_subclass {
let (id, name) = parse_prog_interface_line(trimmed)?;
subclass.prog_interfaces.push(ProgInterfaceBuilder { id, name });
}
}
_ => {
return Err(PciError::InvalidIndentation);
}
}
Ok(())
}
fn finalize_vendor_device(
&mut self,
current_vendor: &mut Option<VendorBuilder>,
current_device: &mut Option<DeviceBuilder>,
) -> PciResult<()> {
if let Some(device) = current_device.take() {
if let Some(ref mut vendor) = current_vendor {
vendor.devices.push(device);
}
}
if let Some(vendor) = current_vendor.take() {
self.vendors.push(vendor);
}
Ok(())
}
fn finalize_class_subclass(
&mut self,
current_class: &mut Option<ClassBuilder>,
current_subclass: &mut Option<SubClassBuilder>,
) -> PciResult<()> {
if let Some(subclass) = current_subclass.take() {
if let Some(ref mut class) = current_class {
class.subclasses.push(subclass);
}
}
if let Some(class) = current_class.take() {
self.classes.push(class);
}
Ok(())
}
#[allow(dead_code)]
pub fn vendors(&self) -> &[VendorBuilder] {
&self.vendors
}
#[allow(dead_code)]
pub fn classes(&self) -> &[ClassBuilder] {
&self.classes
}
pub fn generate_code(&self) -> String {
let mut code = String::new();
code.push_str("// Generated PCI vendor and device data\n");
code.push_str("use crate::vendors::Vendor;\n");
code.push_str("use crate::devices::{Device, Subsystem};\n");
code.push_str("use crate::classes::{DeviceClass, SubClass, ProgInterface};\n");
code.push_str("use crate::types::*;\n\n");
code
}
}
impl Default for PciIdsParser {
fn default() -> Self {
Self::new()
}
}
fn count_leading_tabs(line: &str) -> usize {
line.chars().take_while(|&c| c == '\t').count()
}
fn parse_vendor_line(line: &str) -> PciResult<(VendorId, String)> {
let parts: Vec<&str> = line.splitn(2, " ").collect();
if parts.len() != 2 {
return Err(PciError::InvalidFormat);
}
let id = parse_hex_u16(parts[0])?;
let name = parts[1].trim().to_string();
Ok((VendorId::new(id), name))
}
fn parse_device_line(line: &str) -> PciResult<(DeviceId, String)> {
let parts: Vec<&str> = line.splitn(2, " ").collect();
if parts.len() != 2 {
return Err(PciError::InvalidFormat);
}
let id = parse_hex_u16(parts[0])?;
let name = parts[1].trim().to_string();
Ok((DeviceId::new(id), name))
}
fn parse_subsystem_line(line: &str) -> PciResult<(SubvendorId, SubdeviceId, String)> {
let parts: Vec<&str> = line.splitn(2, " ").collect();
if parts.len() != 2 {
return Err(PciError::InvalidFormat);
}
let ids: Vec<&str> = parts[0].split_whitespace().collect();
if ids.len() != 2 {
return Err(PciError::InvalidFormat);
}
let subvendor_id = parse_hex_u16(ids[0])?;
let subdevice_id = parse_hex_u16(ids[1])?;
let name = parts[1].trim().to_string();
Ok((SubvendorId::new(subvendor_id), SubdeviceId::new(subdevice_id), name))
}
fn parse_class_line(line: &str) -> PciResult<(DeviceClassId, String)> {
if !line.starts_with("C ") {
return Err(PciError::InvalidFormat);
}
let rest = &line[2..]; let parts: Vec<&str> = rest.splitn(2, " ").collect();
if parts.len() != 2 {
return Err(PciError::InvalidFormat);
}
let id = parse_hex_u8(parts[0])?;
let name = parts[1].trim().to_string();
Ok((DeviceClassId::new(id), name))
}
fn parse_subclass_line(line: &str) -> PciResult<(SubClassId, String)> {
let parts: Vec<&str> = line.splitn(2, " ").collect();
if parts.len() != 2 {
return Err(PciError::InvalidFormat);
}
let id = parse_hex_u8(parts[0])?;
let name = parts[1].trim().to_string();
Ok((SubClassId::new(id), name))
}
fn parse_prog_interface_line(line: &str) -> PciResult<(ProgInterfaceId, String)> {
let parts: Vec<&str> = line.splitn(2, " ").collect();
if parts.len() != 2 {
return Err(PciError::InvalidFormat);
}
let id = parse_hex_u8(parts[0])?;
let name = parts[1].trim().to_string();
Ok((ProgInterfaceId::new(id), name))
}
fn parse_hex_u16(hex_str: &str) -> PciResult<u16> {
u16::from_str_radix(hex_str.trim(), 16).map_err(|_| PciError::InvalidHexValue)
}
fn parse_hex_u8(hex_str: &str) -> PciResult<u8> {
u8::from_str_radix(hex_str.trim(), 16).map_err(|_| PciError::InvalidHexValue)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_basic_vendor_parsing() {
let content = r#"
# Test PCI IDs
1234 Test Vendor
5678 Test Device
abcd 1234 Test Subsystem
"#;
let mut parser = PciIdsParser::new();
parser.parse(content).expect("Failed to parse");
assert_eq!(parser.vendors.len(), 1);
let vendor = &parser.vendors[0];
assert_eq!(vendor.id.value(), 0x1234);
assert_eq!(vendor.name, "Test Vendor");
assert_eq!(vendor.devices.len(), 1);
let device = &vendor.devices[0];
assert_eq!(device.id.value(), 0x5678);
assert_eq!(device.name, "Test Device");
assert_eq!(device.subsystems.len(), 1);
let subsystem = &device.subsystems[0];
assert_eq!(subsystem.subvendor_id.value(), 0xabcd);
assert_eq!(subsystem.subdevice_id.value(), 0x1234);
assert_eq!(subsystem.name, "Test Subsystem");
}
#[test]
fn test_basic_class_parsing() {
let content = r#"
C 02 Network controller
00 Ethernet controller
01 Token ring network controller
00 Basic token ring
01 Advanced token ring
C 03 Display controller
00 VGA compatible controller
00 VGA controller
01 8514 controller
"#;
let mut parser = PciIdsParser::new();
parser.parse(content).expect("Failed to parse");
assert_eq!(parser.classes.len(), 2);
let network_class = &parser.classes[0];
assert_eq!(network_class.id.value(), 0x02);
assert_eq!(network_class.name, "Network controller");
assert_eq!(network_class.subclasses.len(), 2);
let ethernet_subclass = &network_class.subclasses[0];
assert_eq!(ethernet_subclass.id.value(), 0x00);
assert_eq!(ethernet_subclass.name, "Ethernet controller");
assert_eq!(ethernet_subclass.prog_interfaces.len(), 0);
let token_ring_subclass = &network_class.subclasses[1];
assert_eq!(token_ring_subclass.id.value(), 0x01);
assert_eq!(token_ring_subclass.name, "Token ring network controller");
assert_eq!(token_ring_subclass.prog_interfaces.len(), 2);
let display_class = &parser.classes[1];
assert_eq!(display_class.id.value(), 0x03);
assert_eq!(display_class.name, "Display controller");
assert_eq!(display_class.subclasses.len(), 1);
}
#[test]
fn test_mixed_parsing() {
let content = r#"
1234 Test Vendor
5678 Test Device
C 02 Network controller
00 Ethernet controller
"#;
let mut parser = PciIdsParser::new();
parser.parse(content).expect("Failed to parse");
assert_eq!(parser.vendors.len(), 1);
assert_eq!(parser.classes.len(), 1);
}
}