153 lines
5.8 KiB
Python
153 lines
5.8 KiB
Python
#!/usr/bin/env python3
|
|
#
|
|
# This file is part of the MicroPython project, http://micropython.org/
|
|
#
|
|
# The MIT License (MIT)
|
|
#
|
|
# Copyright (c) 2020 Michael Schroeder
|
|
#
|
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
# of this software and associated documentation files (the "Software"), to deal
|
|
# in the Software without restriction, including without limitation the rights
|
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
# copies of the Software, and to permit persons to whom the Software is
|
|
# furnished to do so, subject to the following conditions:
|
|
#
|
|
# The above copyright notice and this permission notice shall be included in
|
|
# all copies or substantial portions of the Software.
|
|
#
|
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
# THE SOFTWARE.
|
|
|
|
from collections import defaultdict
|
|
import argparse
|
|
import pathlib
|
|
import re
|
|
import sys
|
|
|
|
DEFAULT_CLUSTERLIST = {
|
|
"0x04D8:0xEC44": ["pycubed", "pycubed_mram", "pycubed_mram_v05", "pycubed_v05"],
|
|
"0x1B4F:0x8D24": ["sparkfun_qwiic_micro_no_flash", "sparkfun_qwiic_micro_with_flash"],
|
|
"0x1D50:0x6153": [
|
|
"jpconstantineau_pykey18",
|
|
"jpconstantineau_pykey44",
|
|
"jpconstantineau_pykey60",
|
|
"jpconstantineau_pykey87",
|
|
],
|
|
"0x239A:0x8019": [
|
|
"circuitplayground_express",
|
|
"circuitplayground_express_crickit",
|
|
"circuitplayground_express_displayio",
|
|
],
|
|
"0x239A:0x801F": ["trinket_m0_haxpress", "trinket_m0"],
|
|
"0x239A:0x8021": ["metro_m4_express", "cp32-m4"],
|
|
"0x239A:0x8023": ["feather_m0_express", "feather_m0_supersized"],
|
|
"0x239A:0x80A6": ["espressif_esp32s2_devkitc_1_n4r2", "espressif_saola_1_wrover"],
|
|
"0x239A:0x80AC": ["unexpectedmaker_feathers2", "unexpectedmaker_feathers2_prerelease"],
|
|
"0x239A:0x80C8": ["espressif_kaluga_1", "espressif_kaluga_1.3"],
|
|
"0x303A:0x7003": [
|
|
"espressif_esp32s3_devkitc_1_n8",
|
|
"espressif_esp32s3_devkitc_1_n8r2",
|
|
"espressif_esp32s3_devkitc_1_n8r8",
|
|
"espressif_esp32s3_devkitc_1_n32r8",
|
|
],
|
|
"0x303A:0x7009": [
|
|
"espressif_esp32s2_devkitc_1_n4",
|
|
"espressif_esp32s2_devkitc_1_n4r2",
|
|
"espressif_esp32s2_devkitc_1_n8r2",
|
|
],
|
|
"0x239A:0x102E": ["weact_studio_pico", "weact_studio_pico_16mb"],
|
|
}
|
|
|
|
cli_parser = argparse.ArgumentParser(
|
|
description="USB VID/PID and Creator/Creation ID Duplicate Checker"
|
|
)
|
|
|
|
|
|
def configboard_files():
|
|
"""A pathlib glob search for all ports/*/boards/*/mpconfigboard.mk file
|
|
paths.
|
|
|
|
:returns: A ``pathlib.Path.glob()`` generator object
|
|
"""
|
|
working_dir = pathlib.Path(__file__).resolve().parent.parent
|
|
return working_dir.glob("ports/**/boards/**/mpconfigboard.mk")
|
|
|
|
|
|
VID_PATTERN = re.compile(r"^USB_VID\s*=\s*(.*)", flags=re.M)
|
|
PID_PATTERN = re.compile(r"^USB_PID\s*=\s*(.*)", flags=re.M)
|
|
CREATOR_PATTERN = re.compile(r"^CIRCUITPY_CREATOR_ID\s*=\s*(.*)", flags=re.M)
|
|
CREATION_PATTERN = re.compile(r"^CIRCUITPY_CREATION_ID\s*=\s*(.*)", flags=re.M)
|
|
|
|
|
|
def check_vid_pid(files, clusterlist):
|
|
"""Compiles a list of USB VID & PID values for all boards, and checks
|
|
for duplicates. Exits with ``sys.exit()`` (non-zero exit code)
|
|
if duplicates are found, and lists the duplicates.
|
|
"""
|
|
|
|
usb_pattern = re.compile(r"^CIRCUITPY_USB\s*=\s*0$|^IDF_TARGET = (esp32|esp32c3)$", flags=re.M)
|
|
|
|
usb_ids = defaultdict(set)
|
|
for board_config in files:
|
|
src_text = board_config.read_text()
|
|
|
|
usb_vid = VID_PATTERN.search(src_text)
|
|
usb_pid = PID_PATTERN.search(src_text)
|
|
creator = CREATOR_PATTERN.search(src_text)
|
|
creation = CREATION_PATTERN.search(src_text)
|
|
non_usb = usb_pattern.search(src_text)
|
|
board_name = board_config.parts[-2]
|
|
|
|
if usb_vid and usb_pid:
|
|
id_group = f"0x{int(usb_vid.group(1), 16):04X}:0x{int(usb_pid.group(1), 16):04X}"
|
|
elif non_usb:
|
|
if creator is None or creation is None:
|
|
print(f"{board_name=} {creator=} {creation=}", file=sys.stderr)
|
|
continue
|
|
id_group = f"0x{int(creator.group(1), 16):08X}:0x{int(creation.group(1), 16):08X}"
|
|
else:
|
|
raise SystemExit(f"Could not find expected settings in {board_config}")
|
|
|
|
usb_ids[id_group].add(board_name)
|
|
|
|
duplicates = []
|
|
for key, boards in usb_ids.items():
|
|
if len(boards) == 1:
|
|
continue
|
|
|
|
# It is a cluster
|
|
cluster = set(clusterlist.get(key, []))
|
|
if cluster != boards:
|
|
if key == "":
|
|
duplicates.append(f"- Non-USB:\n" f" Boards: {', '.join(sorted(boards))}")
|
|
else:
|
|
duplicates.append(f"- VID/PID: {key}\n" f" Boards: {', '.join(sorted(boards))}")
|
|
|
|
if duplicates:
|
|
duplicates = "\n".join(duplicates)
|
|
duplicate_message = (
|
|
f"Duplicate VID/PID usage found!\n{duplicates}\n"
|
|
f"If you are open source maker, then you can request a PID from http://pid.codes\n"
|
|
f"For boards without native USB, you can request a Creator ID from https://github.com/creationid/creators/\n"
|
|
f"Otherwise, companies should pay the USB-IF for a vendor ID: https://www.usb.org/getting-vendor-id"
|
|
)
|
|
sys.exit(duplicate_message)
|
|
|
|
else:
|
|
print("No unexpected ID duplicates found.")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
arguments = cli_parser.parse_args()
|
|
|
|
print("Running USB VID/PID and Creator/Creation ID Duplicate Checker...")
|
|
|
|
board_files = configboard_files()
|
|
check_vid_pid(board_files, DEFAULT_CLUSTERLIST)
|