@@ -193,6 +193,8 @@ pub struct CompositeDeviceConfigOptions {
193193pub struct Match {
194194 #[ serde( skip_serializing_if = "Option::is_none" ) ]
195195 pub dmi_data : Option < DMIMatch > ,
196+ #[ serde( skip_serializing_if = "Option::is_none" ) ]
197+ pub udev : Option < Udev > ,
196198}
197199
198200/// Match DMI data for loading a [CompositeDevice]
@@ -905,6 +907,138 @@ impl CompositeDeviceConfig {
905907 let conf = match_config. clone ( ) ;
906908 let mut has_matches = false ;
907909
910+ if let Some ( udev) = match_config. udev {
911+ let Some ( sys_path) = udev. sys_path else {
912+ log:: warn!( "Match using udev MUST define 'sys_path'" ) ;
913+ return None ;
914+ } ;
915+
916+ let device = UdevDevice :: from_syspath ( sys_path. as_str ( ) ) ;
917+
918+ if let Some ( subsystem_pattern) = udev. subsystem {
919+ let subsystem = device. subsystem ( ) ;
920+ if !glob_match ( & subsystem_pattern, & subsystem) {
921+ continue ;
922+ }
923+ has_matches = true ;
924+ }
925+
926+ if let Some ( driver_pattern) = udev. driver {
927+ let drivers = device. drivers ( ) ;
928+ let mut has_matching_driver = false ;
929+ for driver in drivers {
930+ if !glob_match ( & driver_pattern, & driver) {
931+ continue ;
932+ }
933+ has_matching_driver = true ;
934+ break ;
935+ }
936+ has_matches = has_matching_driver;
937+ }
938+
939+ if let Some ( sys_name_pattern) = udev. sys_name {
940+ let sys_name = device. sysname ( ) ;
941+ if !glob_match ( & sys_name_pattern, & sys_name) {
942+ continue ;
943+ }
944+ has_matches = true ;
945+ }
946+
947+ if let Some ( dev_node_pattern) = udev. dev_node {
948+ let dev_node = device. devnode ( ) ;
949+ if !glob_match ( & dev_node_pattern, & dev_node) {
950+ continue ;
951+ }
952+ has_matches = true ;
953+ }
954+
955+ if let Some ( dev_path_pattern) = udev. dev_path {
956+ let dev_path = device. devpath ( ) ;
957+ if !glob_match ( & dev_path_pattern, & dev_path) {
958+ continue ;
959+ }
960+ has_matches = true ;
961+ }
962+
963+ if let Some ( vendor_id_pattern) = udev. vendor_id {
964+ let vendor_id = device. id_vendor ( ) . to_string ( ) ;
965+ if !glob_match ( & vendor_id_pattern, & vendor_id) {
966+ continue ;
967+ }
968+ has_matches = true ;
969+ }
970+
971+ if let Some ( product_id_pattern) = udev. product_id {
972+ let product_id = device. id_product ( ) . to_string ( ) ;
973+ if !glob_match ( & product_id_pattern, & product_id) {
974+ continue ;
975+ }
976+ has_matches = true ;
977+ }
978+
979+ if let Some ( iface_num_pattern) = udev. interface_number {
980+ let iface_num = device. interface_number ( ) . to_string ( ) ;
981+ if !glob_match ( & iface_num_pattern, & iface_num) {
982+ continue ;
983+ }
984+ has_matches = true ;
985+ }
986+
987+ if let Some ( attribute_patterns) = udev. attributes {
988+ let attributes = device. get_attributes ( ) ;
989+ let mut all_attributes_match = true ;
990+
991+ // All attribute patterns in the config must match
992+ for attr_pattern in attribute_patterns {
993+ let attr_name = attr_pattern. name ;
994+ let Some ( attr_value) = attributes. get ( & attr_name) else {
995+ // If the attribute was not found, this is not a match
996+ all_attributes_match = false ;
997+ break ;
998+ } ;
999+
1000+ let Some ( value_pattern) = attr_pattern. value else {
1001+ // If no value was given, then assume '**' pattern
1002+ continue ;
1003+ } ;
1004+
1005+ if !glob_match ( & value_pattern, attr_value) {
1006+ all_attributes_match = false ;
1007+ break ;
1008+ }
1009+ }
1010+
1011+ has_matches = all_attributes_match;
1012+ }
1013+
1014+ if let Some ( property_patterns) = udev. properties {
1015+ let properties = device. get_properties ( ) ;
1016+ let mut all_properties_match = true ;
1017+
1018+ // All property patterns in the config must match
1019+ for prop_pattern in property_patterns {
1020+ let prop_name = prop_pattern. name ;
1021+ let Some ( prop_value) = properties. get ( & prop_name) else {
1022+ // If the attribute was not found, this is not a match
1023+ all_properties_match = false ;
1024+ break ;
1025+ } ;
1026+
1027+ let Some ( value_pattern) = prop_pattern. value else {
1028+ // If no value was given, then assume '**' pattern
1029+ continue ;
1030+ } ;
1031+
1032+ if !glob_match ( & value_pattern, prop_value) {
1033+ all_properties_match = false ;
1034+ break ;
1035+ }
1036+ }
1037+
1038+ has_matches = all_properties_match;
1039+ }
1040+ }
1041+
9081042 if let Some ( dmi_config) = match_config. dmi_data {
9091043 if let Some ( cpu_vendor) = dmi_config. cpu_vendor {
9101044 if !glob_match (
0 commit comments