24
24
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25
25
# THE SOFTWARE.
26
26
27
+ from collections import defaultdict
27
28
import argparse
28
29
import pathlib
29
30
import re
30
31
import sys
31
32
32
- DEFAULT_IGNORELIST = [
33
- "circuitplayground_express" ,
34
- "circuitplayground_express_crickit" ,
35
- "circuitplayground_express_displayio" ,
36
- "pycubed" ,
37
- "pycubed_mram" ,
38
- "pycubed_v05" ,
39
- "pycubed_mram_v05" ,
40
- "pygamer" ,
41
- "pygamer_advance" ,
42
- "trinket_m0" ,
43
- "trinket_m0_haxpress" ,
44
- "sparkfun_qwiic_micro_with_flash" ,
45
- "sparkfun_qwiic_micro_no_flash" ,
46
- "feather_m0_express" ,
47
- "feather_m0_supersized" ,
48
- "cp32-m4" ,
49
- "metro_m4_express" ,
50
- "unexpectedmaker_feathers2" ,
51
- "unexpectedmaker_feathers2_prerelease" ,
52
- "espressif_kaluga_1" ,
53
- "espressif_kaluga_1.3" ,
54
- "espressif_esp32s2_devkitc_1_n4" ,
55
- "espressif_esp32s2_devkitc_1_n4r2" ,
56
- "espressif_esp32s3_devkitc_1_n8" ,
57
- "espressif_esp32s3_devkitc_1_n8r2" ,
58
- "espressif_esp32s3_devkitc_1_n8r8" ,
59
- "espressif_saola_1_wrover" ,
60
- "jpconstantineau_pykey18" ,
61
- "jpconstantineau_pykey44" ,
62
- "jpconstantineau_pykey60" ,
63
- "jpconstantineau_pykey87" ,
64
- ]
33
+ DEFAULT_CLUSTERLIST = {
34
+ "0x04D8:0xEC44" : ["pycubed" , "pycubed_mram" , "pycubed_mram_v05" , "pycubed_v05" ],
35
+ "0x1B4F:0x8D24" : ["sparkfun_qwiic_micro_no_flash" , "sparkfun_qwiic_micro_with_flash" ],
36
+ "0x1D50:0x6153" : [
37
+ "jpconstantineau_pykey18" ,
38
+ "jpconstantineau_pykey44" ,
39
+ "jpconstantineau_pykey60" ,
40
+ "jpconstantineau_pykey87" ,
41
+ ],
42
+ "0x239A:0x8019" : [
43
+ "circuitplayground_express" ,
44
+ "circuitplayground_express_crickit" ,
45
+ "circuitplayground_express_displayio" ,
46
+ ],
47
+ "0x239A:0x801F" : ["trinket_m0_haxpress" , "trinket_m0" ],
48
+ "0x239A:0x8021" : ["metro_m4_express" , "cp32-m4" ],
49
+ "0x239A:0x8023" : ["feather_m0_express" , "feather_m0_supersized" ],
50
+ "0x239A:0x80A6" : ["espressif_esp32s2_devkitc_1_n4r2" , "espressif_saola_1_wrover" ],
51
+ "0x239A:0x80AC" : ["unexpectedmaker_feathers2" , "unexpectedmaker_feathers2_prerelease" ],
52
+ "0x239A:0x80C8" : ["espressif_kaluga_1" , "espressif_kaluga_1.3" ],
53
+ "0x303A:0x7003" : [
54
+ "espressif_esp32s3_devkitc_1_n8" ,
55
+ "espressif_esp32s3_devkitc_1_n8r2" ,
56
+ "espressif_esp32s3_devkitc_1_n8r8" ,
57
+ ],
58
+ "0x303A:0x7009" : ["espressif_esp32s2_devkitc_1_n4" , "espressif_esp32s2_devkitc_1_n4r2" ],
59
+ }
65
60
66
61
cli_parser = argparse .ArgumentParser (description = "USB VID/PID Duplicate Checker" )
67
- cli_parser .add_argument (
68
- "--ignorelist" ,
69
- dest = "ignorelist" ,
70
- nargs = "?" ,
71
- action = "store" ,
72
- default = DEFAULT_IGNORELIST ,
73
- help = (
74
- "Board names to ignore duplicate VID/PID combinations. Pass an empty "
75
- "string to disable all duplicate ignoring. Defaults are: "
76
- f"{ ', ' .join (DEFAULT_IGNORELIST )} "
77
- ),
78
- )
79
62
80
63
81
64
def configboard_files ():
@@ -88,48 +71,49 @@ def configboard_files():
88
71
return working_dir .glob ("ports/**/boards/**/mpconfigboard.mk" )
89
72
90
73
91
- def check_vid_pid (files , ignorelist ):
74
+ def check_vid_pid (files , clusterlist ):
92
75
"""Compiles a list of USB VID & PID values for all boards, and checks
93
76
for duplicates. Exits with ``sys.exit()`` (non-zero exit code)
94
77
if duplicates are found, and lists the duplicates.
95
78
"""
96
79
97
- duplicates_found = False
98
-
99
- usb_ids = {}
100
-
101
- vid_pattern = re .compile (r"^USB_VID\s*\=\s*(.*)" , flags = re .M )
102
- pid_pattern = re .compile (r"^USB_PID\s*\=\s*(.*)" , flags = re .M )
80
+ vid_pattern = re .compile (r"^USB_VID\s*=\s*(.*)" , flags = re .M )
81
+ pid_pattern = re .compile (r"^USB_PID\s*=\s*(.*)" , flags = re .M )
82
+ usb_pattern = re .compile (r"^CIRCUITPY_USB\s*=\s*0$|^IDF_TARGET = esp32c3$" , flags = re .M )
103
83
84
+ usb_ids = defaultdict (set )
104
85
for board_config in files :
105
86
src_text = board_config .read_text ()
106
87
107
88
usb_vid = vid_pattern .search (src_text )
108
89
usb_pid = pid_pattern .search (src_text )
109
-
90
+ non_usb = usb_pattern . search ( src_text )
110
91
board_name = board_config .parts [- 2 ]
111
92
112
- board_ignorelisted = False
113
- if board_name in ignorelist :
114
- board_ignorelisted = True
115
- board_name += " (ignorelisted)"
116
-
117
93
if usb_vid and usb_pid :
118
- id_group = f"{ usb_vid .group (1 )} :{ usb_pid .group (1 )} "
119
- if id_group not in usb_ids :
120
- usb_ids [id_group ] = {"boards" : [board_name ], "duplicate" : False }
94
+ id_group = f"0x{ int (usb_vid .group (1 ), 16 ):04X} :0x{ int (usb_pid .group (1 ), 16 ):04X} "
95
+ elif non_usb :
96
+ continue
97
+ else :
98
+ raise SystemExit (f"Could not parse { board_config } " )
99
+
100
+ usb_ids [id_group ].add (board_name )
101
+
102
+ duplicates = []
103
+ for key , boards in usb_ids .items ():
104
+ if len (boards ) == 1 :
105
+ continue
106
+
107
+ # It is a cluster
108
+ cluster = set (clusterlist .get (key , []))
109
+ if cluster != boards :
110
+ if key == "" :
111
+ duplicates .append (f"- Non-USB:\n " f" Boards: { ', ' .join (sorted (boards ))} " )
121
112
else :
122
- usb_ids [id_group ]["boards" ].append (board_name )
123
- if not board_ignorelisted :
124
- usb_ids [id_group ]["duplicate" ] = True
125
- duplicates_found = True
126
-
127
- if duplicates_found :
128
- duplicates = ""
129
- for key , value in usb_ids .items ():
130
- if value ["duplicate" ]:
131
- duplicates += f"- VID/PID: { key } \n " f" Boards: { ', ' .join (value ['boards' ])} \n "
113
+ duplicates .append (f"- VID/PID: { key } \n " f" Boards: { ', ' .join (sorted (boards ))} " )
132
114
115
+ if duplicates :
116
+ duplicates = "\n " .join (duplicates )
133
117
duplicate_message = (
134
118
f"Duplicate VID/PID usage found!\n { duplicates } \n "
135
119
f"If you are open source maker, then you can request a PID from http://pid.codes\n "
@@ -144,7 +128,6 @@ def check_vid_pid(files, ignorelist):
144
128
arguments = cli_parser .parse_args ()
145
129
146
130
print ("Running USB VID/PID Duplicate Checker..." )
147
- print (f"Ignoring the following boards: { ', ' .join (arguments .ignorelist )} " , end = "\n \n " )
148
131
149
132
board_files = configboard_files ()
150
- check_vid_pid (board_files , arguments . ignorelist )
133
+ check_vid_pid (board_files , DEFAULT_CLUSTERLIST )
0 commit comments