Skip to content

Commit f82c102

Browse files
committed
[ext] Add TinyUSB module
1 parent 6f0c170 commit f82c102

File tree

12 files changed

+839
-7
lines changed

12 files changed

+839
-7
lines changed

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,6 @@
2828
[submodule "ext/microchip/sam"]
2929
path = ext/microchip/sam
3030
url = https://github.com/modm-io/cmsis-header-sam.git
31+
[submodule "ext/hathach/tinyusb"]
32+
path = ext/hathach/tinyusb
33+
url = https://github.com/modm-ext/tinyusb-partial.git

ext/hathach/module.lb

Lines changed: 310 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,310 @@
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

Comments
 (0)