2727"""
2828
2929import json
30+ import re
3031import sys
3132from collections .abc import Iterator
3233from os import path
6566TEMPLATES_DIR = Path (__file__ ).parent / "templates"
6667RESOURCES_DIR = Path (__file__ ).parent / "static"
6768
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+
68106logger = logging .getLogger (__name__ )
69107
70108
@@ -685,6 +723,7 @@ def run(self):
685723 board_node = BoardNode (id = board_name )
686724 board_node ["full_name" ] = board ["full_name" ]
687725 board_node ["vendor" ] = vendors .get (board ["vendor" ], board ["vendor" ])
726+ board_node ["supported_features" ] = board ["supported_features" ]
688727 board_node ["archs" ] = board ["archs" ]
689728 board_node ["socs" ] = board ["socs" ]
690729 board_node ["image" ] = board ["image" ]
@@ -716,6 +755,137 @@ def run(self):
716755 return [nodes .paragraph (text = "Board catalog is only available in HTML." )]
717756
718757
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+
719889class ZephyrDomain (Domain ):
720890 """Zephyr domain"""
721891
@@ -734,6 +904,7 @@ class ZephyrDomain(Domain):
734904 "code-sample-category" : CodeSampleCategoryDirective ,
735905 "board-catalog" : BoardCatalogDirective ,
736906 "board" : BoardDirective ,
907+ "board-supported-hw" : BoardSupportedHardwareDirective ,
737908 }
738909
739910 object_types : dict [str , ObjType ] = {
0 commit comments