|
| 1 | +#!/usr/bin/env python3 |
| 2 | +# -*- coding: utf-8 -*- |
| 3 | +# |
| 4 | +# Copyright (c) 2020, Niklas Hauser |
| 5 | +# |
| 6 | +# This file is part of the modm project. |
| 7 | +# |
| 8 | +# This Source Code Form is subject to the terms of the Mozilla Public |
| 9 | +# License, v. 2.0. If a copy of the MPL was not distributed with this |
| 10 | +# file, You can obtain one at http://mozilla.org/MPL/2.0/. |
| 11 | +# ----------------------------------------------------------------------------- |
| 12 | + |
| 13 | +from pathlib import Path |
| 14 | +from collections import defaultdict |
| 15 | +tusb_config = {} |
| 16 | + |
| 17 | +# ----------------------------------------------------------------------------- |
| 18 | +def init(module): |
| 19 | + module.name = ":tinyusb" |
| 20 | + module.description = FileReader("module.md") |
| 21 | + |
| 22 | +def prepare(module, options): |
| 23 | + if not (options[":target"].has_driver("usb") or |
| 24 | + options[":target"].has_driver("usb_otg_fs") or |
| 25 | + options[":target"].has_driver("usb_otg_hs")): |
| 26 | + return False |
| 27 | + |
| 28 | + configs = {"device.cdc", "device.msc", "device.vendor", "device.midi", "device.dfu"} |
| 29 | + # TODO: Allow all device classes |
| 30 | + # configs = {"device.{}".format(p.parent.name) |
| 31 | + # for p in Path(localpath("tinyusb/src/class/")).glob("*/*_device.h")} |
| 32 | + # TODO: Allow all host classes |
| 33 | + # configs |= {"host.{}".format(p.parent.name) |
| 34 | + # for p in Path(localpath("tinyusb/src/class/")).glob("*/*_host.h")} |
| 35 | + |
| 36 | + module.add_list_option( |
| 37 | + EnumerationOption(name="config", |
| 38 | + description="Endpoint Configuration", |
| 39 | + enumeration=sorted(configs), |
| 40 | + dependencies=lambda vs: |
| 41 | + [":tinyusb:{}".format(v.replace(".", ":")) for v in vs])) |
| 42 | + |
| 43 | + if options[":target"].has_driver("usb_otg_hs"): |
| 44 | + module.add_option( |
| 45 | + EnumerationOption(name="speed", |
| 46 | + description="USB Port Speed", |
| 47 | + enumeration={"full": "fs", "high": "hs"}, |
| 48 | + default="full", |
| 49 | + dependencies=lambda s: ":platform:usb:{}s".format(s[0]))) |
| 50 | + |
| 51 | + module.add_submodule(TinyUsbDeviceModule()) |
| 52 | + module.add_submodule(TinyUsbHostModule()) |
| 53 | + module.depends(":cmsis:device", ":architecture:interrupt", ":platform:usb") |
| 54 | + return True |
| 55 | + |
| 56 | + |
| 57 | +def validate(env): |
| 58 | + if env.has_module(":tinyusb:device") and env.has_module(":tinyusb:host"): |
| 59 | + raise ValidateException("TinyUSB won't let you use both Device *and* Host at the same time!") |
| 60 | + |
| 61 | + |
| 62 | +def build(env): |
| 63 | + env.collect(":build:path.include", "modm/ext/tinyusb/") |
| 64 | + env.outbasepath = "modm/ext/tinyusb" |
| 65 | + |
| 66 | + env.copy("tinyusb/src/tusb.h", "tusb.h") |
| 67 | + env.copy("tinyusb/src/tusb_option.h", "tusb_option.h") |
| 68 | + env.copy("tinyusb/src/osal/osal.h", "osal/osal.h") |
| 69 | + env.copy("tinyusb/src/common", "common/") |
| 70 | + env.copy("tinyusb/src/tusb.c", "tusb.c") |
| 71 | + |
| 72 | + """ Generic config defaults: |
| 73 | + - CFG_TUSB_CONFIG_FILE = tusb_config.h |
| 74 | + - CFG_TUSB_DEBUG = 0 |
| 75 | + - CFG_TUSB_DEBUG_PRINTF = [undef] |
| 76 | + - CFG_TUSB_MCU = OPT_MCU_NONE |
| 77 | + - CFG_TUSB_MEM_ALIGN = __attribute__((aligned(4))) |
| 78 | + - CFG_TUSB_MEM_SECTION = [empty] # FIXME |
| 79 | + - CFG_TUSB_OS = OPT_OS_NONE |
| 80 | + - CFG_TUSB_RHPORT0_MODE = OPT_MODE_NONE |
| 81 | + - CFG_TUSB_RHPORT1_MODE = OPT_MODE_NONE |
| 82 | + """ |
| 83 | + global tusb_config |
| 84 | + |
| 85 | + target = env[":target"].identifier |
| 86 | + if target.platform == "stm32": |
| 87 | + tusb_config["CFG_TUSB_MCU"] = "OPT_MCU_STM32{}".format(target.family.upper()) |
| 88 | + # TODO: use modm-devices driver type for this |
| 89 | + fs_dev = (target.family in ["f0", "f3", "l0"] or |
| 90 | + (target.family == "f1" and target.name <= "03")) |
| 91 | + if fs_dev: |
| 92 | + # PMA buffer size: 512B or 1024B |
| 93 | + env.copy("tinyusb/src/portable/st/stm32_fsdev/", "portable/st/stm32_fsdev/") |
| 94 | + else: |
| 95 | + env.copy("tinyusb/src/portable/st/synopsys", "portable/st/synopsys/") |
| 96 | + |
| 97 | + elif target.platform == "sam": |
| 98 | + if target.family == "g": |
| 99 | + tusb_config["CFG_TUSB_MCU"] = "OPT_MCU_SAMG" |
| 100 | + env.copy("tinyusb/src/portable/microchip/samg/", "portable/microchip/samg/") |
| 101 | + else: |
| 102 | + tusb_config["CFG_TUSB_MCU"] = "OPT_MCU_SAM{}{}".format(target.family.upper(), target.series.upper()) |
| 103 | + env.copy("tinyusb/src/portable/microchip/samd/", "portable/microchip/samd/") |
| 104 | + |
| 105 | + if env.has_module(":freertos"): |
| 106 | + tusb_config["CFG_TUSB_OS"] = "OPT_OS_FREERTOS" |
| 107 | + env.copy("tinyusb/src/osal/osal_freertos.h", "osal/osal_freertos.h") |
| 108 | + else: |
| 109 | + env.copy("tinyusb/src/osal/osal_none.h", "osal/osal_none.h") |
| 110 | + |
| 111 | + if env.has_module(":debug"): |
| 112 | + tusb_config["CFG_TUSB_DEBUG_PRINTF"] = "tinyusb_debug_printf" |
| 113 | + # env.collect(":build:cppdefines.debug", "CFG_TUSB_DEBUG=2") |
| 114 | + |
| 115 | + has_device = env.has_module(":tinyusb:device") |
| 116 | + has_host = env.has_module(":tinyusb:host") |
| 117 | + speed = env.get("speed", "fs") |
| 118 | + port = 0 if speed == "fs" else 1 |
| 119 | + |
| 120 | + mode = None |
| 121 | + if has_device: mode = "OPT_MODE_DEVICE"; |
| 122 | + if has_host: mode = "OPT_MODE_HOST"; |
| 123 | + if mode is not None: |
| 124 | + if "hs" in speed: mode = "({} | OPT_MODE_HIGH_SPEED)".format(mode); |
| 125 | + tusb_config["CFG_TUSB_RHPORT{}_MODE".format(port)] = mode |
| 126 | + |
| 127 | + itf_config = env["config"] |
| 128 | + # Enumerate the configurations |
| 129 | + config_enum = defaultdict(list) |
| 130 | + config_enum_counter = 0 |
| 131 | + for conf in itf_config: |
| 132 | + config_enum[conf.split(".")[1]].append(config_enum_counter) |
| 133 | + config_enum_counter += 1 |
| 134 | + |
| 135 | + # Generate the ITF and Endpoint counters |
| 136 | + itfs = [] |
| 137 | + itf_enum = [] |
| 138 | + endpoints = {} |
| 139 | + endpoint_counter = 0 |
| 140 | + for devclass, devitfs in config_enum.items(): |
| 141 | + prefix = "{}".format(devclass.upper()) |
| 142 | + for itf in devitfs: |
| 143 | + endpoint_counter += 1 |
| 144 | + itf_prefix = prefix + str(itf) |
| 145 | + itfs.append( (prefix, itf_prefix) ) |
| 146 | + if devclass in ["cdc"]: |
| 147 | + itf_enum.extend([itf_prefix, itf_prefix + "_DATA"]) |
| 148 | + endpoints[itf_prefix + "_NOTIF"] = hex(0x80 | endpoint_counter) |
| 149 | + endpoint_counter += 1 |
| 150 | + elif devclass in ["msc", "midi", "vendor", "dfu"]: |
| 151 | + itf_enum.append(itf_prefix) |
| 152 | + else: |
| 153 | + raise ValidateException("Unknown ITF device class '{}'!".format(devclass)) |
| 154 | + |
| 155 | + endpoints[itf_prefix + "_OUT"] = hex(endpoint_counter) |
| 156 | + endpoints[itf_prefix + "_IN"] = hex(0x80 | endpoint_counter) |
| 157 | + |
| 158 | + if target.platform == "stm32": |
| 159 | + irq_data = env.query(":platform:usb:irqs") |
| 160 | + irqs = irq_data["port_irqs"][speed] |
| 161 | + if irq_data["is_remap"]: |
| 162 | + tusb_config["STM32F3_IRQ_REMAP"] = 1 |
| 163 | + else: |
| 164 | + irqs = ["USB"] |
| 165 | + |
| 166 | + env.substitutions = { |
| 167 | + "target": target, |
| 168 | + "config": tusb_config, |
| 169 | + "irqs": irqs, |
| 170 | + "port": port, |
| 171 | + "with_debug": env.has_module(":debug"), |
| 172 | + "with_cdc": env.has_module(":tinyusb:device:cdc"), |
| 173 | + "itfs": itfs, |
| 174 | + "itfs_enum": itf_enum, |
| 175 | + "endpoints": endpoints, |
| 176 | + } |
| 177 | + env.template("tusb_config.h.in") |
| 178 | + if len(itf_config): env.template("tusb_descriptors.c.in"); |
| 179 | + env.template("tusb_port.cpp.in") |
| 180 | + |
| 181 | + |
| 182 | +# ----------------------------------------------------------------------------- |
| 183 | +class TinyUsbDeviceModule(Module): |
| 184 | + def init(self, module): |
| 185 | + module.name = ":tinyusb:device" |
| 186 | + module.description = "TinyUSB in Device Mode" |
| 187 | + |
| 188 | + def prepare(self, module, options): |
| 189 | + paths = {p.parent for p in Path(localpath("tinyusb/src/class/")).glob("*/*_device.h")} |
| 190 | + for path in paths: |
| 191 | + module.add_submodule(TinyUsbClassModule(path, "device")) |
| 192 | + return True |
| 193 | + |
| 194 | + def build(self, env): |
| 195 | + env.outbasepath = "modm/ext/tinyusb" |
| 196 | + env.copy("tinyusb/src/device/", "device/") |
| 197 | + |
| 198 | + """ Device config defaults: |
| 199 | + - CFG_TUD_ENDPOINT0_SIZE = 64 |
| 200 | + - CFG_TUD_TASK_QUEUE_SZ = 16 |
| 201 | + """ |
| 202 | + |
| 203 | + |
| 204 | +# ----------------------------------------------------------------------------- |
| 205 | +class TinyUsbHostModule(Module): |
| 206 | + def __init__(self): |
| 207 | + self.config = {} |
| 208 | + |
| 209 | + def init(self, module): |
| 210 | + module.name = ":tinyusb:host" |
| 211 | + module.description = "TinyUSB in Host Mode" |
| 212 | + |
| 213 | + def prepare(self, module, options): |
| 214 | + # Only OTG has Host Mode |
| 215 | + if not (options[":target"].has_driver("usb_otg_fs") or |
| 216 | + options[":target"].has_driver("usb_otg_hs")): |
| 217 | + return False |
| 218 | + |
| 219 | + paths = {p.parent for p in Path(localpath("tinyusb/src/class/")).glob("*/*_host.h")} |
| 220 | + for path in paths: |
| 221 | + module.add_submodule(TinyUsbClassModule(path, "host")) |
| 222 | + return True |
| 223 | + |
| 224 | + def build(self, env): |
| 225 | + env.outbasepath = "modm/ext/tinyusb" |
| 226 | + env.copy("tinyusb/src/host/", "host/") |
| 227 | + |
| 228 | + """ Host config defaults: |
| 229 | + - CFG_TUH_TASK_QUEUE_SZ = 16 |
| 230 | + """ |
| 231 | + |
| 232 | + |
| 233 | +# ----------------------------------------------------------------------------- |
| 234 | +class TinyUsbClassModule(Module): |
| 235 | + def __init__(self, path, mode): |
| 236 | + self.mode = mode |
| 237 | + self.name = str(path.name) |
| 238 | + |
| 239 | + def init(self, module): |
| 240 | + module.name = ":tinyusb:{}:{}".format(self.mode, self.name) |
| 241 | + module.description = "{} class {}".format(self.mode.capitalize(), self.name.upper()) |
| 242 | + |
| 243 | + def prepare(self, module, options): |
| 244 | + if self.mode == "device": |
| 245 | + # FIXME: Changing these defaults seems to crash TinyUSB in weird ways. |
| 246 | + # Since all of the TinyUSB examples leave them at these default values |
| 247 | + # I think they are not supposed to be changed? -Niklas |
| 248 | + # if self.name in ["cdc", "midi", "vendor"]: |
| 249 | + # module.add_option(NumericOption("buffer.rx", description="", minimum=64, default=64)) |
| 250 | + # module.add_option(NumericOption("buffer.tx", description="", minimum=64, default=64)) |
| 251 | + # if self.name in ["msc"]: |
| 252 | + # module.add_option(NumericOption("buffer.ep", description="", minimum=512, default=512)) |
| 253 | + if self.name == "cdc": |
| 254 | + module.depends(":architecture:uart") |
| 255 | + module.depends(":tinyusb") |
| 256 | + return True |
| 257 | + |
| 258 | + def build(self, env): |
| 259 | + env.outbasepath = "modm/ext/tinyusb" |
| 260 | + env.copy("tinyusb/src/class/{}".format(self.name), "class/{}".format(self.name), |
| 261 | + ignore=env.ignore_files("*_host.*" if "device" in self.mode else "*_device.*")) |
| 262 | + |
| 263 | + global tusb_config |
| 264 | + cfg_name = "CFG_TU{}_{}".format(self.mode[0].upper(), self.name.upper()) |
| 265 | + if "DFU" in cfg_name: cfg_name += "_RT"; |
| 266 | + tusb_config[cfg_name] = env[":tinyusb:config"].count("{}.{}".format(self.mode, self.name)) |
| 267 | + speed = env.get(":tinyusb:speed", 0) |
| 268 | + |
| 269 | + if self.mode == "device": |
| 270 | + # These are the defaults that don't crash TinyUSB. |
| 271 | + if self.name in ["cdc", "midi", "vendor"]: |
| 272 | + tusb_config[cfg_name+"_RX_BUFSIZE"] = 512 if speed else 64 #env["buffer.rx"] |
| 273 | + tusb_config[cfg_name+"_TX_BUFSIZE"] = 512 if speed else 64 #env["buffer.tx"] |
| 274 | + if self.name in ["msc"]: |
| 275 | + tusb_config[cfg_name+"_EP_BUFSIZE"] = 512 #env["buffer.ep"] |
| 276 | + if self.name == "midi": |
| 277 | + env.copy("tinyusb/src/class/audio", "class/audio") |
| 278 | + |
| 279 | + |
| 280 | + """ Device class defaults: |
| 281 | + - CFG_TUD_BTH_DATA_EPSIZE = 64 |
| 282 | + - CFG_TUD_BTH_EVENT_EPSIZE = 16 |
| 283 | + - CFG_TUD_BTH_ISO_ALT_COUNT = 0 |
| 284 | +
|
| 285 | + - CFG_TUD_CDC_EP_BUFSIZE = 64 or 512 (hs) |
| 286 | + - CFG_TUD_CDC_RX_BUFSIZE = [undef] |
| 287 | + - CFG_TUD_CDC_TX_BUFSIZE = [undef] |
| 288 | +
|
| 289 | + - CFG_TUD_HID_EP_BUFSIZE = 16 |
| 290 | +
|
| 291 | + - CFG_TUD_MIDI_EP_BUFSIZE = 64 or 512 (hs) |
| 292 | + - CFG_TUD_MIDI_RX_BUFSIZE = [undef] |
| 293 | + - CFG_TUD_MIDI_TX_BUFSIZE = [undef] |
| 294 | +
|
| 295 | + - CFG_TUD_MSC_EP_BUFSIZE = [undef] |
| 296 | +
|
| 297 | + - CFG_TUD_NET_ENDPOINT_SIZE = 64 or 512 (hs) |
| 298 | +
|
| 299 | + - CFG_TUD_USBTMC_ENABLE_488 = 1 |
| 300 | +
|
| 301 | + - CFG_TUD_VENDOR_EPSIZE = 64 |
| 302 | + - CFG_TUD_VENDOR_RX_BUFSIZE = [undef] |
| 303 | + - CFG_TUD_VENDOR_TX_BUFSIZE = [undef] |
| 304 | + """ |
| 305 | + |
| 306 | + """ Host config defaults: |
| 307 | + - CFG_TUH_CDC_RNDIS |
| 308 | + - CFG_TUH_HID_KEYBOARD |
| 309 | + - CFG_TUH_HID_MOUSE |
| 310 | + """ |
0 commit comments