27
27
"""
28
28
29
29
import json
30
+ import re
30
31
import sys
31
32
from collections .abc import Iterator
32
33
from os import path
65
66
TEMPLATES_DIR = Path (__file__ ).parent / "templates"
66
67
RESOURCES_DIR = Path (__file__ ).parent / "static"
67
68
69
+ # Load and parse binding types from text file
70
+ BINDINGS_TXT_PATH = ZEPHYR_BASE / "dts" / "bindings" / "binding-types.txt"
71
+ ACRONYM_PATTERN = re .compile (r'([a-zA-Z0-9-]+)\s*\((.*?)\)' )
72
+ BINDING_TYPE_TO_DOCUTILS_NODE = {}
73
+
74
+
75
+ def parse_text_with_acronyms (text ):
76
+ """Parse text that may contain acronyms into a list of nodes."""
77
+ result = nodes .inline ()
78
+ last_end = 0
79
+
80
+ for match in ACRONYM_PATTERN .finditer (text ):
81
+ # Add any text before the acronym
82
+ if match .start () > last_end :
83
+ result += nodes .Text (text [last_end : match .start ()])
84
+
85
+ # Add the acronym
86
+ abbr , explanation = match .groups ()
87
+ result += nodes .abbreviation (abbr , abbr , explanation = explanation )
88
+ last_end = match .end ()
89
+
90
+ # Add any remaining text
91
+ if last_end < len (text ):
92
+ result += nodes .Text (text [last_end :])
93
+
94
+ return result
95
+
96
+
97
+ with open (BINDINGS_TXT_PATH ) as f :
98
+ for line in f :
99
+ line = line .strip ()
100
+ if not line or line .startswith ('#' ):
101
+ continue
102
+
103
+ key , value = line .split ('\t ' , 1 )
104
+ BINDING_TYPE_TO_DOCUTILS_NODE [key ] = parse_text_with_acronyms (value )
105
+
68
106
logger = logging .getLogger (__name__ )
69
107
70
108
@@ -685,6 +723,7 @@ def run(self):
685
723
board_node = BoardNode (id = board_name )
686
724
board_node ["full_name" ] = board ["full_name" ]
687
725
board_node ["vendor" ] = vendors .get (board ["vendor" ], board ["vendor" ])
726
+ board_node ["supported_features" ] = board ["supported_features" ]
688
727
board_node ["archs" ] = board ["archs" ]
689
728
board_node ["socs" ] = board ["socs" ]
690
729
board_node ["image" ] = board ["image" ]
@@ -716,6 +755,137 @@ def run(self):
716
755
return [nodes .paragraph (text = "Board catalog is only available in HTML." )]
717
756
718
757
758
+ class BoardSupportedHardwareDirective (SphinxDirective ):
759
+ """A directive for showing the supported hardware features of a board."""
760
+
761
+ has_content = False
762
+ required_arguments = 0
763
+ optional_arguments = 0
764
+
765
+ def run (self ):
766
+ env = self .env
767
+ docname = env .docname
768
+
769
+ matcher = NodeMatcher (BoardNode )
770
+ board_nodes = list (self .state .document .traverse (matcher ))
771
+ if not board_nodes :
772
+ logger .warning (
773
+ "board-supported-hw directive must be used in a board documentation page." ,
774
+ location = (docname , self .lineno ),
775
+ )
776
+ return []
777
+
778
+ board_node = board_nodes [0 ]
779
+ supported_features = board_node ["supported_features" ]
780
+ result_nodes = []
781
+
782
+ paragraph = nodes .paragraph ()
783
+ paragraph += nodes .Text ("The " )
784
+ paragraph += nodes .literal (text = board_node ["id" ])
785
+ paragraph += nodes .Text (" board supports the hardware features listed below." )
786
+ result_nodes .append (paragraph )
787
+
788
+ if not env .app .config .zephyr_generate_hw_features :
789
+ note = nodes .admonition ()
790
+ note += nodes .title (text = "Note" )
791
+ note ["classes" ].append ("warning" )
792
+ note += nodes .paragraph (
793
+ text = "The list of supported hardware features was not generated. Run a full "
794
+ "documentation build for the required metadata to be available."
795
+ )
796
+ result_nodes .append (note )
797
+ return result_nodes
798
+
799
+ # Add the note before any tables
800
+ note = nodes .admonition ()
801
+ note += nodes .title (text = "Note" )
802
+ note ["classes" ].append ("note" )
803
+ note += nodes .paragraph (
804
+ text = "The tables below were automatically generated using information from the "
805
+ "Devicetree. They may not be fully representative of all the hardware features "
806
+ "supported by the board."
807
+ )
808
+ result_nodes .append (note )
809
+
810
+ for target , features in sorted (supported_features .items ()):
811
+ if not features :
812
+ continue
813
+
814
+ target_heading = nodes .section (ids = [f"{ board_node ['id' ]} -{ target } -hw-features" ])
815
+ heading = nodes .title ()
816
+ heading += nodes .literal (text = target )
817
+ heading += nodes .Text (" target" )
818
+ target_heading += heading
819
+ result_nodes .append (target_heading )
820
+
821
+ table = nodes .table (classes = ["colwidths-given" ])
822
+ tgroup = nodes .tgroup (cols = 3 )
823
+
824
+ tgroup += nodes .colspec (colwidth = 20 , classes = ["col-1" ])
825
+ tgroup += nodes .colspec (colwidth = 50 )
826
+ tgroup += nodes .colspec (colwidth = 30 )
827
+
828
+ thead = nodes .thead ()
829
+ row = nodes .row ()
830
+ headers = ["Type" , "Description" , "Compatible" ]
831
+ for header in headers :
832
+ row += nodes .entry ("" , nodes .paragraph (text = header ))
833
+ thead += row
834
+ tgroup += thead
835
+
836
+ tbody = nodes .tbody ()
837
+
838
+ def feature_sort_key (feature ):
839
+ # Put "CPU" first. Later updates might also give priority to features
840
+ # like "sensor"s, for example.
841
+ if feature == "cpu" :
842
+ return (0 , feature )
843
+ return (1 , feature )
844
+
845
+ sorted_features = sorted (features .keys (), key = feature_sort_key )
846
+
847
+ for feature in sorted_features :
848
+ items = list (features [feature ].items ())
849
+ num_items = len (items )
850
+
851
+ for i , (key , value ) in enumerate (items ):
852
+ row = nodes .row ()
853
+
854
+ # Add type column only for first row of a feature
855
+ if i == 0 :
856
+ type_entry = nodes .entry (morerows = num_items - 1 )
857
+ type_entry += nodes .paragraph (
858
+ "" ,
859
+ "" ,
860
+ BINDING_TYPE_TO_DOCUTILS_NODE .get (
861
+ feature , nodes .Text (feature )
862
+ ).deepcopy (),
863
+ )
864
+ row += type_entry
865
+
866
+ row += nodes .entry ("" , nodes .paragraph (text = value ))
867
+
868
+ # Create compatible xref
869
+ xref = addnodes .pending_xref (
870
+ "" ,
871
+ refdomain = "std" ,
872
+ reftype = "dtcompatible" ,
873
+ reftarget = key ,
874
+ refexplicit = False ,
875
+ refwarn = True ,
876
+ )
877
+ xref += nodes .literal (text = key )
878
+ row += nodes .entry ("" , nodes .paragraph ("" , "" , xref ))
879
+
880
+ tbody += row
881
+
882
+ tgroup += tbody
883
+ table += tgroup
884
+ result_nodes .append (table )
885
+
886
+ return result_nodes
887
+
888
+
719
889
class ZephyrDomain (Domain ):
720
890
"""Zephyr domain"""
721
891
@@ -734,6 +904,7 @@ class ZephyrDomain(Domain):
734
904
"code-sample-category" : CodeSampleCategoryDirective ,
735
905
"board-catalog" : BoardCatalogDirective ,
736
906
"board" : BoardDirective ,
907
+ "board-supported-hw" : BoardSupportedHardwareDirective ,
737
908
}
738
909
739
910
object_types : dict [str , ObjType ] = {
0 commit comments