summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rwxr-xr-xtools/build_adafruit_bins.sh12
-rw-r--r--tools/gen_usb_descriptor.py474
-rw-r--r--tools/hid_report_descriptors.py239
-rwxr-xr-xtools/mpy-tool.py70
-rwxr-xr-xtools/preprocess_frozen_modules.py2
m---------tools/usb_descriptor0
6 files changed, 789 insertions, 8 deletions
diff --git a/tools/build_adafruit_bins.sh b/tools/build_adafruit_bins.sh
index 595d5ba5d..4c672c6de 100755
--- a/tools/build_adafruit_bins.sh
+++ b/tools/build_adafruit_bins.sh
@@ -6,7 +6,9 @@ rm -rf ports/nrf/build*
# Alphabetical.
HW_BOARDS="\
+arduino_mkr1300 \
arduino_zero \
+catwan_usbstick \
circuitplayground_express \
circuitplayground_express_crickit \
feather_huzzah \
@@ -25,8 +27,10 @@ grandcentral_m4_express \
hallowing_m0_express \
itsybitsy_m0_express \
itsybitsy_m4_express \
+makerdiary_nrf52840_mdk \
metro_m0_express \
metro_m4_express \
+meowmeow \
pca10056 \
pca10059 \
pirkey_m0 \
@@ -66,8 +70,9 @@ for board in $boards; do
for language_file in $(ls locale/*.po); do
language=$(basename -s .po $language_file)
echo "Building $board for $language"
+ # There is a bug in the Huzzah Makefile that causes it to fail occasionally with -j > 1.
if [[ $board == "feather_huzzah" ]]; then
- make $PARALLEL -C ports/esp8266 TRANSLATION=$language BOARD=$board
+ make -C ports/esp8266 TRANSLATION=$language BOARD=$board
(( exit_status = exit_status || $? ))
temp_filename=ports/esp8266/build/firmware-combined.bin
extension=bin
@@ -81,6 +86,11 @@ for board in $boards; do
(( exit_status = exit_status || $? ))
temp_filename=ports/nrf/build-$board-s140/firmware.uf2
extension=uf2
+ elif [[ $board == "makerdiary_nrf52840_mdk" ]]; then
+ make $PARALLEL -C ports/nrf TRANSLATION=$language BOARD=$board SD=s140
+ (( exit_status = exit_status || $? ))
+ temp_filename=ports/nrf/build-$board-s140/firmware.hex
+ extension=hex
else
time make $PARALLEL -C ports/atmel-samd TRANSLATION=$language BOARD=$board
(( exit_status = exit_status || $? ))
diff --git a/tools/gen_usb_descriptor.py b/tools/gen_usb_descriptor.py
new file mode 100644
index 000000000..2cb06ec2f
--- /dev/null
+++ b/tools/gen_usb_descriptor.py
@@ -0,0 +1,474 @@
+import argparse
+
+import os
+import sys
+
+sys.path.append("../../tools/usb_descriptor")
+
+from adafruit_usb_descriptor import audio, audio10, cdc, hid, midi, msc, standard, util
+import hid_report_descriptors
+
+parser = argparse.ArgumentParser(description='Generate USB descriptors.')
+parser.add_argument('--manufacturer', type=str,
+ help='manufacturer of the device')
+parser.add_argument('--product', type=str,
+ help='product name of the device')
+parser.add_argument('--vid', type=lambda x: int(x, 16),
+ help='vendor id')
+parser.add_argument('--pid', type=lambda x: int(x, 16),
+ help='product id')
+parser.add_argument('--serial_number_length', type=int, default=32,
+ help='length needed for the serial number in digits')
+parser.add_argument('--output_c_file', type=argparse.FileType('w'), required=True)
+parser.add_argument('--output_h_file', type=argparse.FileType('w'), required=True)
+
+args = parser.parse_args()
+
+class StringIndex:
+ """Assign a monotonically increasing index to each unique string. Start with 0."""
+ string_to_index = {}
+ index_to_variable = {}
+ strings = []
+
+ @classmethod
+ def index(cls, string, *, variable_name = None):
+ if string in cls.string_to_index:
+ idx = cls.string_to_index[string]
+ if not cls.index_to_variable[idx]:
+ cls.index_to_variable[idx] = variable_name
+ return idx
+ else:
+ idx = len(cls.strings)
+ cls.string_to_index[string] = idx
+ cls.strings.append(string)
+ cls.index_to_variable[idx] = variable_name
+ return idx
+
+ @classmethod
+ def strings_in_order(cls):
+ return cls.strings
+
+
+
+# langid must be the 0th string descriptor
+LANGID_INDEX = StringIndex.index("\u0409", variable_name="language_id")
+assert LANGID_INDEX == 0
+SERIAL_NUMBER_INDEX = StringIndex.index("S" * args.serial_number_length, variable_name="usb_serial_number")
+
+device = standard.DeviceDescriptor(
+ description="top",
+ idVendor=args.vid,
+ idProduct=args.pid,
+ iManufacturer=StringIndex.index(args.manufacturer),
+ iProduct=StringIndex.index(args.product),
+ iSerialNumber=SERIAL_NUMBER_INDEX)
+
+# Interface numbers are interface-set local and endpoints are interface local
+# until util.join_interfaces renumbers them.
+
+cdc_union = cdc.Union(
+ description="CDC comm",
+ bMasterInterface=0x00, # Adjust this after interfaces are renumbered.
+ bSlaveInterface_list=[0x01]) # Adjust this after interfaces are renumbered.
+
+cdc_call_management = cdc.CallManagement(
+ description="CDC comm",
+ bmCapabilities=0x01,
+ bDataInterface=0x01) # Adjust this after interfaces are renumbered.
+
+cdc_comm_interface = standard.InterfaceDescriptor(
+ description="CDC comm",
+ bInterfaceClass=cdc.CDC_CLASS_COMM, # Communications Device Class
+ bInterfaceSubClass=cdc.CDC_SUBCLASS_ACM, # Abstract control model
+ bInterfaceProtocol=cdc.CDC_PROTOCOL_NONE,
+ iInterface=StringIndex.index("CircuitPython CDC control"),
+ subdescriptors=[
+ cdc.Header(
+ description="CDC comm",
+ bcdCDC=0x0110),
+ cdc_call_management,
+ cdc.AbstractControlManagement(
+ description="CDC comm",
+ bmCapabilities=0x02),
+ cdc_union,
+ standard.EndpointDescriptor(
+ description="CDC comm in",
+ bEndpointAddress=0x0 | standard.EndpointDescriptor.DIRECTION_IN,
+ bmAttributes=standard.EndpointDescriptor.TYPE_INTERRUPT,
+ wMaxPacketSize=0x0040,
+ bInterval=0x10)
+ ])
+
+cdc_data_interface = standard.InterfaceDescriptor(
+ description="CDC data",
+ bInterfaceClass=cdc.CDC_CLASS_DATA,
+ iInterface=StringIndex.index("CircuitPython CDC data"),
+ subdescriptors=[
+ standard.EndpointDescriptor(
+ description="CDC data out",
+ bEndpointAddress=0x0 | standard.EndpointDescriptor.DIRECTION_OUT,
+ bmAttributes=standard.EndpointDescriptor.TYPE_BULK),
+ standard.EndpointDescriptor(
+ description="CDC data in",
+ bEndpointAddress=0x0 | standard.EndpointDescriptor.DIRECTION_IN,
+ bmAttributes=standard.EndpointDescriptor.TYPE_BULK),
+ ])
+
+cdc_interfaces = [cdc_comm_interface, cdc_data_interface]
+
+msc_interfaces = [
+ standard.InterfaceDescriptor(
+ description="MSC",
+ bInterfaceClass=msc.MSC_CLASS,
+ bInterfaceSubClass=msc.MSC_SUBCLASS_TRANSPARENT,
+ bInterfaceProtocol=msc.MSC_PROTOCOL_BULK,
+ iInterface=StringIndex.index("CircuitPython Mass Storage"),
+ subdescriptors=[
+ standard.EndpointDescriptor(
+ description="MSC in",
+ bEndpointAddress=0x0 | standard.EndpointDescriptor.DIRECTION_IN,
+ bmAttributes=standard.EndpointDescriptor.TYPE_BULK,
+ bInterval=0),
+ standard.EndpointDescriptor(
+ description="MSC out",
+ bEndpointAddress=0x1 | standard.EndpointDescriptor.DIRECTION_OUT,
+ bmAttributes=standard.EndpointDescriptor.TYPE_BULK,
+ bInterval=0)
+ ]
+ )
+]
+
+# Include only these HID devices.
+# DIGITIZER works on Linux but conflicts with MOUSE, so leave it out for now.
+hid_devices = ("KEYBOARD", "MOUSE", "CONSUMER", "GAMEPAD")
+
+combined_hid_report_descriptor = hid.ReportDescriptor(
+ description="MULTIDEVICE",
+ report_descriptor=b''.join(
+ hid_report_descriptors.REPORT_DESCRIPTORS[name].report_descriptor for name in hid_devices ))
+
+hid_report_ids_dict = { name: hid_report_descriptors.REPORT_IDS[name] for name in hid_devices }
+hid_report_lengths_dict = { name: hid_report_descriptors.REPORT_LENGTHS[name] for name in hid_devices }
+hid_max_report_length = max(hid_report_lengths_dict.values())
+
+# ASF4 expects keyboard and generic devices to have both in and out endpoints,
+# and will fail (possibly silently) if both are not supplied.
+hid_endpoint_in_descriptor = standard.EndpointDescriptor(
+ description="HID in",
+ bEndpointAddress=0x0 | standard.EndpointDescriptor.DIRECTION_IN,
+ bmAttributes=standard.EndpointDescriptor.TYPE_INTERRUPT,
+ bInterval=10)
+
+hid_interfaces = [
+ standard.InterfaceDescriptor(
+ description="HID Multiple Devices",
+ bInterfaceClass=hid.HID_CLASS,
+ bInterfaceSubClass=hid.HID_SUBCLASS_NOBOOT,
+ bInterfaceProtocol=hid.HID_PROTOCOL_NONE,
+ iInterface=StringIndex.index("CircuitPython HID"),
+ subdescriptors=[
+ hid.HIDDescriptor(
+ description="HID",
+ wDescriptorLength=len(bytes(combined_hid_report_descriptor))),
+ hid_endpoint_in_descriptor,
+ ]
+ ),
+ ]
+
+# Audio!
+midi_in_jack = midi.InJackDescriptor(
+ description="MIDI PC <- CircuitPython internals",
+ bJackType=midi.JACK_TYPE_EMBEDDED,
+ iJack=0)
+midi_out_jack = midi.OutJackDescriptor(
+ description="MIDI PC -> CircuitPython internals",
+ bJackType=midi.JACK_TYPE_EMBEDDED,
+ iJack=0)
+audio_midi_interface = standard.InterfaceDescriptor(
+ description="All the audio",
+ bInterfaceClass=audio.AUDIO_CLASS_DEVICE,
+ bInterfaceSubClass=audio.AUDIO_SUBCLASS_MIDI_STREAMING,
+ bInterfaceProtocol=audio.AUDIO_PROTOCOL_V1,
+ iInterface=StringIndex.index("CircuitPython MIDI"),
+ subdescriptors=[
+ midi.Header(
+ jacks_and_elements=[
+ midi_in_jack,
+ midi.InJackDescriptor(
+ description="MIDI data in from user code.",
+ bJackType=midi.JACK_TYPE_EXTERNAL, iJack=0),
+ midi_out_jack,
+ midi.OutJackDescriptor(
+ description="MIDI data out to user code.",
+ bJackType=midi.JACK_TYPE_EXTERNAL, iJack=0),
+ ]
+ ),
+ standard.EndpointDescriptor(
+ description="MIDI data out",
+ bEndpointAddress=0x0 | standard.EndpointDescriptor.DIRECTION_OUT,
+ bmAttributes=standard.EndpointDescriptor.TYPE_BULK),
+ midi.DataEndpointDescriptor(baAssocJack=[midi_out_jack]),
+ standard.EndpointDescriptor(
+ description="MIDI data in",
+ bEndpointAddress=0x0 | standard.EndpointDescriptor.DIRECTION_IN,
+ bmAttributes=standard.EndpointDescriptor.TYPE_BULK),
+ midi.DataEndpointDescriptor(baAssocJack=[midi_in_jack]),
+ ])
+
+cs_ac_interface = audio10.AudioControlInterface(
+ description="Empty audio control",
+ audio_streaming_interfaces = [],
+ midi_streaming_interfaces = [
+ audio_midi_interface
+ ]
+ )
+
+audio_control_interface = standard.InterfaceDescriptor(
+ description="All the audio",
+ bInterfaceClass=audio.AUDIO_CLASS_DEVICE,
+ bInterfaceSubClass=audio.AUDIO_SUBCLASS_CONTROL,
+ bInterfaceProtocol=audio.AUDIO_PROTOCOL_V1,
+ iInterface=StringIndex.index("CircuitPython Audio"),
+ subdescriptors=[
+ cs_ac_interface,
+ ])
+
+# Audio streaming interfaces must occur before MIDI ones.
+# audio_interfaces = [audio_control_interface] + cs_ac_interface.audio_streaming_interfaces + cs_ac_interface.midi_streaming_interfaces
+
+# This will renumber the endpoints to make them unique across descriptors,
+# and renumber the interfaces in order. But we still need to fix up certain
+# interface cross-references.
+interfaces = util.join_interfaces(cdc_interfaces, msc_interfaces, hid_interfaces)
+
+# Now adjust the CDC interface cross-references.
+
+cdc_union.bMasterInterface = cdc_comm_interface.bInterfaceNumber
+cdc_union.bSlaveInterface_list = [cdc_data_interface.bInterfaceNumber]
+
+cdc_call_management.bDataInterface = cdc_data_interface.bInterfaceNumber
+
+cdc_iad = standard.InterfaceAssociationDescriptor(
+ description="CDC IAD",
+ bFirstInterface=cdc_comm_interface.bInterfaceNumber,
+ bInterfaceCount=len(cdc_interfaces),
+ bFunctionClass=cdc.CDC_CLASS_COMM, # Communications Device Class
+ bFunctionSubClass=cdc.CDC_SUBCLASS_ACM, # Abstract control model
+ bFunctionProtocol=cdc.CDC_PROTOCOL_NONE)
+
+# audio_iad = standard.InterfaceAssociationDescriptor(
+# description="Audio IAD",
+# bFirstInterface=audio_control_interface.bInterfaceNumber,
+# bInterfaceCount=len(audio_interfaces),
+# bFunctionClass=audio.AUDIO_CLASS_DEVICE,
+# bFunctionSubClass=audio.AUDIO_SUBCLASS_UNKNOWN,
+# bFunctionProtocol=audio.AUDIO_PROTOCOL_V1)
+
+
+descriptor_list = []
+descriptor_list.append(cdc_iad)
+# descriptor_list.append(audio_iad)
+descriptor_list.extend(cdc_interfaces)
+descriptor_list.extend(msc_interfaces)
+# descriptor_list.append(audio_control_interface)
+# Put the CDC IAD just before the CDC interfaces.
+# There appears to be a bug in the Windows composite USB driver that requests the
+# HID report descriptor with the wrong interface number if the HID interface is not given
+# first. However, it still fetches the descriptor anyway. We could reorder the interfaces but
+# the Windows 7 Adafruit_usbser.inf file thinks CDC is at Interface 0, so we'll leave it
+# there for backwards compatibility.
+descriptor_list.extend(hid_interfaces)
+
+configuration = standard.ConfigurationDescriptor(
+ description="Composite configuration",
+ wTotalLength=(standard.ConfigurationDescriptor.bLength +
+ sum([len(bytes(x)) for x in descriptor_list])),
+ bNumInterfaces=len(interfaces))
+descriptor_list.insert(0, configuration)
+
+string_descriptors = [standard.StringDescriptor(string) for string in StringIndex.strings_in_order()]
+serial_number_descriptor = string_descriptors[SERIAL_NUMBER_INDEX]
+
+c_file = args.output_c_file
+h_file = args.output_h_file
+
+
+c_file.write("""\
+#include <stdint.h>
+
+#include "{H_FILE_NAME}"
+
+""".format(H_FILE_NAME=h_file.name))
+
+c_file.write("""\
+// {DESCRIPTION} : {CLASS}
+""".format(DESCRIPTION=device.description,
+ CLASS=device.__class__))
+
+c_file.write("""\
+const uint8_t usb_desc_dev[] = {
+""")
+for b in bytes(device):
+ c_file.write("0x{:02x}, ".format(b))
+
+c_file.write("""\
+};
+""")
+
+c_file.write("""\
+const uint8_t usb_desc_cfg[] = {
+""")
+
+# Write out all the regular descriptors as one long array (that's how ASF4 does it).
+descriptor_length = 0
+for descriptor in descriptor_list:
+ c_file.write("""\
+// {DESCRIPTION} : {CLASS}
+""".format(DESCRIPTION=descriptor.description,
+ CLASS=descriptor.__class__))
+
+ b = bytes(descriptor)
+ notes = descriptor.notes()
+ i = 0
+
+ # This prints each subdescriptor on a separate line.
+ n = 0
+ while i < len(b):
+ length = b[i]
+ for j in range(length):
+ c_file.write("0x{:02x}, ".format(b[i + j]))
+ c_file.write("// " + notes[n])
+ n += 1
+ c_file.write("\n")
+ i += length
+ descriptor_length += len(b)
+
+c_file.write("""\
+};
+""")
+
+pointers_to_strings = []
+
+for idx, descriptor in enumerate(string_descriptors):
+ c_file.write("""\
+// {DESCRIPTION} : {CLASS}
+""".format(DESCRIPTION=descriptor.description,
+ CLASS=descriptor.__class__))
+
+ b = bytes(descriptor)
+ notes = descriptor.notes()
+ i = 0
+
+ # This prints each subdescriptor on a separate line.
+ variable_name = StringIndex.index_to_variable[idx]
+ if not variable_name:
+ variable_name = "string_descriptor{}".format(idx)
+
+ const = "const "
+ if variable_name == "usb_serial_number":
+ const = ""
+ c_file.write("""\
+{const}uint16_t {NAME}[] = {{
+""".format(const=const, NAME=variable_name))
+ pointers_to_strings.append("{name}".format(name=variable_name))
+ n = 0
+ while i < len(b):
+ length = b[i]
+ for j in range(length // 2):
+ c_file.write("0x{:04x}, ".format(b[i + 2*j + 1] << 8 | b[i + 2*j]))
+ n += 1
+ c_file.write("\n")
+ i += length
+ c_file.write("""\
+};
+""")
+
+c_file.write("""\
+// array of pointer to string descriptors
+uint16_t const * const string_desc_arr [] =
+{
+""")
+c_file.write(""",\
+
+""".join(pointers_to_strings))
+
+c_file.write("""
+};
+""")
+
+c_file.write("\n");
+
+hid_descriptor_length = len(bytes(combined_hid_report_descriptor))
+
+# Now we values we need for the .h file.
+h_file.write("""\
+#ifndef MICROPY_INCLUDED_AUTOGEN_USB_DESCRIPTOR_H
+#define MICROPY_INCLUDED_AUTOGEN_USB_DESCRIPTOR_H
+
+#include <stdint.h>
+
+const uint8_t usb_desc_dev[{device_length}];
+// Make sure the control buffer is big enough to fit the descriptor.
+#define CFG_TUD_ENUM_BUFFER_SIZE {max_configuration_length}
+const uint8_t usb_desc_cfg[{configuration_length}];
+uint16_t usb_serial_number[{serial_number_length}];
+uint16_t const * const string_desc_arr [{string_descriptor_length}];
+
+const uint8_t hid_report_descriptor[{HID_REPORT_DESCRIPTOR_LENGTH}];
+
+// Vendor name included in Inquiry response, max 8 bytes
+#define CFG_TUD_MSC_VENDOR "{msc_vendor}"
+
+// Product name included in Inquiry response, max 16 bytes
+#define CFG_TUD_MSC_PRODUCT "{msc_product}"
+
+"""
+.format(serial_number_length=len(bytes(serial_number_descriptor)) // 2,
+ device_length=len(bytes(device)),
+ configuration_length=descriptor_length,
+ max_configuration_length=max(hid_descriptor_length, descriptor_length),
+ string_descriptor_length=len(pointers_to_strings),
+ HID_REPORT_DESCRIPTOR_LENGTH=len(bytes(combined_hid_report_descriptor)),
+ msc_vendor=args.manufacturer[:8],
+ msc_product=args.product[:16]))
+
+# #define the report ID's used in the combined HID descriptor
+for name, id in hid_report_ids_dict.items():
+ h_file.write("""\
+#define USB_HID_REPORT_ID_{name} {id}
+""".format(name=name,
+ id=id))
+
+h_file.write("\n")
+
+# #define the report sizes used in the combined HID descriptor
+for name, length in hid_report_lengths_dict.items():
+ h_file.write("""\
+#define USB_HID_REPORT_LENGTH_{name} {length}
+""".format(name=name,
+ length=length))
+
+h_file.write("\n")
+
+h_file.write("""\
+#define USB_HID_NUM_DEVICES {num_devices}
+#define USB_HID_MAX_REPORT_LENGTH {max_length}
+""".format(num_devices=len(hid_report_lengths_dict),
+ max_length=hid_max_report_length))
+
+
+
+# Write out the report descriptor and info
+c_file.write("""\
+const uint8_t hid_report_descriptor[{HID_DESCRIPTOR_LENGTH}] = {{
+""".format(HID_DESCRIPTOR_LENGTH=hid_descriptor_length))
+
+for b in bytes(combined_hid_report_descriptor):
+ c_file.write("0x{:02x}, ".format(b))
+c_file.write("""
+};
+""")
+
+h_file.write("""\
+#endif // MICROPY_INCLUDED_AUTOGEN_USB_DESCRIPTOR_H
+""")
diff --git a/tools/hid_report_descriptors.py b/tools/hid_report_descriptors.py
new file mode 100644
index 000000000..f3b28ebcf
--- /dev/null
+++ b/tools/hid_report_descriptors.py
@@ -0,0 +1,239 @@
+# The MIT License (MIT)
+#
+# Copyright (c) 2018 Dan Halbert for Adafruit Industries
+#
+# 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.
+
+import struct
+
+"""
+HID specific descriptors
+========================
+
+* Author(s): Dan Halbert
+"""
+
+from adafruit_usb_descriptor import hid
+
+REPORT_IDS = {
+ "KEYBOARD" : 1,
+ "MOUSE" : 2,
+ "CONSUMER" : 3,
+ "SYS_CONTROL" : 4,
+ "GAMEPAD" : 5,
+ "DIGITIZER" : 6,
+ }
+
+# Byte count for each kind of report. Length does not include report ID in first byte.
+REPORT_LENGTHS = {
+ "KEYBOARD" : 8,
+ "MOUSE" : 4,
+ "CONSUMER" : 2,
+ "SYS_CONTROL" : 1,
+ "GAMEPAD" : 6,
+ "DIGITIZER" : 5,
+ }
+
+KEYBOARD_WITH_ID = hid.ReportDescriptor(
+ description="KEYBOARD",
+ report_descriptor=bytes([
+ # Regular keyboard
+ 0x05, 0x01, # Usage Page (Generic Desktop)
+ 0x09, 0x06, # Usage (Keyboard)
+ 0xA1, 0x01, # Collection (Application)
+ 0x85, REPORT_IDS["KEYBOARD"], # Report ID (1)
+ 0x05, 0x07, # Usage Page (Keyboard)
+ 0x19, 224, # Usage Minimum (224)
+ 0x29, 231, # Usage Maximum (231)
+ 0x15, 0x00, # Logical Minimum (0)
+ 0x25, 0x01, # Logical Maximum (1)
+ 0x75, 0x01, # Report Size (1)
+ 0x95, 0x08, # Report Count (8)
+ 0x81, 0x02, # Input (Data, Variable, Absolute)
+ 0x81, 0x01, # Input (Constant)
+ 0x19, 0x00, # Usage Minimum (0)
+ 0x29, 101, # Usage Maximum (101)
+ 0x15, 0x00, # Logical Minimum (0)
+ 0x25, 101, # Logical Maximum (101)
+ 0x75, 0x08, # Report Size (8)
+ 0x95, 0x06, # Report Count (6)
+ 0x81, 0x00, # Input (Data, Array)
+ 0x05, 0x08, # Usage Page (LED)
+ 0x19, 0x01, # Usage Minimum (1)
+ 0x29, 0x05, # Usage Maximum (5)
+ 0x15, 0x00, # Logical Minimum (0)
+ 0x25, 0x01, # Logical Maximum (1)
+ 0x75, 0x01, # Report Size (1)
+ 0x95, 0x05, # Report Count (5)
+ 0x91, 0x02, # Output (Data, Variable, Absolute)
+ 0x95, 0x03, # Report Count (3)
+ 0x91, 0x01, # Output (Constant)
+ 0xC0, # End Collection
+ ]))
+
+MOUSE_WITH_ID = hid.ReportDescriptor(
+ description="MOUSE",
+ report_descriptor=bytes([
+ # Regular mouse
+ 0x05, 0x01, # Usage Page (Generic Desktop)
+ 0x09, 0x02, # Usage (Mouse)
+ 0xA1, 0x01, # Collection (Application)
+ 0x09, 0x01, # Usage (Pointer)
+ 0xA1, 0x00, # Collection (Physical)
+ 0x85, REPORT_IDS["MOUSE"], # Report ID (n)
+ 0x05, 0x09, # Usage Page (Button)
+ 0x19, 0x01, # Usage Minimum (0x01)
+ 0x29, 0x05, # Usage Maximum (0x05)
+ 0x15, 0x00, # Logical Minimum (0)
+ 0x25, 0x01, # Logical Maximum (1)
+ 0x95, 0x05, # Report Count (5)
+ 0x75, 0x01, # Report Size (1)
+ 0x81, 0x02, # Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ 0x95, 0x01, # Report Count (1)
+ 0x75, 0x03, # Report Size (3)
+ 0x81, 0x01, # Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ 0x05, 0x01, # Usage Page (Generic Desktop Ctrls)
+ 0x09, 0x30, # Usage (X)
+ 0x09, 0x31, # Usage (Y)
+ 0x15, 0x81, # Logical Minimum (-127)
+ 0x25, 0x7F, # Logical Maximum (127)
+ 0x75, 0x08, # Report Size (8)
+ 0x95, 0x02, # Report Count (2)
+ 0x81, 0x06, # Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
+ 0x09, 0x38, # Usage (Wheel)
+ 0x15, 0x81, # Logical Minimum (-127)
+ 0x25, 0x7F, # Logical Maximum (127)
+ 0x75, 0x08, # Report Size (8)
+ 0x95, 0x01, # Report Count (1)
+ 0x81, 0x06, # Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
+ 0xC0, # End Collection
+ 0xC0, # End Collection
+ ]))
+
+CONSUMER_WITH_ID = hid.ReportDescriptor(
+ description="CONSUMER",
+ report_descriptor=bytes([
+ # Consumer ("multimedia") keys
+ 0x05, 0x0C, # Usage Page (Consumer)
+ 0x09, 0x01, # Usage (Consumer Control)
+ 0xA1, 0x01, # Collection (Application)
+ 0x85, REPORT_IDS["CONSUMER"], # Report ID (n)
+ 0x75, 0x10, # Report Size (16)
+ 0x95, 0x01, # Report Count (1)
+ 0x15, 0x01, # Logical Minimum (1)
+ 0x26, 0x8C, 0x02, # Logical Maximum (652)
+ 0x19, 0x01, # Usage Minimum (Consumer Control)
+ 0x2A, 0x8C, 0x02, # Usage Maximum (AC Send)
+ 0x81, 0x00, # Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ 0xC0, # End Collection
+ ]))
+
+SYS_CONTROL_WITH_ID = hid.ReportDescriptor(
+ description="SYS_CONTROL",
+ report_descriptor=bytes([
+ # Power controls
+ 0x05, 0x01, # Usage Page (Generic Desktop Ctrls)
+ 0x09, 0x80, # Usage (Sys Control)
+ 0xA1, 0x01, # Collection (Application)
+ 0x85, REPORT_IDS["SYS_CONTROL"], # Report ID (n)
+ 0x75, 0x02, # Report Size (2)
+ 0x95, 0x01, # Report Count (1)
+ 0x15, 0x01, # Logical Minimum (1)
+ 0x25, 0x03, # Logical Maximum (3)
+ 0x09, 0x82, # Usage (Sys Sleep)
+ 0x09, 0x81, # Usage (Sys Power Down)
+ 0x09, 0x83, # Usage (Sys Wake Up)
+ 0x81, 0x60, # Input (Data,Array,Abs,No Wrap,Linear,No Preferred State,Null State)
+ 0x75, 0x06, # Report Size (6)
+ 0x81, 0x03, # Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ 0xC0, # End Collection
+ ]))
+
+GAMEPAD_WITH_ID = hid.ReportDescriptor(
+ description="GAMEPAD",
+ report_descriptor=bytes([
+ # Gamepad with 16 buttons and two joysticks
+ 0x05, 0x01, # Usage Page (Generic Desktop Ctrls)
+ 0x09, 0x05, # Usage (Game Pad)
+ 0xA1, 0x01, # Collection (Application)
+ 0x85, REPORT_IDS["GAMEPAD"], # Report ID (n)
+ 0x05, 0x09, # Usage Page (Button)
+ 0x19, 0x01, # Usage Minimum (Button 1)
+ 0x29, 0x10, # Usage Maximum (Button 16)
+ 0x15, 0x00, # Logical Minimum (0)
+ 0x25, 0x01, # Logical Maximum (1)
+ 0x75, 0x01, # Report Size (1)
+ 0x95, 0x10, # Report Count (16)
+ 0x81, 0x02, # Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ 0x05, 0x01, # Usage Page (Generic Desktop Ctrls)
+ 0x15, 0x81, # Logical Minimum (-127)
+ 0x25, 0x7F, # Logical Maximum (127)
+ 0x09, 0x30, # Usage (X)
+ 0x09, 0x31, # Usage (Y)
+ 0x09, 0x32, # Usage (Z)
+ 0x09, 0x35, # Usage (Rz)
+ 0x75, 0x08, # Report Size (8)
+ 0x95, 0x04, # Report Count (4)
+ 0x81, 0x02, # Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ 0xC0, # End Collection
+ ]))
+
+DIGITIZER_WITH_ID = hid.ReportDescriptor(
+ description="DIGITIZER",
+ report_descriptor=bytes([
+ # Digitizer (used as an absolute pointer)
+ 0x05, 0x0D, # Usage Page (Digitizers)
+ 0x09, 0x02, # Usage (Pen)
+ 0xA1, 0x01, # Collection (Application)
+ 0x85, REPORT_IDS["DIGITIZER"], # Report ID (n)
+ 0x09, 0x01, # Usage (Stylus)
+ 0xA1, 0x00, # Collection (Physical)
+ 0x09, 0x32, # Usage (In-Range)
+ 0x09, 0x42, # Usage (Tip Switch)
+ 0x09, 0x44, # Usage (Barrel Switch)
+ 0x09, 0x45, # Usage (Eraser Switch)
+ 0x15, 0x00, # Logical Minimum (0)
+ 0x25, 0x01, # Logical Maximum (1)
+ 0x75, 0x01, # Report Size (1)
+ 0x95, 0x04, # Report Count (4)
+ 0x81, 0x02, # Input (Data,Var,Abs)
+ 0x75, 0x04, # Report Size (4) -- Filler
+ 0x95, 0x01, # Report Count (1) -- Filler
+ 0x81, 0x01, # Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ 0x05, 0x01, # Usage Page (Generic Desktop Ctrls)
+ 0x15, 0x00, # Logical Minimum (0)
+ 0x26, 0xff, 0x7f, # Logical Maximum (32767)
+ 0x09, 0x30, # Usage (X)
+ 0x09, 0x31, # Usage (Y)
+ 0x75, 0x10, # Report Size (16)
+ 0x95, 0x02, # Report Count (2)
+ 0x81, 0x02, # Input (Data,Var,Abs)
+ 0xC0, # End Collection
+ 0xC0, # End Collection
+ ]))
+
+# Byte count for each kind of report. Length does not include report ID in first byte.
+REPORT_DESCRIPTORS = {
+ "KEYBOARD" : KEYBOARD_WITH_ID,
+ "MOUSE" : MOUSE_WITH_ID,
+ "CONSUMER" : CONSUMER_WITH_ID,
+ "SYS_CONTROL" : SYS_CONTROL_WITH_ID,
+ "GAMEPAD" : GAMEPAD_WITH_ID,
+ "DIGITIZER" : DIGITIZER_WITH_ID,
+ }
diff --git a/tools/mpy-tool.py b/tools/mpy-tool.py
index 7deb76a8d..5ce24061b 100755
--- a/tools/mpy-tool.py
+++ b/tools/mpy-tool.py
@@ -78,6 +78,18 @@ MP_BC_LOAD_GLOBAL = 0x1d
MP_BC_LOAD_ATTR = 0x1e
MP_BC_STORE_ATTR = 0x26
+# load opcode names
+opcode_names = {}
+with open("../../py/bc0.h") as f:
+ for line in f.readlines():
+ if line.startswith("#define"):
+ s = line.split(maxsplit=3)
+ if len(s) < 3:
+ continue
+ _, name, value = s[:3]
+ opcode = int(value.strip("()"), 0)
+ opcode_names[opcode] = name
+
def make_opcode_format():
def OC4(a, b, c, d):
return a | (b << 2) | (c << 4) | (d << 6)
@@ -252,35 +264,50 @@ class RawCode:
i += 1
RawCode.escaped_names.add(self.escaped_name)
+ sizes = {"bytecode": 0, "strings": 0, "raw_code_overhead": 0, "const_table_overhead": 0, "string_overhead": 0, "number_overhead": 0}
# emit children first
for rc in self.raw_codes:
- rc.freeze(self.escaped_name + '_')
+ subsize = rc.freeze(self.escaped_name + '_')
+ for k in sizes:
+ sizes[k] += subsize[k]
+
# generate bytecode data
print()
print('// frozen bytecode for file %s, scope %s%s' % (self.source_file.str, parent_name, self.simple_name.str))
+ print("// bytecode size", len(self.bytecode))
print('STATIC ', end='')
if not config.MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE:
print('const ', end='')
print('byte bytecode_data_%s[%u] = {' % (self.escaped_name, len(self.bytecode)))
+ sizes["bytecode"] += len(self.bytecode)
print(' ', end='')
for i in range(self.ip2):
print(' 0x%02x,' % self.bytecode[i], end='')
print()
+ print(" // simple name")
print(' ', self.simple_name.qstr_id, '& 0xff,', self.simple_name.qstr_id, '>> 8,')
+ print(" // source file")
print(' ', self.source_file.qstr_id, '& 0xff,', self.source_file.qstr_id, '>> 8,')
+ print(" // code info")
print(' ', end='')
for i in range(self.ip2 + 4, self.ip):
print(' 0x%02x,' % self.bytecode[i], end='')
print()
+ print(" // bytecode")
ip = self.ip
while ip < len(self.bytecode):
f, sz = mp_opcode_format(self.bytecode, ip)
+ opcode = self.bytecode[ip]
+ if opcode in opcode_names:
+ opcode = opcode_names[opcode]
+ else:
+ opcode = '0x%02x' % opcode
if f == 1:
qst = self._unpack_qstr(ip + 1).qstr_id
- print(' ', '0x%02x,' % self.bytecode[ip], qst, '& 0xff,', qst, '>> 8,')
+ print(' {}, {} & 0xff, {} >> 8,'.format(opcode, qst, qst))
else:
- print(' ', ''.join('0x%02x, ' % self.bytecode[ip + i] for i in range(sz)))
+ print(' {},{}'.format(opcode, ''.join(' 0x%02x,' % self.bytecode[ip + i] for i in range(1, sz))))
ip += sz
print('};')
@@ -295,9 +322,12 @@ class RawCode:
obj_type = 'mp_type_str'
else:
obj_type = 'mp_type_bytes'
- print('STATIC const mp_obj_str_t %s = {{&%s}, %u, %u, (const byte*)"%s"};'
+ print('STATIC const mp_obj_str_t %s = {{&%s}, %u, %u, (const byte*)"%s"}; // %s'
% (obj_name, obj_type, qstrutil.compute_hash(obj, config.MICROPY_QSTR_BYTES_IN_HASH),
- len(obj), ''.join(('\\x%02x' % b) for b in obj)))
+ len(obj), ''.join(('\\x%02x' % b) for b in obj), obj))
+ sizes["strings"] += len(obj)
+ sizes["string_overhead"] += 16
+
elif is_int_type(obj):
if config.MICROPY_LONGINT_IMPL == config.MICROPY_LONGINT_IMPL_NONE:
# TODO check if we can actually fit this long-int into a small-int
@@ -321,14 +351,17 @@ class RawCode:
print('STATIC const mp_obj_int_t %s = {{&mp_type_int}, '
'{.neg=%u, .fixed_dig=1, .alloc=%u, .len=%u, .dig=(uint%u_t[]){%s}}};'
% (obj_name, neg, ndigs, ndigs, bits_per_dig, digs))
+ sizes["number_overhead"] += 16
elif type(obj) is float:
print('#if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_A || MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_B')
print('STATIC const mp_obj_float_t %s = {{&mp_type_float}, %.16g};'
% (obj_name, obj))
print('#endif')
+ sizes["number_overhead"] += 8
elif type(obj) is complex:
print('STATIC const mp_obj_complex_t %s = {{&mp_type_complex}, %.16g, %.16g};'
% (obj_name, obj.real, obj.imag))
+ sizes["number_overhead"] += 12
else:
raise FreezeError(self, 'freezing of object %r is not implemented' % (obj,))
@@ -338,8 +371,10 @@ class RawCode:
print('STATIC const mp_rom_obj_t const_table_data_%s[%u] = {'
% (self.escaped_name, const_table_len))
for qst in self.qstrs:
+ sizes["const_table_overhead"] += 4
print(' MP_ROM_QSTR(%s),' % global_qstrs[qst].qstr_id)
for i in range(len(self.objs)):
+ sizes["const_table_overhead"] += 4
if type(self.objs[i]) is float:
print('#if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_A || MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_B')
print(' MP_ROM_PTR(&const_obj_%s_%u),' % (self.escaped_name, i))
@@ -353,6 +388,7 @@ class RawCode:
else:
print(' MP_ROM_PTR(&const_obj_%s_%u),' % (self.escaped_name, i))
for rc in self.raw_codes:
+ sizes["const_table_overhead"] += 4
print(' MP_ROM_PTR(&raw_code_%s),' % rc.escaped_name)
print('};')
@@ -376,6 +412,9 @@ class RawCode:
print(' #endif')
print(' },')
print('};')
+ sizes["raw_code_overhead"] += 16
+
+ return sizes
def read_uint(f):
i = 0
@@ -467,6 +506,7 @@ def freeze_mpy(base_qstrs, raw_codes):
new[q.qstr_esc] = (len(new), q.qstr_esc, q.str)
new = sorted(new.values(), key=lambda x: x[0])
+ print('#include "py/bc0.h"')
print('#include "py/mpconfig.h"')
print('#include "py/objint.h"')
print('#include "py/objstr.h"')
@@ -523,27 +563,45 @@ def freeze_mpy(base_qstrs, raw_codes):
print(' %u, // allocated entries' % len(new))
print(' %u, // used entries' % len(new))
print(' {')
+ qstr_size = {"metadata": 0, "data": 0}
for _, _, qstr in new:
+ qstr_size["metadata"] += config.MICROPY_QSTR_BYTES_IN_LEN + config.MICROPY_QSTR_BYTES_IN_HASH
+ qstr_size["data"] += len(qstr)
print(' %s,'
% qstrutil.make_bytes(config.MICROPY_QSTR_BYTES_IN_LEN, config.MICROPY_QSTR_BYTES_IN_HASH, qstr))
print(' },')
print('};')
+ sizes = {}
for rc in raw_codes:
- rc.freeze(rc.source_file.str.replace('/', '_')[:-3] + '_')
+ sizes[rc.source_file.str] = rc.freeze(rc.source_file.str.replace('/', '_')[:-3] + '_')
print()
print('const char mp_frozen_mpy_names[] = {')
+ qstr_size["filenames"] = 1
for rc in raw_codes:
module_name = rc.source_file.str
print('"%s\\0"' % module_name)
+ qstr_size["filenames"] += len(module_name) + 1
print('"\\0"};')
print('const mp_raw_code_t *const mp_frozen_mpy_content[] = {')
for rc in raw_codes:
print(' &raw_code_%s,' % rc.escaped_name)
+ size = sizes[rc.source_file.str]
+ print(' // Total size:', sum(size.values()))
+ for k in size:
+ print(" // {} {}".format(k, size[k]))
print('};')
+ print()
+ print('// Total size:', sum([sum(x.values()) for x in sizes.values()]) + sum(qstr_size.values()))
+ for k in size:
+ total = sum([x[k] for x in sizes.values()])
+ print("// {} {}".format(k, total))
+ for k in qstr_size:
+ print("// qstr {} {}".format(k, qstr_size[k]))
+
def main():
import argparse
cmd_parser = argparse.ArgumentParser(description='A tool to work with MicroPython .mpy files.')
diff --git a/tools/preprocess_frozen_modules.py b/tools/preprocess_frozen_modules.py
index d157deeee..bb6959f0d 100755
--- a/tools/preprocess_frozen_modules.py
+++ b/tools/preprocess_frozen_modules.py
@@ -33,7 +33,7 @@ def copy_and_process(in_dir, out_dir):
for root, subdirs, files in os.walk(in_dir):
# Skip library examples directories.
- if Path(root).name in ['examples', 'docs']:
+ if Path(root).name in ['examples', 'docs', 'tests']:
continue
for file in files:
diff --git a/tools/usb_descriptor b/tools/usb_descriptor
-Subproject 2507847031a0395465956539253cbfa27f87511
+Subproject 57bf602dac9cba8c4226f764c286cbc60103d67