aboutsummaryrefslogtreecommitdiff
path: root/circuitpython/shared-module
diff options
context:
space:
mode:
Diffstat (limited to 'circuitpython/shared-module')
-rw-r--r--circuitpython/shared-module/_bleio/Address.c45
-rw-r--r--circuitpython/shared-module/_bleio/Address.h41
-rw-r--r--circuitpython/shared-module/_bleio/Attribute.c46
-rw-r--r--circuitpython/shared-module/_bleio/Attribute.h41
-rw-r--r--circuitpython/shared-module/_bleio/Characteristic.h56
-rw-r--r--circuitpython/shared-module/_bleio/ScanEntry.c97
-rw-r--r--circuitpython/shared-module/_bleio/ScanEntry.h47
-rw-r--r--circuitpython/shared-module/_bleio/ScanResults.c139
-rw-r--r--circuitpython/shared-module/_bleio/ScanResults.h64
-rw-r--r--circuitpython/shared-module/_eve/__init__.c323
-rw-r--r--circuitpython/shared-module/_eve/__init__.h37
-rw-r--r--circuitpython/shared-module/_stage/Layer.c105
-rw-r--r--circuitpython/shared-module/_stage/Layer.h48
-rw-r--r--circuitpython/shared-module/_stage/Text.c67
-rw-r--r--circuitpython/shared-module/_stage/Text.h46
-rw-r--r--circuitpython/shared-module/_stage/__init__.c101
-rw-r--r--circuitpython/shared-module/_stage/__init__.h46
-rw-r--r--circuitpython/shared-module/adafruit_bus_device/__init__.c0
-rw-r--r--circuitpython/shared-module/adafruit_bus_device/i2c_device/I2CDevice.c90
-rw-r--r--circuitpython/shared-module/adafruit_bus_device/i2c_device/I2CDevice.h39
-rw-r--r--circuitpython/shared-module/adafruit_bus_device/spi_device/SPIDevice.c102
-rw-r--r--circuitpython/shared-module/adafruit_bus_device/spi_device/SPIDevice.h45
-rw-r--r--circuitpython/shared-module/adafruit_pixelbuf/PixelBuf.c335
-rw-r--r--circuitpython/shared-module/adafruit_pixelbuf/PixelBuf.h72
-rw-r--r--circuitpython/shared-module/adafruit_pixelbuf/__init__.c0
-rw-r--r--circuitpython/shared-module/aesio/__init__.c58
-rw-r--r--circuitpython/shared-module/aesio/__init__.h59
-rw-r--r--circuitpython/shared-module/aesio/aes.c610
-rw-r--r--circuitpython/shared-module/aesio/aes.h105
-rw-r--r--circuitpython/shared-module/atexit/__init__.c92
-rw-r--r--circuitpython/shared-module/atexit/__init__.h46
-rw-r--r--circuitpython/shared-module/audiocore/RawSample.c99
-rw-r--r--circuitpython/shared-module/audiocore/RawSample.h58
-rw-r--r--circuitpython/shared-module/audiocore/WaveFile.c272
-rw-r--r--circuitpython/shared-module/audiocore/WaveFile.h71
-rw-r--r--circuitpython/shared-module/audiocore/__init__.c132
-rw-r--r--circuitpython/shared-module/audiocore/__init__.h85
-rw-r--r--circuitpython/shared-module/audioio/__init__.c0
-rw-r--r--circuitpython/shared-module/audioio/__init__.h32
-rw-r--r--circuitpython/shared-module/audiomixer/Mixer.c365
-rw-r--r--circuitpython/shared-module/audiomixer/Mixer.h69
-rw-r--r--circuitpython/shared-module/audiomixer/MixerVoice.c88
-rw-r--r--circuitpython/shared-module/audiomixer/MixerVoice.h46
-rw-r--r--circuitpython/shared-module/audiomixer/__init__.c0
-rw-r--r--circuitpython/shared-module/audiomixer/__init__.h32
-rw-r--r--circuitpython/shared-module/audiomp3/MP3Decoder.c399
-rw-r--r--circuitpython/shared-module/audiomp3/MP3Decoder.h78
-rw-r--r--circuitpython/shared-module/audiomp3/__init__.c0
-rw-r--r--circuitpython/shared-module/audiomp3/__init__.h30
-rw-r--r--circuitpython/shared-module/audiopwmio/__init__.c0
-rw-r--r--circuitpython/shared-module/audiopwmio/__init__.h32
-rw-r--r--circuitpython/shared-module/bitbangio/I2C.c256
-rw-r--r--circuitpython/shared-module/bitbangio/I2C.h43
-rw-r--r--circuitpython/shared-module/bitbangio/SPI.c309
-rw-r--r--circuitpython/shared-module/bitbangio/SPI.h47
-rw-r--r--circuitpython/shared-module/bitbangio/__init__.c27
-rw-r--r--circuitpython/shared-module/bitmaptools/__init__.c859
-rw-r--r--circuitpython/shared-module/bitops/__init__.c163
-rw-r--r--circuitpython/shared-module/bitops/__init__.h27
-rw-r--r--circuitpython/shared-module/board/__init__.c245
-rw-r--r--circuitpython/shared-module/board/__init__.h32
-rw-r--r--circuitpython/shared-module/canio/Match.c44
-rw-r--r--circuitpython/shared-module/canio/Match.h36
-rw-r--r--circuitpython/shared-module/canio/Message.c70
-rw-r--r--circuitpython/shared-module/canio/Message.h37
-rw-r--r--circuitpython/shared-module/canio/RemoteTransmissionRequest.c60
-rw-r--r--circuitpython/shared-module/canio/RemoteTransmissionRequest.h33
-rw-r--r--circuitpython/shared-module/displayio/Bitmap.c256
-rw-r--r--circuitpython/shared-module/displayio/Bitmap.h55
-rw-r--r--circuitpython/shared-module/displayio/ColorConverter.c287
-rw-r--r--circuitpython/shared-module/displayio/ColorConverter.h56
-rw-r--r--circuitpython/shared-module/displayio/Display.c461
-rw-r--r--circuitpython/shared-module/displayio/Display.h73
-rw-r--r--circuitpython/shared-module/displayio/EPaperDisplay.c453
-rw-r--r--circuitpython/shared-module/displayio/EPaperDisplay.h71
-rw-r--r--circuitpython/shared-module/displayio/FourWire.c179
-rw-r--r--circuitpython/shared-module/displayio/FourWire.h46
-rw-r--r--circuitpython/shared-module/displayio/Group.c451
-rw-r--r--circuitpython/shared-module/displayio/Group.h61
-rw-r--r--circuitpython/shared-module/displayio/I2CDisplay.c127
-rw-r--r--circuitpython/shared-module/displayio/I2CDisplay.h41
-rw-r--r--circuitpython/shared-module/displayio/OnDiskBitmap.c208
-rw-r--r--circuitpython/shared-module/displayio/OnDiskBitmap.h56
-rw-r--r--circuitpython/shared-module/displayio/Palette.c114
-rw-r--r--circuitpython/shared-module/displayio/Palette.h85
-rw-r--r--circuitpython/shared-module/displayio/Shape.c144
-rw-r--r--circuitpython/shared-module/displayio/Shape.h51
-rw-r--r--circuitpython/shared-module/displayio/TileGrid.c652
-rw-r--r--circuitpython/shared-module/displayio/TileGrid.h89
-rw-r--r--circuitpython/shared-module/displayio/__init__.c360
-rw-r--r--circuitpython/shared-module/displayio/__init__.h98
-rw-r--r--circuitpython/shared-module/displayio/area.c161
-rw-r--r--circuitpython/shared-module/displayio/area.h80
-rw-r--r--circuitpython/shared-module/displayio/display_core.c398
-rw-r--r--circuitpython/shared-module/displayio/display_core.h94
-rw-r--r--circuitpython/shared-module/displayio/mipi_constants.h37
-rw-r--r--circuitpython/shared-module/floppyio/__init__.c110
-rw-r--r--circuitpython/shared-module/fontio/BuiltinFont.c79
-rw-r--r--circuitpython/shared-module/fontio/BuiltinFont.h47
-rw-r--r--circuitpython/shared-module/fontio/__init__.c27
-rw-r--r--circuitpython/shared-module/framebufferio/FramebufferDisplay.c375
-rw-r--r--circuitpython/shared-module/framebufferio/FramebufferDisplay.h113
-rw-r--r--circuitpython/shared-module/framebufferio/__init__.c0
-rw-r--r--circuitpython/shared-module/framebufferio/__init__.h0
-rw-r--r--circuitpython/shared-module/gamepadshift/GamePadShift.c52
-rw-r--r--circuitpython/shared-module/gamepadshift/GamePadShift.h43
-rw-r--r--circuitpython/shared-module/gamepadshift/__init__.c57
-rw-r--r--circuitpython/shared-module/gamepadshift/__init__.h33
-rw-r--r--circuitpython/shared-module/getpass/__init__.c61
-rw-r--r--circuitpython/shared-module/getpass/__init__.h34
-rw-r--r--circuitpython/shared-module/gifio/GifWriter.c283
-rw-r--r--circuitpython/shared-module/gifio/GifWriter.h45
-rw-r--r--circuitpython/shared-module/gifio/__init__.c0
-rw-r--r--circuitpython/shared-module/gifio/__init__.h0
-rw-r--r--circuitpython/shared-module/imagecapture/ParallelImageCapture.c44
-rw-r--r--circuitpython/shared-module/ipaddress/IPv4Address.c39
-rw-r--r--circuitpython/shared-module/ipaddress/IPv4Address.h37
-rw-r--r--circuitpython/shared-module/ipaddress/__init__.c35
-rw-r--r--circuitpython/shared-module/ipaddress/__init__.h36
-rw-r--r--circuitpython/shared-module/is31fl3741/FrameBuffer.c227
-rw-r--r--circuitpython/shared-module/is31fl3741/FrameBuffer.h46
-rw-r--r--circuitpython/shared-module/is31fl3741/IS31FL3741.c165
-rw-r--r--circuitpython/shared-module/is31fl3741/IS31FL3741.h61
-rw-r--r--circuitpython/shared-module/is31fl3741/__init__.c25
-rw-r--r--circuitpython/shared-module/is31fl3741/__init__.h0
-rw-r--r--circuitpython/shared-module/is31fl3741/allocator.h35
-rw-r--r--circuitpython/shared-module/keypad/Event.c50
-rw-r--r--circuitpython/shared-module/keypad/Event.h40
-rw-r--r--circuitpython/shared-module/keypad/EventQueue.c98
-rw-r--r--circuitpython/shared-module/keypad/EventQueue.h41
-rw-r--r--circuitpython/shared-module/keypad/KeyMatrix.c165
-rw-r--r--circuitpython/shared-module/keypad/KeyMatrix.h46
-rw-r--r--circuitpython/shared-module/keypad/Keys.c111
-rw-r--r--circuitpython/shared-module/keypad/Keys.h45
-rw-r--r--circuitpython/shared-module/keypad/ShiftRegisterKeys.c134
-rw-r--r--circuitpython/shared-module/keypad/ShiftRegisterKeys.h49
-rw-r--r--circuitpython/shared-module/keypad/__init__.c157
-rw-r--r--circuitpython/shared-module/keypad/__init__.h67
-rw-r--r--circuitpython/shared-module/memorymonitor/AllocationAlarm.c94
-rw-r--r--circuitpython/shared-module/memorymonitor/AllocationAlarm.h51
-rw-r--r--circuitpython/shared-module/memorymonitor/AllocationSize.c91
-rw-r--r--circuitpython/shared-module/memorymonitor/AllocationSize.h51
-rw-r--r--circuitpython/shared-module/memorymonitor/__init__.c39
-rw-r--r--circuitpython/shared-module/memorymonitor/__init__.h35
-rw-r--r--circuitpython/shared-module/msgpack/__init__.c502
-rw-r--r--circuitpython/shared-module/msgpack/__init__.h34
-rw-r--r--circuitpython/shared-module/multiterminal/__init__.c50
-rw-r--r--circuitpython/shared-module/multiterminal/__init__.h34
-rw-r--r--circuitpython/shared-module/onewireio/OneWire.c89
-rw-r--r--circuitpython/shared-module/onewireio/OneWire.h39
-rw-r--r--circuitpython/shared-module/onewireio/__init__.c27
-rw-r--r--circuitpython/shared-module/os/__init__.c214
-rw-r--r--circuitpython/shared-module/paralleldisplay/ParallelBus.c37
-rw-r--r--circuitpython/shared-module/qrio/QRDecoder.c137
-rw-r--r--circuitpython/shared-module/qrio/QRDecoder.h45
-rw-r--r--circuitpython/shared-module/qrio/__init__.c0
-rw-r--r--circuitpython/shared-module/qrio/quirc_alloc.h14
-rw-r--r--circuitpython/shared-module/rainbowio/__init__.c47
-rw-r--r--circuitpython/shared-module/random/__init__.c132
-rw-r--r--circuitpython/shared-module/rgbmatrix/RGBMatrix.c224
-rw-r--r--circuitpython/shared-module/rgbmatrix/RGBMatrix.h50
-rw-r--r--circuitpython/shared-module/rgbmatrix/__init__.c0
-rw-r--r--circuitpython/shared-module/rgbmatrix/__init__.h0
-rw-r--r--circuitpython/shared-module/rgbmatrix/allocator.h37
-rw-r--r--circuitpython/shared-module/rotaryio/IncrementalEncoder.c90
-rw-r--r--circuitpython/shared-module/rotaryio/IncrementalEncoder.h34
-rw-r--r--circuitpython/shared-module/sdcardio/SDCard.c504
-rw-r--r--circuitpython/shared-module/sdcardio/SDCard.h54
-rw-r--r--circuitpython/shared-module/sdcardio/__init__.c0
-rw-r--r--circuitpython/shared-module/sdcardio/__init__.h0
-rw-r--r--circuitpython/shared-module/sharpdisplay/SharpMemoryFramebuffer.c249
-rw-r--r--circuitpython/shared-module/sharpdisplay/SharpMemoryFramebuffer.h59
-rw-r--r--circuitpython/shared-module/sharpdisplay/__init__.c0
-rw-r--r--circuitpython/shared-module/sharpdisplay/__init__.h0
-rw-r--r--circuitpython/shared-module/storage/__init__.c277
-rw-r--r--circuitpython/shared-module/storage/__init__.h40
-rw-r--r--circuitpython/shared-module/struct/__init__.c218
-rw-r--r--circuitpython/shared-module/struct/__init__.h34
-rw-r--r--circuitpython/shared-module/synthio/MidiTrack.c212
-rw-r--r--circuitpython/shared-module/synthio/MidiTrack.h65
-rw-r--r--circuitpython/shared-module/synthio/__init__.c0
-rw-r--r--circuitpython/shared-module/synthio/__init__.h32
-rw-r--r--circuitpython/shared-module/terminalio/Terminal.c177
-rw-r--r--circuitpython/shared-module/terminalio/Terminal.h46
-rw-r--r--circuitpython/shared-module/terminalio/__init__.c27
-rw-r--r--circuitpython/shared-module/terminalio/__init__.h30
-rw-r--r--circuitpython/shared-module/time/__init__.c46
-rw-r--r--circuitpython/shared-module/touchio/TouchIn.c118
-rw-r--r--circuitpython/shared-module/touchio/TouchIn.h44
-rw-r--r--circuitpython/shared-module/touchio/__init__.c1
-rw-r--r--circuitpython/shared-module/traceback/__init__.c31
-rw-r--r--circuitpython/shared-module/traceback/__init__.h36
-rw-r--r--circuitpython/shared-module/uheap/__init__.c292
-rw-r--r--circuitpython/shared-module/usb/__init__.c27
-rw-r--r--circuitpython/shared-module/usb/core/Device.c185
-rw-r--r--circuitpython/shared-module/usb/core/Device.h37
-rw-r--r--circuitpython/shared-module/usb/core/__init__.c27
-rw-r--r--circuitpython/shared-module/usb/utf16le.c85
-rw-r--r--circuitpython/shared-module/usb/utf16le.h34
-rw-r--r--circuitpython/shared-module/usb_cdc/Serial.c158
-rw-r--r--circuitpython/shared-module/usb_cdc/Serial.h39
-rw-r--r--circuitpython/shared-module/usb_cdc/__init__.c417
-rw-r--r--circuitpython/shared-module/usb_cdc/__init__.h51
-rw-r--r--circuitpython/shared-module/usb_hid/Device.c314
-rw-r--r--circuitpython/shared-module/usb_hid/Device.h56
-rw-r--r--circuitpython/shared-module/usb_hid/__init__.c357
-rw-r--r--circuitpython/shared-module/usb_hid/__init__.h52
-rw-r--r--circuitpython/shared-module/usb_midi/PortIn.c38
-rw-r--r--circuitpython/shared-module/usb_midi/PortIn.h39
-rw-r--r--circuitpython/shared-module/usb_midi/PortOut.c38
-rw-r--r--circuitpython/shared-module/usb_midi/PortOut.h39
-rw-r--r--circuitpython/shared-module/usb_midi/__init__.c270
-rw-r--r--circuitpython/shared-module/usb_midi/__init__.h39
-rw-r--r--circuitpython/shared-module/ustack/__init__.c52
-rw-r--r--circuitpython/shared-module/vectorio/Circle.c80
-rw-r--r--circuitpython/shared-module/vectorio/Circle.h18
-rw-r--r--circuitpython/shared-module/vectorio/Polygon.c204
-rw-r--r--circuitpython/shared-module/vectorio/Polygon.h19
-rw-r--r--circuitpython/shared-module/vectorio/Rectangle.c82
-rw-r--r--circuitpython/shared-module/vectorio/Rectangle.h18
-rw-r--r--circuitpython/shared-module/vectorio/VectorShape.c539
-rw-r--r--circuitpython/shared-module/vectorio/VectorShape.h54
-rw-r--r--circuitpython/shared-module/vectorio/__init__.c2
-rw-r--r--circuitpython/shared-module/vectorio/__init__.h14
-rw-r--r--circuitpython/shared-module/zlib/__init__.c100
225 files changed, 23807 insertions, 0 deletions
diff --git a/circuitpython/shared-module/_bleio/Address.c b/circuitpython/shared-module/_bleio/Address.c
new file mode 100644
index 0000000..8c8b043
--- /dev/null
+++ b/circuitpython/shared-module/_bleio/Address.c
@@ -0,0 +1,45 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Dan Halbert for Adafruit Industries
+ * Copyright (c) 2018 Artur Pacholec
+ *
+ * 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.
+ */
+
+#include <string.h>
+
+#include "py/objstr.h"
+#include "shared-bindings/_bleio/Address.h"
+#include "shared-module/_bleio/Address.h"
+
+void common_hal_bleio_address_construct(bleio_address_obj_t *self, uint8_t *bytes, uint8_t address_type) {
+ self->bytes = mp_obj_new_bytes(bytes, NUM_BLEIO_ADDRESS_BYTES);
+ self->type = address_type;
+}
+
+mp_obj_t common_hal_bleio_address_get_address_bytes(bleio_address_obj_t *self) {
+ return self->bytes;
+}
+
+uint8_t common_hal_bleio_address_get_type(bleio_address_obj_t *self) {
+ return self->type;
+}
diff --git a/circuitpython/shared-module/_bleio/Address.h b/circuitpython/shared-module/_bleio/Address.h
new file mode 100644
index 0000000..3978984
--- /dev/null
+++ b/circuitpython/shared-module/_bleio/Address.h
@@ -0,0 +1,41 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Dan Halbert for Adafruit Industries
+ * Copyright (c) 2018 Artur Pacholec
+ *
+ * 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.
+ */
+
+#ifndef MICROPY_INCLUDED_SHARED_MODULE_BLEIO_ADDRESS_H
+#define MICROPY_INCLUDED_SHARED_MODULE_BLEIO_ADDRESS_H
+
+#include "py/obj.h"
+
+#define NUM_BLEIO_ADDRESS_BYTES 6
+
+typedef struct {
+ mp_obj_base_t base;
+ uint8_t type;
+ mp_obj_t bytes; // a bytes() object
+} bleio_address_obj_t;
+
+#endif // MICROPY_INCLUDED_SHARED_MODULE_BLEIO_ADDRESS_H
diff --git a/circuitpython/shared-module/_bleio/Attribute.c b/circuitpython/shared-module/_bleio/Attribute.c
new file mode 100644
index 0000000..3acfcf1
--- /dev/null
+++ b/circuitpython/shared-module/_bleio/Attribute.c
@@ -0,0 +1,46 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 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.
+ */
+
+#include <string.h>
+
+#include "py/runtime.h"
+#include "shared-bindings/_bleio/Attribute.h"
+
+void common_hal_bleio_attribute_security_mode_check_valid(bleio_attribute_security_mode_t security_mode) {
+ switch (security_mode) {
+ case SECURITY_MODE_NO_ACCESS:
+ case SECURITY_MODE_OPEN:
+ case SECURITY_MODE_ENC_NO_MITM:
+ case SECURITY_MODE_ENC_WITH_MITM:
+ case SECURITY_MODE_LESC_ENC_WITH_MITM:
+ case SECURITY_MODE_SIGNED_NO_MITM:
+ case SECURITY_MODE_SIGNED_WITH_MITM:
+ break;
+ default:
+ mp_raise_ValueError(translate("Invalid security_mode"));
+ break;
+ }
+}
diff --git a/circuitpython/shared-module/_bleio/Attribute.h b/circuitpython/shared-module/_bleio/Attribute.h
new file mode 100644
index 0000000..a498a14
--- /dev/null
+++ b/circuitpython/shared-module/_bleio/Attribute.h
@@ -0,0 +1,41 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 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.
+ */
+
+#ifndef MICROPY_INCLUDED_SHARED_MODULE_BLEIO_ATTRIBUTE_H
+#define MICROPY_INCLUDED_SHARED_MODULE_BLEIO_ATTRIBUTE_H
+
+// BLE security modes: 0x<level><mode>
+typedef enum {
+ SECURITY_MODE_NO_ACCESS = 0x00,
+ SECURITY_MODE_OPEN = 0x11,
+ SECURITY_MODE_ENC_NO_MITM = 0x21,
+ SECURITY_MODE_ENC_WITH_MITM = 0x31,
+ SECURITY_MODE_LESC_ENC_WITH_MITM = 0x41,
+ SECURITY_MODE_SIGNED_NO_MITM = 0x12,
+ SECURITY_MODE_SIGNED_WITH_MITM = 0x22,
+} bleio_attribute_security_mode_t;
+
+#endif // MICROPY_INCLUDED_SHARED_MODULE_BLEIO_ATTRIBUTE_H
diff --git a/circuitpython/shared-module/_bleio/Characteristic.h b/circuitpython/shared-module/_bleio/Characteristic.h
new file mode 100644
index 0000000..298592b
--- /dev/null
+++ b/circuitpython/shared-module/_bleio/Characteristic.h
@@ -0,0 +1,56 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 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.
+ */
+
+#ifndef MICROPY_INCLUDED_SHARED_MODULE_BLEIO_CHARACTERISTIC_H
+#define MICROPY_INCLUDED_SHARED_MODULE_BLEIO_CHARACTERISTIC_H
+
+// These are not the Bluetooth spec values. They are what is used by the Nordic SoftDevice.
+// The bit values are in different positions.
+
+typedef enum {
+ CHAR_PROP_NONE = 0,
+ CHAR_PROP_BROADCAST = 1u << 0,
+ CHAR_PROP_INDICATE = 1u << 1,
+ CHAR_PROP_NOTIFY = 1u << 2,
+ CHAR_PROP_READ = 1u << 3,
+ CHAR_PROP_WRITE = 1u << 4,
+ CHAR_PROP_WRITE_NO_RESPONSE = 1u << 5,
+ CHAR_PROP_ALL = (CHAR_PROP_BROADCAST | CHAR_PROP_INDICATE | CHAR_PROP_NOTIFY |
+ CHAR_PROP_READ | CHAR_PROP_WRITE | CHAR_PROP_WRITE_NO_RESPONSE)
+} bleio_characteristic_properties_enum_t;
+typedef uint8_t bleio_characteristic_properties_t;
+
+// Bluetooth spec property values
+#define BT_GATT_CHRC_BROADCAST 0x01
+#define BT_GATT_CHRC_READ 0x02
+#define BT_GATT_CHRC_WRITE_WITHOUT_RESP 0x04
+#define BT_GATT_CHRC_WRITE 0x08
+#define BT_GATT_CHRC_NOTIFY 0x10
+#define BT_GATT_CHRC_INDICATE 0x20
+#define BT_GATT_CHRC_AUTH 0x40
+#define BT_GATT_CHRC_EXT_PROP 0x80
+
+#endif // MICROPY_INCLUDED_SHARED_MODULE_BLEIO_CHARACTERISTIC_H
diff --git a/circuitpython/shared-module/_bleio/ScanEntry.c b/circuitpython/shared-module/_bleio/ScanEntry.c
new file mode 100644
index 0000000..7a57d8a
--- /dev/null
+++ b/circuitpython/shared-module/_bleio/ScanEntry.c
@@ -0,0 +1,97 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Dan Halbert for Adafruit Industries
+ * Copyright (c) 2018 Artur Pacholec
+ * Copyright (c) 2017 Glenn Ruben Bakke
+ *
+ * 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.
+ */
+
+#include <string.h>
+
+#include "shared-bindings/_bleio/Address.h"
+#include "shared-bindings/_bleio/ScanEntry.h"
+#include "shared-module/_bleio/Address.h"
+#include "shared-module/_bleio/ScanEntry.h"
+
+mp_obj_t common_hal_bleio_scanentry_get_address(bleio_scanentry_obj_t *self) {
+ return MP_OBJ_FROM_PTR(self->address);
+}
+
+mp_obj_t common_hal_bleio_scanentry_get_advertisement_bytes(bleio_scanentry_obj_t *self) {
+ return MP_OBJ_FROM_PTR(self->data);
+}
+
+mp_int_t common_hal_bleio_scanentry_get_rssi(bleio_scanentry_obj_t *self) {
+ return self->rssi;
+}
+
+bool common_hal_bleio_scanentry_get_connectable(bleio_scanentry_obj_t *self) {
+ return self->connectable;
+}
+
+bool common_hal_bleio_scanentry_get_scan_response(bleio_scanentry_obj_t *self) {
+ return self->scan_response;
+}
+
+bool bleio_scanentry_data_matches(const uint8_t *data, size_t len, const uint8_t *prefixes, size_t prefixes_length, bool any) {
+ if (prefixes_length == 0) {
+ return true;
+ }
+ if (len == 0) {
+ // Prefixes exist, but no data.
+ return false;
+ }
+ size_t i = 0;
+ while (i < prefixes_length) {
+ uint8_t prefix_length = prefixes[i];
+ i += 1;
+ size_t j = 0;
+ bool prefix_matched = false;
+ while (j < len) {
+ uint8_t structure_length = data[j];
+ j += 1;
+ if (structure_length == 0) {
+ break;
+ }
+ if (structure_length >= prefix_length && memcmp(data + j, prefixes + i, prefix_length) == 0) {
+ if (any) {
+ return true;
+ }
+ prefix_matched = true;
+ break;
+ }
+ j += structure_length;
+ }
+ // If all (!any), the current prefix must have matched at least one field.
+ if (!prefix_matched && !any) {
+ return false;
+ }
+ i += prefix_length;
+ }
+ // All prefixes matched some field (if !any), or none did (if any).
+ return !any;
+}
+
+bool common_hal_bleio_scanentry_matches(bleio_scanentry_obj_t *self, const uint8_t *prefixes, size_t prefixes_len, bool match_all) {
+ return bleio_scanentry_data_matches(self->data->data, self->data->len, prefixes, prefixes_len, !match_all);
+}
diff --git a/circuitpython/shared-module/_bleio/ScanEntry.h b/circuitpython/shared-module/_bleio/ScanEntry.h
new file mode 100644
index 0000000..9e142fd
--- /dev/null
+++ b/circuitpython/shared-module/_bleio/ScanEntry.h
@@ -0,0 +1,47 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Dan Halbert for Adafruit Industries
+ * Copyright (c) 2018 Artur Pacholec
+ *
+ * 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.
+ */
+
+#ifndef MICROPY_INCLUDED_SHARED_MODULE_BLEIO_SCANENTRY_H
+#define MICROPY_INCLUDED_SHARED_MODULE_BLEIO_SCANENTRY_H
+
+#include "py/obj.h"
+#include "py/objstr.h"
+#include "shared-bindings/_bleio/Address.h"
+
+typedef struct {
+ mp_obj_base_t base;
+ bool connectable;
+ bool scan_response;
+ int8_t rssi;
+ bleio_address_obj_t *address;
+ mp_obj_str_t *data;
+ uint64_t time_received;
+} bleio_scanentry_obj_t;
+
+bool bleio_scanentry_data_matches(const uint8_t *data, size_t len, const uint8_t *prefixes, size_t prefix_length, bool any);
+
+#endif // MICROPY_INCLUDED_SHARED_MODULE_BLEIO_SCANENTRY_H
diff --git a/circuitpython/shared-module/_bleio/ScanResults.c b/circuitpython/shared-module/_bleio/ScanResults.c
new file mode 100644
index 0000000..1f4dbff
--- /dev/null
+++ b/circuitpython/shared-module/_bleio/ScanResults.c
@@ -0,0 +1,139 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Dan Halbert for Adafruit Industries
+ * Copyright (c) 2018 Artur Pacholec
+ * Copyright (c) 2017 Glenn Ruben Bakke
+ *
+ * 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.
+ */
+
+#include <string.h>
+
+#include "shared/runtime/interrupt_char.h"
+#include "py/objstr.h"
+#include "py/runtime.h"
+#include "shared-bindings/_bleio/ScanEntry.h"
+#include "shared-bindings/_bleio/ScanResults.h"
+
+bleio_scanresults_obj_t *shared_module_bleio_new_scanresults(size_t buffer_size, uint8_t *prefixes, size_t prefixes_len, mp_int_t minimum_rssi) {
+ bleio_scanresults_obj_t *self = m_new_obj(bleio_scanresults_obj_t);
+ self->base.type = &bleio_scanresults_type;
+ ringbuf_alloc(&self->buf, buffer_size, false);
+ self->prefixes = prefixes;
+ self->prefix_length = prefixes_len;
+ self->minimum_rssi = minimum_rssi;
+ return self;
+}
+
+mp_obj_t common_hal_bleio_scanresults_next(bleio_scanresults_obj_t *self) {
+ while (ringbuf_num_filled(&self->buf) == 0 && !self->done && !mp_hal_is_interrupted()) {
+ RUN_BACKGROUND_TASKS;
+ }
+ if (ringbuf_num_filled(&self->buf) == 0 || mp_hal_is_interrupted()) {
+ return mp_const_none;
+ }
+
+ // Create a ScanEntry out of the data on the buffer.
+ uint8_t type = ringbuf_get(&self->buf);
+ bool connectable = (type & (1 << 0)) != 0;
+ bool scan_response = (type & (1 << 1)) != 0;
+ uint64_t ticks_ms;
+ ringbuf_get_n(&self->buf, (uint8_t *)&ticks_ms, sizeof(ticks_ms));
+ uint8_t rssi = ringbuf_get(&self->buf);
+ uint8_t peer_addr[NUM_BLEIO_ADDRESS_BYTES];
+ ringbuf_get_n(&self->buf, peer_addr, sizeof(peer_addr));
+ uint8_t addr_type = ringbuf_get(&self->buf);
+ uint16_t len;
+ ringbuf_get_n(&self->buf, (uint8_t *)&len, sizeof(len));
+
+ mp_obj_str_t *o = MP_OBJ_TO_PTR(mp_obj_new_bytes_of_zeros(len));
+ ringbuf_get_n(&self->buf, (uint8_t *)o->data, len);
+
+ bleio_scanentry_obj_t *entry = m_new_obj(bleio_scanentry_obj_t);
+ entry->base.type = &bleio_scanentry_type;
+ entry->rssi = rssi;
+
+ bleio_address_obj_t *address = m_new_obj(bleio_address_obj_t);
+ address->base.type = &bleio_address_type;
+ common_hal_bleio_address_construct(MP_OBJ_TO_PTR(address), peer_addr, addr_type);
+ entry->address = address;
+
+ entry->data = o;
+ entry->time_received = ticks_ms;
+ entry->connectable = connectable;
+ entry->scan_response = scan_response;
+
+ return MP_OBJ_FROM_PTR(entry);
+}
+
+
+void shared_module_bleio_scanresults_append(bleio_scanresults_obj_t *self,
+ uint64_t ticks_ms,
+ bool connectable,
+ bool scan_response,
+ int8_t rssi,
+ const uint8_t *peer_addr,
+ uint8_t addr_type,
+ const uint8_t *data,
+ uint16_t len) {
+ int32_t packet_size = sizeof(uint8_t) + sizeof(ticks_ms) + sizeof(rssi) + NUM_BLEIO_ADDRESS_BYTES +
+ sizeof(addr_type) + sizeof(len) + len;
+ int32_t empty_space = self->buf.size - ringbuf_num_filled(&self->buf);
+ if (packet_size >= empty_space) {
+ // We can't fit the packet so skip it.
+ return;
+ }
+ // Filter the packet.
+ if (rssi < self->minimum_rssi) {
+ return;
+ }
+
+ // If any prefixes are provided, then only include packets that include at least one of them.
+ if (!bleio_scanentry_data_matches(data, len, self->prefixes, self->prefix_length, true)) {
+ return;
+ }
+ uint8_t type = 0;
+ if (connectable) {
+ type |= 1 << 0;
+ }
+ if (scan_response) {
+ type |= 1 << 1;
+ }
+
+ // Add the packet to the buffer.
+ ringbuf_put(&self->buf, type);
+ ringbuf_put_n(&self->buf, (uint8_t *)&ticks_ms, sizeof(ticks_ms));
+ ringbuf_put(&self->buf, rssi);
+ ringbuf_put_n(&self->buf, peer_addr, NUM_BLEIO_ADDRESS_BYTES);
+ ringbuf_put(&self->buf, addr_type);
+ ringbuf_put_n(&self->buf, (uint8_t *)&len, sizeof(len));
+ ringbuf_put_n(&self->buf, data, len);
+}
+
+bool shared_module_bleio_scanresults_get_done(bleio_scanresults_obj_t *self) {
+ return self->done;
+}
+
+void shared_module_bleio_scanresults_set_done(bleio_scanresults_obj_t *self, bool done) {
+ self->done = done;
+ self->common_hal_data = NULL;
+}
diff --git a/circuitpython/shared-module/_bleio/ScanResults.h b/circuitpython/shared-module/_bleio/ScanResults.h
new file mode 100644
index 0000000..945809c
--- /dev/null
+++ b/circuitpython/shared-module/_bleio/ScanResults.h
@@ -0,0 +1,64 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Dan Halbert for Adafruit Industries
+ * Copyright (c) 2018 Artur Pacholec
+ *
+ * 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.
+ */
+
+#ifndef MICROPY_INCLUDED_SHARED_MODULE_BLEIO_SCANRESULTS_H
+#define MICROPY_INCLUDED_SHARED_MODULE_BLEIO_SCANRESULTS_H
+
+#include <stdint.h>
+
+#include "py/obj.h"
+#include "py/ringbuf.h"
+
+typedef struct {
+ mp_obj_base_t base;
+ // Pointers that needs to live until the scan is done.
+ void *common_hal_data;
+ ringbuf_t buf;
+ // Prefixes is a length encoded array of prefixes.
+ uint8_t *prefixes;
+ size_t prefix_length;
+ mp_int_t minimum_rssi;
+ bool active;
+ bool done;
+} bleio_scanresults_obj_t;
+
+bleio_scanresults_obj_t *shared_module_bleio_new_scanresults(size_t buffer_size, uint8_t *prefixes, size_t prefixes_len, mp_int_t minimum_rssi);
+
+bool shared_module_bleio_scanresults_get_done(bleio_scanresults_obj_t *self);
+void shared_module_bleio_scanresults_set_done(bleio_scanresults_obj_t *self, bool done);
+
+void shared_module_bleio_scanresults_append(bleio_scanresults_obj_t *self,
+ uint64_t ticks_ms,
+ bool connectable,
+ bool scan_result,
+ int8_t rssi,
+ const uint8_t *peer_addr,
+ uint8_t addr_type,
+ const uint8_t *data,
+ uint16_t len);
+
+#endif // MICROPY_INCLUDED_SHARED_MODULE_BLEIO_SCANRESULTS_H
diff --git a/circuitpython/shared-module/_eve/__init__.c b/circuitpython/shared-module/_eve/__init__.c
new file mode 100644
index 0000000..a39e6f9
--- /dev/null
+++ b/circuitpython/shared-module/_eve/__init__.c
@@ -0,0 +1,323 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 James Bowman for Excamera Labs
+ *
+ * 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.
+ */
+
+#include <stddef.h>
+#include <stdint.h>
+#include "py/runtime.h"
+#include "shared-bindings/_eve/__init__.h"
+#include "shared-module/_eve/__init__.h"
+
+STATIC void write(common_hal__eve_t *eve, size_t len, void *buf) {
+ eve->dest[2] = mp_obj_new_bytearray_by_ref(len, buf);
+ mp_call_method_n_kw(1, 0, eve->dest);
+}
+
+void common_hal__eve_flush(common_hal__eve_t *eve) {
+ if (eve->n != 0) {
+ write(eve, eve->n, eve->buf);
+ eve->n = 0;
+ }
+}
+
+static void *append(common_hal__eve_t *eve, size_t m) {
+ if ((eve->n + m) > sizeof(eve->buf)) {
+ common_hal__eve_flush(eve);
+ }
+ uint8_t *r = eve->buf + eve->n;
+ eve->n += m;
+ return (void *)r;
+}
+
+void common_hal__eve_add(common_hal__eve_t *eve, size_t len, void *buf) {
+ if (len <= sizeof(eve->buf)) {
+ uint8_t *p = (uint8_t *)append(eve, len);
+ // memcpy(p, buffer_info.buf, buffer_info.len);
+ uint8_t *s = buf;
+ for (size_t i = 0; i < len; i++) { *p++ = *s++;
+ }
+ } else {
+ common_hal__eve_flush(eve);
+ write(eve, len, buf);
+ }
+}
+
+#define C4(eve, u) (*(uint32_t *)append((eve), sizeof(uint32_t)) = (u))
+
+void common_hal__eve_Vertex2f(common_hal__eve_t *eve, mp_float_t x, mp_float_t y) {
+ int16_t ix = (int)(eve->vscale * x);
+ int16_t iy = (int)(eve->vscale * y);
+ C4(eve, (1 << 30) | ((ix & 32767) << 15) | (iy & 32767));
+}
+
+void common_hal__eve_VertexFormat(common_hal__eve_t *eve, uint32_t frac) {
+ C4(eve, ((39 << 24) | ((frac & 7))));
+ eve->vscale = 1 << frac;
+}
+
+
+
+void common_hal__eve_AlphaFunc(common_hal__eve_t *eve, uint32_t func, uint32_t ref) {
+ C4(eve, ((9 << 24) | ((func & 7) << 8) | ((ref & 255))));
+}
+
+
+void common_hal__eve_Begin(common_hal__eve_t *eve, uint32_t prim) {
+ C4(eve, ((31 << 24) | ((prim & 15))));
+}
+
+
+void common_hal__eve_BitmapExtFormat(common_hal__eve_t *eve, uint32_t fmt) {
+ C4(eve, ((46 << 24) | (fmt & 65535)));
+}
+
+
+void common_hal__eve_BitmapHandle(common_hal__eve_t *eve, uint32_t handle) {
+ C4(eve, ((5 << 24) | ((handle & 31))));
+}
+
+
+void common_hal__eve_BitmapLayoutH(common_hal__eve_t *eve, uint32_t linestride, uint32_t height) {
+ C4(eve, ((40 << 24) | (((linestride) & 3) << 2) | (((height) & 3))));
+}
+
+
+void common_hal__eve_BitmapLayout(common_hal__eve_t *eve, uint32_t format, uint32_t linestride, uint32_t height) {
+ C4(eve, ((7 << 24) | ((format & 31) << 19) | ((linestride & 1023) << 9) | ((height & 511))));
+}
+
+
+void common_hal__eve_BitmapSizeH(common_hal__eve_t *eve, uint32_t width, uint32_t height) {
+ C4(eve, ((41 << 24) | (((width) & 3) << 2) | (((height) & 3))));
+}
+
+
+void common_hal__eve_BitmapSize(common_hal__eve_t *eve, uint32_t filter, uint32_t wrapx, uint32_t wrapy, uint32_t width, uint32_t height) {
+ C4(eve, ((8 << 24) | ((filter & 1) << 20) | ((wrapx & 1) << 19) | ((wrapy & 1) << 18) | ((width & 511) << 9) | ((height & 511))));
+}
+
+
+void common_hal__eve_BitmapSource(common_hal__eve_t *eve, uint32_t addr) {
+ C4(eve, ((1 << 24) | ((addr & 0xffffff))));
+}
+
+
+void common_hal__eve_BitmapSwizzle(common_hal__eve_t *eve, uint32_t r, uint32_t g, uint32_t b, uint32_t a) {
+ C4(eve, ((47 << 24) | ((r & 7) << 9) | ((g & 7) << 6) | ((b & 7) << 3) | ((a & 7))));
+}
+
+
+void common_hal__eve_BitmapTransformA(common_hal__eve_t *eve, uint32_t p, uint32_t v) {
+ C4(eve, ((21 << 24) | ((p & 1) << 17) | ((v & 131071))));
+}
+
+
+void common_hal__eve_BitmapTransformB(common_hal__eve_t *eve, uint32_t p, uint32_t v) {
+ C4(eve, ((22 << 24) | ((p & 1) << 17) | ((v & 131071))));
+}
+
+
+void common_hal__eve_BitmapTransformC(common_hal__eve_t *eve, uint32_t v) {
+ C4(eve, ((23 << 24) | ((v & 16777215))));
+}
+
+
+void common_hal__eve_BitmapTransformD(common_hal__eve_t *eve, uint32_t p, uint32_t v) {
+ C4(eve, ((24 << 24) | ((p & 1) << 17) | ((v & 131071))));
+}
+
+
+void common_hal__eve_BitmapTransformE(common_hal__eve_t *eve, uint32_t p, uint32_t v) {
+ C4(eve, ((25 << 24) | ((p & 1) << 17) | ((v & 131071))));
+}
+
+
+void common_hal__eve_BitmapTransformF(common_hal__eve_t *eve, uint32_t v) {
+ C4(eve, ((26 << 24) | ((v & 16777215))));
+}
+
+
+void common_hal__eve_BlendFunc(common_hal__eve_t *eve, uint32_t src, uint32_t dst) {
+ C4(eve, ((11 << 24) | ((src & 7) << 3) | ((dst & 7))));
+}
+
+
+void common_hal__eve_Call(common_hal__eve_t *eve, uint32_t dest) {
+ C4(eve, ((29 << 24) | ((dest & 65535))));
+}
+
+
+void common_hal__eve_Cell(common_hal__eve_t *eve, uint32_t cell) {
+ C4(eve, ((6 << 24) | ((cell & 127))));
+}
+
+
+void common_hal__eve_ClearColorA(common_hal__eve_t *eve, uint32_t alpha) {
+ C4(eve, ((15 << 24) | ((alpha & 255))));
+}
+
+
+void common_hal__eve_ClearColorRGB(common_hal__eve_t *eve, uint32_t red, uint32_t green, uint32_t blue) {
+ C4(eve, ((2 << 24) | ((red & 255) << 16) | ((green & 255) << 8) | ((blue & 255))));
+}
+
+
+void common_hal__eve_Clear(common_hal__eve_t *eve, uint32_t c, uint32_t s, uint32_t t) {
+ C4(eve, ((38 << 24) | ((c & 1) << 2) | ((s & 1) << 1) | ((t & 1))));
+}
+
+
+void common_hal__eve_ClearStencil(common_hal__eve_t *eve, uint32_t s) {
+ C4(eve, ((17 << 24) | ((s & 255))));
+}
+
+
+void common_hal__eve_ClearTag(common_hal__eve_t *eve, uint32_t s) {
+ C4(eve, ((18 << 24) | ((s & 255))));
+}
+
+
+void common_hal__eve_ColorA(common_hal__eve_t *eve, uint32_t alpha) {
+ C4(eve, ((16 << 24) | ((alpha & 255))));
+}
+
+
+void common_hal__eve_ColorMask(common_hal__eve_t *eve, uint32_t r, uint32_t g, uint32_t b, uint32_t a) {
+ C4(eve, ((32 << 24) | ((r & 1) << 3) | ((g & 1) << 2) | ((b & 1) << 1) | ((a & 1))));
+}
+
+
+void common_hal__eve_ColorRGB(common_hal__eve_t *eve, uint32_t red, uint32_t green, uint32_t blue) {
+ C4(eve, ((4 << 24) | ((red & 255) << 16) | ((green & 255) << 8) | ((blue & 255))));
+}
+
+
+void common_hal__eve_Display(common_hal__eve_t *eve) {
+ C4(eve, ((0 << 24)));
+}
+
+
+void common_hal__eve_End(common_hal__eve_t *eve) {
+ C4(eve, ((33 << 24)));
+}
+
+
+void common_hal__eve_Jump(common_hal__eve_t *eve, uint32_t dest) {
+ C4(eve, ((30 << 24) | ((dest & 65535))));
+}
+
+
+void common_hal__eve_LineWidth(common_hal__eve_t *eve, mp_float_t width) {
+ int16_t iw = (int)(8 * width);
+ C4(eve, ((14 << 24) | ((iw & 4095))));
+}
+
+
+void common_hal__eve_Macro(common_hal__eve_t *eve, uint32_t m) {
+ C4(eve, ((37 << 24) | ((m & 1))));
+}
+
+
+void common_hal__eve_Nop(common_hal__eve_t *eve) {
+ C4(eve, ((45 << 24)));
+}
+
+
+void common_hal__eve_PaletteSource(common_hal__eve_t *eve, uint32_t addr) {
+ C4(eve, ((42 << 24) | (((addr) & 4194303))));
+}
+
+
+void common_hal__eve_PointSize(common_hal__eve_t *eve, mp_float_t size) {
+ int16_t is = (int)(8 * size);
+ C4(eve, ((13 << 24) | ((is & 8191))));
+}
+
+
+void common_hal__eve_RestoreContext(common_hal__eve_t *eve) {
+ C4(eve, ((35 << 24)));
+}
+
+
+void common_hal__eve_Return(common_hal__eve_t *eve) {
+ C4(eve, ((36 << 24)));
+}
+
+
+void common_hal__eve_SaveContext(common_hal__eve_t *eve) {
+ C4(eve, ((34 << 24)));
+}
+
+
+void common_hal__eve_ScissorSize(common_hal__eve_t *eve, uint32_t width, uint32_t height) {
+ C4(eve, ((28 << 24) | ((width & 4095) << 12) | ((height & 4095))));
+}
+
+
+void common_hal__eve_ScissorXY(common_hal__eve_t *eve, uint32_t x, uint32_t y) {
+ C4(eve, ((27 << 24) | ((x & 2047) << 11) | ((y & 2047))));
+}
+
+
+void common_hal__eve_StencilFunc(common_hal__eve_t *eve, uint32_t func, uint32_t ref, uint32_t mask) {
+ C4(eve, ((10 << 24) | ((func & 7) << 16) | ((ref & 255) << 8) | ((mask & 255))));
+}
+
+
+void common_hal__eve_StencilMask(common_hal__eve_t *eve, uint32_t mask) {
+ C4(eve, ((19 << 24) | ((mask & 255))));
+}
+
+
+void common_hal__eve_StencilOp(common_hal__eve_t *eve, uint32_t sfail, uint32_t spass) {
+ C4(eve, ((12 << 24) | ((sfail & 7) << 3) | ((spass & 7))));
+}
+
+
+void common_hal__eve_TagMask(common_hal__eve_t *eve, uint32_t mask) {
+ C4(eve, ((20 << 24) | ((mask & 1))));
+}
+
+
+void common_hal__eve_Tag(common_hal__eve_t *eve, uint32_t s) {
+ C4(eve, ((3 << 24) | ((s & 255))));
+}
+
+
+void common_hal__eve_VertexTranslateX(common_hal__eve_t *eve, mp_float_t x) {
+ int16_t ix = (int)(16 * x);
+ C4(eve, ((43 << 24) | (ix & 131071)));
+}
+
+
+void common_hal__eve_VertexTranslateY(common_hal__eve_t *eve, mp_float_t y) {
+ int16_t iy = (int)(16 * y);
+ C4(eve, ((44 << 24) | (iy & 131071)));
+}
+
+
+void common_hal__eve_Vertex2ii(common_hal__eve_t *eve, uint32_t x, uint32_t y, uint32_t handle, uint32_t cell) {
+ C4(eve, ((2 << 30) | (((x) & 511) << 21) | (((y) & 511) << 12) | (((handle) & 31) << 7) | (((cell) & 127) << 0)));
+}
diff --git a/circuitpython/shared-module/_eve/__init__.h b/circuitpython/shared-module/_eve/__init__.h
new file mode 100644
index 0000000..5217d86
--- /dev/null
+++ b/circuitpython/shared-module/_eve/__init__.h
@@ -0,0 +1,37 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 James Bowman for Excamera Labs
+ *
+ * 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.
+ */
+
+#ifndef MICROPY_INCLUDED_SHARED_MODULE__EVE___INIT___H
+#define MICROPY_INCLUDED_SHARED_MODULE__EVE___INIT___H
+
+typedef struct _common_hal__eve_t {
+ mp_obj_t dest[3]; // Own 'write' method, plus argument
+ int vscale; // fixed-point scaling used for Vertex2f
+ size_t n; // Current size of command buffer
+ uint8_t buf[512]; // Command buffer
+} common_hal__eve_t;
+
+#endif // MICROPY_INCLUDED_SHARED_MODULE__EVE___INIT___H
diff --git a/circuitpython/shared-module/_stage/Layer.c b/circuitpython/shared-module/_stage/Layer.c
new file mode 100644
index 0000000..6d06e72
--- /dev/null
+++ b/circuitpython/shared-module/_stage/Layer.c
@@ -0,0 +1,105 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2017 Radomir Dopieralski
+ *
+ * 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.
+ */
+
+#include "Layer.h"
+#include "__init__.h"
+
+
+// Get the color of the pixel on the layer.
+uint16_t get_layer_pixel(layer_obj_t *layer, int16_t x, int16_t y) {
+
+ // Shift by the layer's position offset.
+ x -= layer->x;
+ y -= layer->y;
+
+ // Bounds check.
+ if ((x < 0) || (x >= layer->width << 4) ||
+ (y < 0) || (y >= layer->height << 4)) {
+ return TRANSPARENT;
+ }
+
+ // Get the tile from the grid location or from sprite frame.
+ uint8_t frame = layer->frame;
+ if (layer->map) {
+ uint8_t tx = x >> 4;
+ uint8_t ty = y >> 4;
+
+ frame = layer->map[(ty * layer->width + tx) >> 1];
+ if (tx & 0x01) {
+ frame &= 0x0f;
+ } else {
+ frame >>= 4;
+ }
+ }
+
+ // Get the position within the tile.
+ x &= 0x0f;
+ y &= 0x0f;
+
+ // Rotate the image.
+ uint8_t ty = y; // Temporary variable for swapping.
+ switch (layer->rotation) {
+ case 1: // 90 degrees clockwise
+ y = 15 - x;
+ x = ty;
+ break;
+ case 2: // 180 degrees
+ y = 15 - ty;
+ x = 15 - x;
+ break;
+ case 3: // 90 degrees counter-clockwise
+ y = x;
+ x = 15 - ty;
+ break;
+ case 4: // 0 degrees, mirrored
+ x = 15 - x;
+ break;
+ case 5: // 90 degrees clockwise, mirrored
+ y = x;
+ x = ty;
+ break;
+ case 6: // 180 degrees, mirrored
+ y = 15 - ty;
+ break;
+ case 7: // 90 degrees counter-clockwise, mirrored
+ y = 15 - x;
+ x = 15 - ty;
+ break;
+ default: // 0 degrees
+ break;
+ }
+
+ // Get the value of the pixel.
+ uint8_t pixel = layer->graphic[(frame << 7) + (y << 3) + (x >> 1)];
+ if (x & 0x01) {
+ pixel &= 0x0f;
+ } else {
+ pixel >>= 4;
+ }
+
+ // Convert to 16-bit color using the palette.
+ return layer->palette[pixel << 1] | layer->palette[(pixel << 1) + 1] << 8;
+}
diff --git a/circuitpython/shared-module/_stage/Layer.h b/circuitpython/shared-module/_stage/Layer.h
new file mode 100644
index 0000000..4233847
--- /dev/null
+++ b/circuitpython/shared-module/_stage/Layer.h
@@ -0,0 +1,48 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2017 Radomir Dopieralski
+ *
+ * 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.
+ */
+
+#ifndef MICROPY_INCLUDED_SHARED_MODULE__STAGE_LAYER_H
+#define MICROPY_INCLUDED_SHARED_MODULE__STAGE_LAYER_H
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "py/obj.h"
+
+typedef struct {
+ mp_obj_base_t base;
+ uint8_t *map;
+ uint8_t *graphic;
+ uint8_t *palette;
+ int16_t x, y;
+ uint8_t width, height;
+ uint8_t frame;
+ uint8_t rotation;
+} layer_obj_t;
+
+uint16_t get_layer_pixel(layer_obj_t *layer, int16_t x, int16_t y);
+
+#endif // MICROPY_INCLUDED_SHARED_MODULE__STAGE_LAYER
diff --git a/circuitpython/shared-module/_stage/Text.c b/circuitpython/shared-module/_stage/Text.c
new file mode 100644
index 0000000..a803b85
--- /dev/null
+++ b/circuitpython/shared-module/_stage/Text.c
@@ -0,0 +1,67 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2017 Radomir Dopieralski
+ *
+ * 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.
+ */
+
+#include "Text.h"
+#include "__init__.h"
+
+
+// Get the color of the pixel on the text.
+uint16_t get_text_pixel(text_obj_t *text, int16_t x, int16_t y) {
+
+ // Shift by the text's position offset.
+ x -= text->x;
+ y -= text->y;
+
+ // Bounds check.
+ if ((x < 0) || (x >= text->width << 3) ||
+ (y < 0) || (y >= text->height << 3)) {
+ return TRANSPARENT;
+ }
+
+ // Get the tile from the grid location or from sprite frame.
+ uint8_t tx = x >> 3;
+ uint8_t ty = y >> 3;
+ uint8_t c = text->chars[ty * text->width + tx];
+ uint8_t color_offset = 0;
+ if (c & 0x80) {
+ color_offset = 4;
+ }
+ c &= 0x7f;
+ if (!c) {
+ return TRANSPARENT;
+ }
+
+ // Get the position within the char.
+ x &= 0x07;
+ y &= 0x07;
+
+ // Get the value of the pixel.
+ uint8_t pixel = text->font[(c << 4) + (y << 1) + (x >> 2)];
+ pixel = ((pixel >> ((x & 0x03) << 1)) & 0x03) + color_offset;
+
+ // Convert to 16-bit color using the palette.
+ return text->palette[pixel << 1] | text->palette[(pixel << 1) + 1] << 8;
+}
diff --git a/circuitpython/shared-module/_stage/Text.h b/circuitpython/shared-module/_stage/Text.h
new file mode 100644
index 0000000..dd75465
--- /dev/null
+++ b/circuitpython/shared-module/_stage/Text.h
@@ -0,0 +1,46 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2017 Radomir Dopieralski
+ *
+ * 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.
+ */
+
+#ifndef MICROPY_INCLUDED_SHARED_MODULE__STAGE_TEXT_H
+#define MICROPY_INCLUDED_SHARED_MODULE__STAGE_TEXT_H
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "py/obj.h"
+
+typedef struct {
+ mp_obj_base_t base;
+ uint8_t *chars;
+ uint8_t *font;
+ uint8_t *palette;
+ int16_t x, y;
+ uint8_t width, height;
+} text_obj_t;
+
+uint16_t get_text_pixel(text_obj_t *text, int16_t x, int16_t y);
+
+#endif // MICROPY_INCLUDED_SHARED_MODULE__STAGE_TEXT
diff --git a/circuitpython/shared-module/_stage/__init__.c b/circuitpython/shared-module/_stage/__init__.c
new file mode 100644
index 0000000..7ec640b
--- /dev/null
+++ b/circuitpython/shared-module/_stage/__init__.c
@@ -0,0 +1,101 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2017 Radomir Dopieralski
+ *
+ * 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.
+ */
+
+#include "Layer.h"
+#include "Text.h"
+#include "__init__.h"
+#include "shared-bindings/_stage/Layer.h"
+#include "shared-bindings/_stage/Text.h"
+
+
+void render_stage(
+ uint16_t x0, uint16_t y0,
+ uint16_t x1, uint16_t y1,
+ int16_t vx, int16_t vy,
+ mp_obj_t *layers, size_t layers_size,
+ uint16_t *buffer, size_t buffer_size,
+ displayio_display_obj_t *display,
+ uint8_t scale, uint16_t background) {
+
+
+ displayio_area_t area;
+ area.x1 = x0 * scale;
+ area.y1 = y0 * scale;
+ area.x2 = x1 * scale;
+ area.y2 = y1 * scale;
+ displayio_display_core_set_region_to_update(
+ &display->core, display->set_column_command, display->set_row_command,
+ NO_COMMAND, NO_COMMAND, display->data_as_commands, false, &area,
+ display->SH1107_addressing);
+
+ while (!displayio_display_core_begin_transaction(&display->core)) {
+ RUN_BACKGROUND_TASKS;
+ }
+ display->core.send(display->core.bus, DISPLAY_COMMAND,
+ CHIP_SELECT_TOGGLE_EVERY_BYTE,
+ &display->write_ram_command, 1);
+ size_t index = 0;
+ for (int16_t y = y0 + vy; y < y1 + vy; ++y) {
+ for (uint8_t yscale = 0; yscale < scale; ++yscale) {
+ for (int16_t x = x0 + vx; x < x1 + vx; ++x) {
+ uint16_t c = TRANSPARENT;
+ for (size_t layer = 0; layer < layers_size; ++layer) {
+ layer_obj_t *obj = MP_OBJ_TO_PTR(layers[layer]);
+ if (obj->base.type == &mp_type_layer) {
+ c = get_layer_pixel(obj, x, y);
+ } else if (obj->base.type == &mp_type_text) {
+ c = get_text_pixel((text_obj_t *)obj, x, y);
+ }
+ if (c != TRANSPARENT) {
+ break;
+ }
+ }
+ if (c == TRANSPARENT) {
+ c = background;
+ }
+ for (uint8_t xscale = 0; xscale < scale; ++xscale) {
+ buffer[index] = c;
+ index += 1;
+ // The buffer is full, send it.
+ if (index >= buffer_size) {
+ display->core.send(display->core.bus, DISPLAY_DATA,
+ CHIP_SELECT_UNTOUCHED,
+ ((uint8_t *)buffer), buffer_size * 2);
+ index = 0;
+ }
+ }
+ }
+ }
+ }
+ // Send the remaining data.
+ if (index) {
+ display->core.send(display->core.bus, DISPLAY_DATA,
+ CHIP_SELECT_UNTOUCHED,
+ ((uint8_t *)buffer), index * 2);
+ }
+
+ displayio_display_core_end_transaction(&display->core);
+}
diff --git a/circuitpython/shared-module/_stage/__init__.h b/circuitpython/shared-module/_stage/__init__.h
new file mode 100644
index 0000000..596752b
--- /dev/null
+++ b/circuitpython/shared-module/_stage/__init__.h
@@ -0,0 +1,46 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2017 Radomir Dopieralski
+ *
+ * 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.
+ */
+
+#ifndef MICROPY_INCLUDED_SHARED_MODULE__STAGE_H
+#define MICROPY_INCLUDED_SHARED_MODULE__STAGE_H
+
+#include "shared-bindings/displayio/Display.h"
+#include <stdint.h>
+#include <stdbool.h>
+#include "py/obj.h"
+
+#define TRANSPARENT (0x1ff8)
+
+void render_stage(
+ uint16_t x0, uint16_t y0,
+ uint16_t x1, uint16_t y1,
+ int16_t vx, int16_t vy,
+ mp_obj_t *layers, size_t layers_size,
+ uint16_t *buffer, size_t buffer_size,
+ displayio_display_obj_t *display,
+ uint8_t scale, uint16_t background);
+
+#endif // MICROPY_INCLUDED_SHARED_MODULE__STAGE
diff --git a/circuitpython/shared-module/adafruit_bus_device/__init__.c b/circuitpython/shared-module/adafruit_bus_device/__init__.c
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/circuitpython/shared-module/adafruit_bus_device/__init__.c
diff --git a/circuitpython/shared-module/adafruit_bus_device/i2c_device/I2CDevice.c b/circuitpython/shared-module/adafruit_bus_device/i2c_device/I2CDevice.c
new file mode 100644
index 0000000..a630f1e
--- /dev/null
+++ b/circuitpython/shared-module/adafruit_bus_device/i2c_device/I2CDevice.c
@@ -0,0 +1,90 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Mark Komus
+ *
+ * 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.
+ */
+
+#include "shared-bindings/adafruit_bus_device/i2c_device/I2CDevice.h"
+#include "shared-bindings/busio/I2C.h"
+#include "py/mperrno.h"
+#include "py/nlr.h"
+#include "py/runtime.h"
+#include "shared/runtime/interrupt_char.h"
+
+void common_hal_adafruit_bus_device_i2cdevice_construct(adafruit_bus_device_i2cdevice_obj_t *self, mp_obj_t *i2c, uint8_t device_address) {
+ self->i2c = i2c;
+ self->device_address = device_address;
+}
+
+void common_hal_adafruit_bus_device_i2cdevice_lock(adafruit_bus_device_i2cdevice_obj_t *self) {
+ mp_obj_t dest[2];
+ mp_load_method(self->i2c, MP_QSTR_try_lock, dest);
+
+ mp_obj_t success = mp_call_method_n_kw(0, 0, dest);
+
+ while (!mp_obj_is_true(success)) {
+ RUN_BACKGROUND_TASKS;
+ if (mp_hal_is_interrupted()) {
+ break;
+ }
+
+ success = mp_call_method_n_kw(0, 0, dest);
+ }
+}
+
+void common_hal_adafruit_bus_device_i2cdevice_unlock(adafruit_bus_device_i2cdevice_obj_t *self) {
+ mp_obj_t dest[2];
+ mp_load_method(self->i2c, MP_QSTR_unlock, dest);
+ mp_call_method_n_kw(0, 0, dest);
+}
+
+void common_hal_adafruit_bus_device_i2cdevice_probe_for_device(adafruit_bus_device_i2cdevice_obj_t *self) {
+ common_hal_adafruit_bus_device_i2cdevice_lock(self);
+
+ mp_buffer_info_t write_bufinfo;
+ mp_obj_t write_buffer = mp_obj_new_bytearray_of_zeros(0);
+ mp_get_buffer_raise(write_buffer, &write_bufinfo, MP_BUFFER_READ);
+
+ mp_obj_t dest[4];
+
+ /* catch exceptions that may be thrown while probing for the device */
+ nlr_buf_t nlr;
+ if (nlr_push(&nlr) == 0) {
+ mp_load_method(self->i2c, MP_QSTR_writeto, dest);
+ dest[2] = MP_OBJ_NEW_SMALL_INT(self->device_address);
+ dest[3] = write_buffer;
+ mp_call_method_n_kw(2, 0, dest);
+ nlr_pop();
+ } else {
+ common_hal_adafruit_bus_device_i2cdevice_unlock(self);
+
+ if (mp_obj_is_subclass_fast(MP_OBJ_FROM_PTR(((mp_obj_base_t *)nlr.ret_val)->type), MP_OBJ_FROM_PTR(&mp_type_OSError))) {
+ mp_raise_ValueError_varg(translate("No I2C device at address: 0x%x"), self->device_address);
+ } else {
+ /* In case we receive an unrelated exception pass it up */
+ nlr_raise(MP_OBJ_FROM_PTR(nlr.ret_val));
+ }
+ }
+
+ common_hal_adafruit_bus_device_i2cdevice_unlock(self);
+}
diff --git a/circuitpython/shared-module/adafruit_bus_device/i2c_device/I2CDevice.h b/circuitpython/shared-module/adafruit_bus_device/i2c_device/I2CDevice.h
new file mode 100644
index 0000000..b76bafb
--- /dev/null
+++ b/circuitpython/shared-module/adafruit_bus_device/i2c_device/I2CDevice.h
@@ -0,0 +1,39 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2017 Scott Shawcroft 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.
+ */
+
+#ifndef MICROPY_INCLUDED_ATMEL_SAMD_SHARED_MODULE_BUSDEVICE_I2CDEVICE_H
+#define MICROPY_INCLUDED_ATMEL_SAMD_SHARED_MODULE_BUSDEVICE_I2CDEVICE_H
+
+#include "py/obj.h"
+#include "common-hal/busio/I2C.h"
+
+typedef struct {
+ mp_obj_base_t base;
+ mp_obj_t *i2c;
+ uint8_t device_address;
+} adafruit_bus_device_i2cdevice_obj_t;
+
+#endif // MICROPY_INCLUDED_ATMEL_SAMD_SHARED_MODULE_BUSDEVICE_I2CDEVICE_H
diff --git a/circuitpython/shared-module/adafruit_bus_device/spi_device/SPIDevice.c b/circuitpython/shared-module/adafruit_bus_device/spi_device/SPIDevice.c
new file mode 100644
index 0000000..2f4ab4d
--- /dev/null
+++ b/circuitpython/shared-module/adafruit_bus_device/spi_device/SPIDevice.c
@@ -0,0 +1,102 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Mark Komus
+ *
+ * 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.
+ */
+
+#include "shared-bindings/adafruit_bus_device/spi_device/SPIDevice.h"
+#include "shared-bindings/busio/SPI.h"
+#include "shared-bindings/digitalio/DigitalInOut.h"
+#include "py/mperrno.h"
+#include "py/nlr.h"
+#include "py/runtime.h"
+
+void common_hal_adafruit_bus_device_spidevice_construct(adafruit_bus_device_spidevice_obj_t *self, busio_spi_obj_t *spi, digitalio_digitalinout_obj_t *cs,
+ bool cs_active_value, uint32_t baudrate, uint8_t polarity, uint8_t phase, uint8_t extra_clocks) {
+ self->spi = spi;
+ self->baudrate = baudrate;
+ self->polarity = polarity;
+ self->phase = phase;
+ self->extra_clocks = extra_clocks;
+ self->chip_select = cs;
+ self->cs_active_value = cs_active_value;
+}
+
+mp_obj_t common_hal_adafruit_bus_device_spidevice_enter(adafruit_bus_device_spidevice_obj_t *self) {
+ {
+ mp_obj_t dest[2];
+ mp_load_method(self->spi, MP_QSTR_try_lock, dest);
+
+ while (!mp_obj_is_true(mp_call_method_n_kw(0, 0, dest))) {
+ mp_handle_pending(true);
+ }
+ }
+
+ {
+ mp_obj_t dest[10];
+ mp_load_method(self->spi, MP_QSTR_configure, dest);
+ dest[2] = MP_OBJ_NEW_QSTR(MP_QSTR_baudrate);
+ dest[3] = MP_OBJ_NEW_SMALL_INT(self->baudrate);
+ dest[4] = MP_OBJ_NEW_QSTR(MP_QSTR_polarity);
+ dest[5] = MP_OBJ_NEW_SMALL_INT(self->polarity);
+ dest[6] = MP_OBJ_NEW_QSTR(MP_QSTR_phase);
+ dest[7] = MP_OBJ_NEW_SMALL_INT(self->phase);
+ dest[8] = MP_OBJ_NEW_QSTR(MP_QSTR_bits);
+ dest[9] = MP_OBJ_NEW_SMALL_INT(8);
+ mp_call_method_n_kw(0, 4, dest);
+ }
+
+ if (self->chip_select != MP_OBJ_NULL) {
+ common_hal_digitalio_digitalinout_set_value(MP_OBJ_TO_PTR(self->chip_select), self->cs_active_value);
+ }
+ return self->spi;
+}
+
+void common_hal_adafruit_bus_device_spidevice_exit(adafruit_bus_device_spidevice_obj_t *self) {
+ if (self->chip_select != MP_OBJ_NULL) {
+ common_hal_digitalio_digitalinout_set_value(MP_OBJ_TO_PTR(self->chip_select), !(self->cs_active_value));
+ }
+
+ if (self->extra_clocks > 0) {
+
+ mp_buffer_info_t bufinfo;
+ mp_obj_t buffer = mp_obj_new_bytearray_of_zeros(1);
+
+ mp_get_buffer_raise(buffer, &bufinfo, MP_BUFFER_WRITE);
+ ((uint8_t *)bufinfo.buf)[0] = 0xFF;
+
+ uint8_t clocks = (self->extra_clocks + 7) / 8;
+
+ mp_obj_t dest[3];
+ mp_load_method(self->spi, MP_QSTR_write, dest);
+ dest[2] = buffer;
+ while (clocks > 0) {
+ mp_call_method_n_kw(1, 0, dest);
+ clocks--;
+ }
+ }
+
+ mp_obj_t dest[2];
+ mp_load_method(self->spi, MP_QSTR_unlock, dest);
+ mp_call_method_n_kw(0, 0, dest);
+}
diff --git a/circuitpython/shared-module/adafruit_bus_device/spi_device/SPIDevice.h b/circuitpython/shared-module/adafruit_bus_device/spi_device/SPIDevice.h
new file mode 100644
index 0000000..7f577c6
--- /dev/null
+++ b/circuitpython/shared-module/adafruit_bus_device/spi_device/SPIDevice.h
@@ -0,0 +1,45 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2017 Scott Shawcroft 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.
+ */
+
+#ifndef MICROPY_INCLUDED_ATMEL_SAMD_SHARED_MODULE_BUSDEVICE_SPIDEVICE_H
+#define MICROPY_INCLUDED_ATMEL_SAMD_SHARED_MODULE_BUSDEVICE_SPIDEVICE_H
+
+#include "py/obj.h"
+#include "common-hal/busio/SPI.h"
+#include "common-hal/digitalio/DigitalInOut.h"
+
+typedef struct {
+ mp_obj_base_t base;
+ busio_spi_obj_t *spi;
+ uint32_t baudrate;
+ uint8_t polarity;
+ uint8_t phase;
+ uint8_t extra_clocks;
+ digitalio_digitalinout_obj_t *chip_select;
+ bool cs_active_value;
+} adafruit_bus_device_spidevice_obj_t;
+
+#endif // MICROPY_INCLUDED_ATMEL_SAMD_SHARED_MODULE_BUSDEVICE_SPIDEVICE_H
diff --git a/circuitpython/shared-module/adafruit_pixelbuf/PixelBuf.c b/circuitpython/shared-module/adafruit_pixelbuf/PixelBuf.c
new file mode 100644
index 0000000..99980c7
--- /dev/null
+++ b/circuitpython/shared-module/adafruit_pixelbuf/PixelBuf.c
@@ -0,0 +1,335 @@
+/*
+ * This file is part of the CircuitPython project, https://github.com/adafruit/circuitpython
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Rose Hooper
+ *
+ * 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.
+ */
+
+
+#include "py/obj.h"
+#include "py/objstr.h"
+#include "py/objtype.h"
+#include "py/runtime.h"
+#include "shared-bindings/adafruit_pixelbuf/PixelBuf.h"
+#include <string.h>
+#include <math.h>
+
+// Helper to ensure we have the native super class instead of a subclass.
+static pixelbuf_pixelbuf_obj_t *native_pixelbuf(mp_obj_t pixelbuf_obj) {
+ mp_obj_t native_pixelbuf = mp_obj_cast_to_native_base(pixelbuf_obj, &pixelbuf_pixelbuf_type);
+ mp_obj_assert_native_inited(native_pixelbuf);
+ return MP_OBJ_TO_PTR(native_pixelbuf);
+}
+
+void common_hal_adafruit_pixelbuf_pixelbuf_construct(pixelbuf_pixelbuf_obj_t *self, size_t n,
+ pixelbuf_byteorder_details_t *byteorder, mp_float_t brightness, bool auto_write,
+ uint8_t *header, size_t header_len, uint8_t *trailer, size_t trailer_len) {
+
+ self->pixel_count = n;
+ self->byteorder = *byteorder; // Copied because we modify for dotstar
+ self->bytes_per_pixel = byteorder->is_dotstar ? 4 : byteorder->bpp;
+ self->auto_write = false;
+
+ size_t pixel_len = self->pixel_count * self->bytes_per_pixel;
+ self->transmit_buffer_obj = mp_obj_new_bytes_of_zeros(header_len + pixel_len + trailer_len);
+ mp_obj_str_t *o = MP_OBJ_TO_PTR(self->transmit_buffer_obj);
+
+ // Abuse the bytes object a bit by mutating it's data by dropping the const. If the user's
+ // Python code holds onto it, they'll find out that it changes. At least this way it isn't
+ // mutable by the code itself.
+ uint8_t *transmit_buffer = (uint8_t *)o->data;
+ memcpy(transmit_buffer, header, header_len);
+ memcpy(transmit_buffer + header_len + pixel_len, trailer, trailer_len);
+ self->post_brightness_buffer = transmit_buffer + header_len;
+
+ if (self->byteorder.is_dotstar) {
+ // Initialize the buffer with the dotstar start bytes.
+ // Note: Header and end must be setup by caller
+ for (uint i = 0; i < self->pixel_count * 4; i += 4) {
+ self->post_brightness_buffer[i] = DOTSTAR_LED_START_FULL_BRIGHT;
+ }
+ }
+ // Call set_brightness so that it can allocate a second buffer if needed.
+ self->brightness = 1.0;
+ self->scaled_brightness = 0x100;
+ common_hal_adafruit_pixelbuf_pixelbuf_set_brightness(MP_OBJ_FROM_PTR(self), brightness);
+
+ // Turn on auto_write. We don't want to do it with the above brightness call.
+ self->auto_write = auto_write;
+}
+
+size_t common_hal_adafruit_pixelbuf_pixelbuf_get_len(mp_obj_t self_in) {
+ pixelbuf_pixelbuf_obj_t *self = native_pixelbuf(self_in);
+ return self->pixel_count;
+}
+
+uint8_t common_hal_adafruit_pixelbuf_pixelbuf_get_bpp(mp_obj_t self_in) {
+ pixelbuf_pixelbuf_obj_t *self = native_pixelbuf(self_in);
+ return self->byteorder.bpp;
+}
+
+mp_obj_t common_hal_adafruit_pixelbuf_pixelbuf_get_byteorder_string(mp_obj_t self_in) {
+ pixelbuf_pixelbuf_obj_t *self = native_pixelbuf(self_in);
+ return self->byteorder.order_string;
+}
+
+bool common_hal_adafruit_pixelbuf_pixelbuf_get_auto_write(mp_obj_t self_in) {
+ pixelbuf_pixelbuf_obj_t *self = native_pixelbuf(self_in);
+ return self->auto_write;
+}
+
+void common_hal_adafruit_pixelbuf_pixelbuf_set_auto_write(mp_obj_t self_in, bool auto_write) {
+ pixelbuf_pixelbuf_obj_t *self = native_pixelbuf(self_in);
+ self->auto_write = auto_write;
+}
+
+mp_float_t common_hal_adafruit_pixelbuf_pixelbuf_get_brightness(mp_obj_t self_in) {
+ pixelbuf_pixelbuf_obj_t *self = native_pixelbuf(self_in);
+ return self->brightness;
+}
+
+void common_hal_adafruit_pixelbuf_pixelbuf_set_brightness(mp_obj_t self_in, mp_float_t brightness) {
+ pixelbuf_pixelbuf_obj_t *self = native_pixelbuf(self_in);
+ // Skip out if the brightness is already set. The default of self->brightness is 1.0. So, this
+ // also prevents the pre_brightness_buffer allocation when brightness is set to 1.0 again.
+ self->brightness = brightness;
+ // Use 256 steps of brightness so that we can do integer math below.
+ uint16_t new_scaled_brightness = (uint16_t)(brightness * 256);
+ if (new_scaled_brightness == self->scaled_brightness) {
+ return;
+ }
+ self->scaled_brightness = new_scaled_brightness;
+ size_t pixel_len = self->pixel_count * self->bytes_per_pixel;
+ if (self->scaled_brightness == 0x100 && !self->pre_brightness_buffer) {
+ return;
+ } else {
+ if (self->pre_brightness_buffer == NULL) {
+ self->pre_brightness_buffer = m_malloc(pixel_len, false);
+ memcpy(self->pre_brightness_buffer, self->post_brightness_buffer, pixel_len);
+ }
+ for (size_t i = 0; i < pixel_len; i++) {
+ // Don't adjust per-pixel luminance bytes in dotstar mode
+ if (self->byteorder.is_dotstar && i % 4 == 0) {
+ continue;
+ }
+ self->post_brightness_buffer[i] = (self->pre_brightness_buffer[i] * self->scaled_brightness) / 256;
+ }
+
+ if (self->auto_write) {
+ common_hal_adafruit_pixelbuf_pixelbuf_show(self_in);
+ }
+ }
+}
+
+STATIC uint8_t _pixelbuf_get_as_uint8(mp_obj_t obj) {
+ if (mp_obj_is_small_int(obj)) {
+ return MP_OBJ_SMALL_INT_VALUE(obj);
+ } else if (mp_obj_is_int(obj)) {
+ return mp_obj_get_int_truncated(obj);
+ } else if (mp_obj_is_float(obj)) {
+ return (uint8_t)mp_obj_get_float(obj);
+ }
+ mp_raise_TypeError_varg(
+ translate("can't convert %q to %q"), mp_obj_get_type_qstr(obj), MP_QSTR_int);
+}
+
+STATIC void _pixelbuf_parse_color(pixelbuf_pixelbuf_obj_t *self, mp_obj_t color, uint8_t *r, uint8_t *g, uint8_t *b, uint8_t *w) {
+ pixelbuf_byteorder_details_t *byteorder = &self->byteorder;
+ // w is shared between white in NeoPixels and brightness in dotstars (so that DotStars can have
+ // per-pixel brightness). Set the defaults here in case it isn't set below.
+ if (byteorder->is_dotstar) {
+ *w = 255;
+ } else {
+ *w = 0;
+ }
+
+ if (mp_obj_is_int(color) || mp_obj_is_float(color)) {
+ mp_int_t value = mp_obj_is_int(color) ? mp_obj_get_int_truncated(color) : (mp_int_t)mp_obj_get_float(color);
+ *r = value >> 16 & 0xff;
+ *g = (value >> 8) & 0xff;
+ *b = value & 0xff;
+ } else {
+ mp_obj_t *items;
+ size_t len;
+ mp_obj_get_array(color, &len, &items);
+ if (len < 3 || len > 4) {
+ mp_raise_ValueError_varg(translate("Expected tuple of length %d, got %d"), byteorder->bpp, len);
+ }
+
+ *r = _pixelbuf_get_as_uint8(items[PIXEL_R]);
+ *g = _pixelbuf_get_as_uint8(items[PIXEL_G]);
+ *b = _pixelbuf_get_as_uint8(items[PIXEL_B]);
+ if (len > 3) {
+ if (mp_obj_is_float(items[PIXEL_W])) {
+ *w = 255 * mp_obj_get_float(items[PIXEL_W]);
+ } else {
+ *w = mp_obj_get_int_truncated(items[PIXEL_W]);
+ }
+ return;
+ }
+ }
+ // Int colors can't set white directly so convert to white when all components are equal.
+ // Also handles RGBW values assigned an RGB tuple.
+ if (!byteorder->is_dotstar && byteorder->bpp == 4 && byteorder->has_white && *r == *g && *r == *b) {
+ *w = *r;
+ *r = 0;
+ *g = 0;
+ *b = 0;
+ }
+}
+
+STATIC void _pixelbuf_set_pixel_color(pixelbuf_pixelbuf_obj_t *self, size_t index, uint8_t r, uint8_t g, uint8_t b, uint8_t w) {
+ // DotStars don't have white, instead they have 5 bit brightness so pack it into w. Shift right
+ // by three to leave the top five bits.
+ if (self->bytes_per_pixel == 4 && self->byteorder.is_dotstar) {
+ w = DOTSTAR_LED_START | w >> 3;
+ }
+ pixelbuf_rgbw_t *rgbw_order = &self->byteorder.byteorder;
+ size_t offset = index * self->bytes_per_pixel;
+ uint8_t *scaled_buffer, *unscaled_buffer;
+ if (self->pre_brightness_buffer) {
+ scaled_buffer = self->post_brightness_buffer + offset;
+ unscaled_buffer = self->pre_brightness_buffer + offset;
+ } else {
+ scaled_buffer = NULL;
+ unscaled_buffer = self->post_brightness_buffer + offset;
+ }
+
+ if (self->bytes_per_pixel == 4) {
+ unscaled_buffer[rgbw_order->w] = w;
+ }
+
+ unscaled_buffer[rgbw_order->r] = r;
+ unscaled_buffer[rgbw_order->g] = g;
+ unscaled_buffer[rgbw_order->b] = b;
+
+ if (scaled_buffer) {
+ if (self->bytes_per_pixel == 4) {
+ if (!self->byteorder.is_dotstar) {
+ w = (w * self->scaled_brightness) / 256;
+ }
+ scaled_buffer[rgbw_order->w] = w;
+ }
+ scaled_buffer[rgbw_order->r] = (r * self->scaled_brightness) / 256;
+ scaled_buffer[rgbw_order->g] = (g * self->scaled_brightness) / 256;
+ scaled_buffer[rgbw_order->b] = (b * self->scaled_brightness) / 256;
+ }
+}
+
+STATIC void _pixelbuf_set_pixel(pixelbuf_pixelbuf_obj_t *self, size_t index, mp_obj_t value) {
+ uint8_t r;
+ uint8_t g;
+ uint8_t b;
+ uint8_t w;
+ _pixelbuf_parse_color(self, value, &r, &g, &b, &w);
+ _pixelbuf_set_pixel_color(self, index, r, g, b, w);
+}
+
+void common_hal_adafruit_pixelbuf_pixelbuf_set_pixels(mp_obj_t self_in, size_t start, mp_int_t step, size_t slice_len, mp_obj_t *values,
+ mp_obj_tuple_t *flatten_to) {
+ pixelbuf_pixelbuf_obj_t *self = native_pixelbuf(self_in);
+ mp_obj_iter_buf_t iter_buf;
+ mp_obj_t iterable = mp_getiter(values, &iter_buf);
+ mp_obj_t item;
+ size_t i = 0;
+ bool flattened = flatten_to != mp_const_none;
+ if (flattened) {
+ flatten_to->len = self->bytes_per_pixel;
+ }
+ while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
+ if (flattened) {
+ flatten_to->items[i % self->bytes_per_pixel] = item;
+ if (++i % self->bytes_per_pixel == 0) {
+ _pixelbuf_set_pixel(self, start, flatten_to);
+ start += step;
+ }
+ } else {
+ _pixelbuf_set_pixel(self, start, item);
+ start += step;
+ }
+ }
+ if (self->auto_write) {
+ common_hal_adafruit_pixelbuf_pixelbuf_show(self_in);
+ }
+}
+
+
+
+void common_hal_adafruit_pixelbuf_pixelbuf_set_pixel(mp_obj_t self_in, size_t index, mp_obj_t value) {
+ pixelbuf_pixelbuf_obj_t *self = native_pixelbuf(self_in);
+ _pixelbuf_set_pixel(self, index, value);
+ if (self->auto_write) {
+ common_hal_adafruit_pixelbuf_pixelbuf_show(self_in);
+ }
+}
+
+mp_obj_t common_hal_adafruit_pixelbuf_pixelbuf_get_pixel(mp_obj_t self_in, size_t index) {
+ pixelbuf_pixelbuf_obj_t *self = native_pixelbuf(self_in);
+ mp_obj_t elems[self->byteorder.bpp];
+ uint8_t *pixel_buffer = self->post_brightness_buffer;
+ if (self->pre_brightness_buffer != NULL) {
+ pixel_buffer = self->pre_brightness_buffer;
+ }
+ pixel_buffer += self->byteorder.bpp * index;
+
+ pixelbuf_rgbw_t *rgbw_order = &self->byteorder.byteorder;
+ elems[0] = MP_OBJ_NEW_SMALL_INT(pixel_buffer[rgbw_order->r]);
+ elems[1] = MP_OBJ_NEW_SMALL_INT(pixel_buffer[rgbw_order->g]);
+ elems[2] = MP_OBJ_NEW_SMALL_INT(pixel_buffer[rgbw_order->b]);
+ if (self->byteorder.bpp > 3) {
+ uint8_t w = pixel_buffer[rgbw_order->w];
+ if (self->byteorder.is_dotstar) {
+ elems[3] = mp_obj_new_float((w & 0b00011111) / 31.0);
+ } else {
+ elems[3] = MP_OBJ_NEW_SMALL_INT(w);
+ }
+ }
+
+ return mp_obj_new_tuple(self->byteorder.bpp, elems);
+}
+
+void common_hal_adafruit_pixelbuf_pixelbuf_show(mp_obj_t self_in) {
+ pixelbuf_pixelbuf_obj_t *self = native_pixelbuf(self_in);
+ mp_obj_t dest[2 + 1];
+ mp_load_method(self_in, MP_QSTR__transmit, dest);
+
+ dest[2] = self->transmit_buffer_obj;
+
+ mp_call_method_n_kw(1, 0, dest);
+}
+
+void common_hal_adafruit_pixelbuf_pixelbuf_fill(mp_obj_t self_in, mp_obj_t fill_color) {
+ pixelbuf_pixelbuf_obj_t *self = native_pixelbuf(self_in);
+
+ uint8_t r;
+ uint8_t g;
+ uint8_t b;
+ uint8_t w;
+ _pixelbuf_parse_color(self, fill_color, &r, &g, &b, &w);
+
+ for (size_t i = 0; i < self->pixel_count; i++) {
+ _pixelbuf_set_pixel_color(self, i, r, g, b, w);
+ }
+ if (self->auto_write) {
+ common_hal_adafruit_pixelbuf_pixelbuf_show(self_in);
+ }
+}
diff --git a/circuitpython/shared-module/adafruit_pixelbuf/PixelBuf.h b/circuitpython/shared-module/adafruit_pixelbuf/PixelBuf.h
new file mode 100644
index 0000000..b526254
--- /dev/null
+++ b/circuitpython/shared-module/adafruit_pixelbuf/PixelBuf.h
@@ -0,0 +1,72 @@
+/*
+ * This file is part of the CircuitPython project, https://github.com/adafruit/circuitpython
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Rose Hooper
+ *
+ * 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.
+ */
+
+
+#include "py/obj.h"
+#include "py/objarray.h"
+
+#ifndef PIXELBUF_SHARED_MODULE_H
+#define PIXELBUF_SHARED_MODULE_H
+
+typedef struct {
+ uint8_t r;
+ uint8_t g;
+ uint8_t b;
+ uint8_t w;
+} pixelbuf_rgbw_t;
+
+typedef struct {
+ uint8_t bpp;
+ pixelbuf_rgbw_t byteorder;
+ bool has_white;
+ bool is_dotstar;
+ mp_obj_t order_string;
+} pixelbuf_byteorder_details_t;
+
+typedef struct {
+ mp_obj_base_t base;
+ size_t pixel_count;
+ uint16_t bytes_per_pixel;
+ uint16_t scaled_brightness;
+ pixelbuf_byteorder_details_t byteorder;
+ mp_float_t brightness;
+ mp_obj_t transmit_buffer_obj;
+ // The post_brightness_buffer is offset into the buffer allocated in transmit_buffer_obj to
+ // account for any header.
+ uint8_t *post_brightness_buffer;
+ uint8_t *pre_brightness_buffer;
+ bool auto_write;
+} pixelbuf_pixelbuf_obj_t;
+
+#define PIXEL_R 0
+#define PIXEL_G 1
+#define PIXEL_B 2
+#define PIXEL_W 3
+
+#define DOTSTAR_LED_START 0b11100000
+#define DOTSTAR_LED_START_FULL_BRIGHT 0xFF
+
+#endif
diff --git a/circuitpython/shared-module/adafruit_pixelbuf/__init__.c b/circuitpython/shared-module/adafruit_pixelbuf/__init__.c
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/circuitpython/shared-module/adafruit_pixelbuf/__init__.c
diff --git a/circuitpython/shared-module/aesio/__init__.c b/circuitpython/shared-module/aesio/__init__.c
new file mode 100644
index 0000000..bd748f9
--- /dev/null
+++ b/circuitpython/shared-module/aesio/__init__.c
@@ -0,0 +1,58 @@
+#include <string.h>
+
+#include "py/runtime.h"
+
+#include "shared-bindings/aesio/__init__.h"
+#include "shared-module/aesio/__init__.h"
+
+void common_hal_aesio_aes_construct(aesio_aes_obj_t *self, const uint8_t *key,
+ uint32_t key_length, const uint8_t *iv,
+ int mode, int counter) {
+ self->mode = mode;
+ self->counter = counter;
+ common_hal_aesio_aes_rekey(self, key, key_length, iv);
+}
+
+void common_hal_aesio_aes_rekey(aesio_aes_obj_t *self, const uint8_t *key,
+ uint32_t key_length, const uint8_t *iv) {
+ memset(&self->ctx, 0, sizeof(self->ctx));
+ if (iv != NULL) {
+ AES_init_ctx_iv(&self->ctx, key, key_length, iv);
+ } else {
+ AES_init_ctx(&self->ctx, key, key_length);
+ }
+}
+
+void common_hal_aesio_aes_set_mode(aesio_aes_obj_t *self, int mode) {
+ self->mode = mode;
+}
+
+void common_hal_aesio_aes_encrypt(aesio_aes_obj_t *self, uint8_t *buffer,
+ size_t length) {
+ switch (self->mode) {
+ case AES_MODE_ECB:
+ AES_ECB_encrypt(&self->ctx, buffer);
+ break;
+ case AES_MODE_CBC:
+ AES_CBC_encrypt_buffer(&self->ctx, buffer, length);
+ break;
+ case AES_MODE_CTR:
+ AES_CTR_xcrypt_buffer(&self->ctx, buffer, length);
+ break;
+ }
+}
+
+void common_hal_aesio_aes_decrypt(aesio_aes_obj_t *self, uint8_t *buffer,
+ size_t length) {
+ switch (self->mode) {
+ case AES_MODE_ECB:
+ AES_ECB_decrypt(&self->ctx, buffer);
+ break;
+ case AES_MODE_CBC:
+ AES_CBC_decrypt_buffer(&self->ctx, buffer, length);
+ break;
+ case AES_MODE_CTR:
+ AES_CTR_xcrypt_buffer(&self->ctx, buffer, length);
+ break;
+ }
+}
diff --git a/circuitpython/shared-module/aesio/__init__.h b/circuitpython/shared-module/aesio/__init__.h
new file mode 100644
index 0000000..1a0bb86
--- /dev/null
+++ b/circuitpython/shared-module/aesio/__init__.h
@@ -0,0 +1,59 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * 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.
+ */
+
+#ifndef MICROPY_INCLUDED_SHARED_MODULE_AESIO__INIT__H
+#define MICROPY_INCLUDED_SHARED_MODULE_AESIO__INIT__H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "py/obj.h"
+#include "py/proto.h"
+
+#include "shared-module/aesio/aes.h"
+
+// These values were chosen to correspond with the values
+// present in pycrypto.
+enum AES_MODE {
+ AES_MODE_ECB = 1,
+ AES_MODE_CBC = 2,
+ AES_MODE_CTR = 6,
+};
+
+typedef struct {
+ mp_obj_base_t base;
+
+ // The tinyaes context
+ struct AES_ctx ctx;
+
+ // Which AES mode this instance of the object is configured to use
+ enum AES_MODE mode;
+
+ // Counter for running in CTR mode
+ uint32_t counter;
+} aesio_aes_obj_t;
+
+#endif // MICROPY_INCLUDED_SHARED_MODULE_AESIO__INIT__H
diff --git a/circuitpython/shared-module/aesio/aes.c b/circuitpython/shared-module/aesio/aes.c
new file mode 100644
index 0000000..f417e76
--- /dev/null
+++ b/circuitpython/shared-module/aesio/aes.c
@@ -0,0 +1,610 @@
+/*
+
+This is an implementation of the AES algorithm, specifically ECB, CTR and CBC mode.
+Block size can be chosen in aes.h - available choices are AES128, AES192, AES256.
+
+The implementation is verified against the test vectors in:
+ National Institute of Standards and Technology Special Publication 800-38A 2001 ED
+
+ECB-AES128
+----------
+
+ plain-text:
+ 6bc1bee22e409f96e93d7e117393172a
+ ae2d8a571e03ac9c9eb76fac45af8e51
+ 30c81c46a35ce411e5fbc1191a0a52ef
+ f69f2445df4f9b17ad2b417be66c3710
+
+ key:
+ 2b7e151628aed2a6abf7158809cf4f3c
+
+ resulting cipher
+ 3ad77bb40d7a3660a89ecaf32466ef97
+ f5d3d58503b9699de785895a96fdbaaf
+ 43b1cd7f598ece23881b00e3ed030688
+ 7b0c785e27e8ad3f8223207104725dd4
+
+
+NOTE: String length must be evenly divisible by 16byte (str_len % 16 == 0)
+ You should pad the end of the string with zeros if this is not the case.
+ For AES192/256 the key size is proportionally larger.
+
+*/
+
+/*****************************************************************************/
+/* Includes: */
+/*****************************************************************************/
+#include <string.h> // CBC mode, for memset
+#include "aes.h"
+
+/*****************************************************************************/
+/* Defines: */
+/*****************************************************************************/
+// The number of columns comprising a state in AES. This is a constant in AES.
+// Value=4
+#define Nb 4UL
+
+#if defined(AES256) && (AES256 == 1)
+ #define Nk256 8UL
+ #define Nr256 14UL
+#endif
+#if defined(AES192) && (AES192 == 1)
+ #define Nk192 6UL
+ #define Nr192 12UL
+#endif
+#if defined(AES128) && (AES128 == 1)
+ #define Nk128 4UL // The number of 32 bit words in a key.
+ #define Nr128 10UL // The number of rounds in AES Cipher.
+#endif
+
+// jcallan@github points out that declaring Multiply as a function reduces code
+// size considerably with the Keil ARM compiler. See this link for more
+// information: https://github.com/kokke/tiny-AES-C/pull/3
+#ifndef MULTIPLY_AS_A_FUNCTION
+ #define MULTIPLY_AS_A_FUNCTION 0
+#endif
+
+
+
+
+/*****************************************************************************/
+/* Private variables: */
+/*****************************************************************************/
+// state - array holding the intermediate results during decryption.
+typedef uint8_t state_t[4][4];
+
+
+
+// The lookup-tables are marked const so they can be placed in read-only storage
+// instead of RAM The numbers below can be computed dynamically trading ROM for
+// RAM - This can be useful in (embedded) bootloader applications, where ROM is
+// often limited.
+static const uint8_t sbox[256] = {
+ // 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
+ 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
+ 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
+ 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
+ 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
+ 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
+ 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
+ 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
+ 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
+ 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
+ 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
+ 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
+ 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
+ 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
+ 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
+ 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16
+};
+
+static const uint8_t rsbox[256] = {
+ 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
+ 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
+ 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
+ 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
+ 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
+ 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
+ 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
+ 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
+ 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
+ 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
+ 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
+ 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
+ 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
+ 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
+ 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
+ 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d
+};
+
+// The round constant word array, Rcon[i], contains the values given by x to the
+// power (i-1) being powers of x (x is denoted as {02}) in the field GF(2^8)
+static const uint8_t Rcon[11] = {
+ 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36
+};
+
+/*
+ * Jordan Goulder points out in PR #12
+ * (https://github.com/kokke/tiny-AES-C/pull/12), that you can remove most of
+ * the elements in the Rcon array, because they are unused.
+ *
+ * From Wikipedia's article on the Rijndael key schedule @
+ * https://en.wikipedia.org/wiki/Rijndael_key_schedule#Rcon
+ *
+ * "Only the first some of these constants are actually used – up to rcon[10]
+ * for AES-128 (as 11 round keys are needed), up to rcon[8] for AES-192, up to
+ * rcon[7] for AES-256. rcon[0] is not used in AES algorithm."
+ */
+
+
+/*****************************************************************************/
+/* Private functions: */
+/*****************************************************************************/
+static const uint8_t *GetRoundKey(const struct AES_ctx *ctx) {
+ switch (ctx->KeyLength) {
+ #if defined(AES128) && (AES128 == 1)
+ case 16:
+ return ctx->RoundKey128;
+ #endif
+ #if defined(AES192) && (AES192 == 1)
+ case 24:
+ return ctx->RoundKey192;
+ #endif
+ #if defined(AES256) && (AES256 == 1)
+ case 32:
+ return ctx->RoundKey256;
+ #endif
+ }
+ return NULL;
+}
+
+
+/*
+static uint8_t getSBoxValue(uint8_t num)
+{
+ return sbox[num];
+}
+*/
+#define getSBoxValue(num) (sbox[(num)])
+/*
+static uint8_t getSBoxInvert(uint8_t num)
+{
+ return rsbox[num];
+}
+*/
+#define getSBoxInvert(num) (rsbox[(num)])
+
+// This function produces Nb(Nr+1) round keys. The round keys are used in each
+// round to decrypt the states.
+static void KeyExpansion(struct AES_ctx *ctx, const uint8_t *Key) {
+ uint8_t *RoundKey = (uint8_t *)GetRoundKey(ctx);
+
+ unsigned i, j, k;
+ uint8_t tempa[4]; // Used for the column/row operations
+
+ // The first round key is the key itself.
+ for (i = 0; i < ctx->Nk; ++i)
+ {
+ RoundKey[(i * 4) + 0] = Key[(i * 4) + 0];
+ RoundKey[(i * 4) + 1] = Key[(i * 4) + 1];
+ RoundKey[(i * 4) + 2] = Key[(i * 4) + 2];
+ RoundKey[(i * 4) + 3] = Key[(i * 4) + 3];
+ }
+
+ // All other round keys are found from the previous round keys.
+ for (i = ctx->Nk; i < Nb * (ctx->Nr + 1); ++i)
+ {
+ {
+ k = (i - 1) * 4;
+ tempa[0] = RoundKey[k + 0];
+ tempa[1] = RoundKey[k + 1];
+ tempa[2] = RoundKey[k + 2];
+ tempa[3] = RoundKey[k + 3];
+
+ }
+
+ if (i % ctx->Nk == 0) {
+ // This function shifts the 4 bytes in a word to the left once.
+ // [a0,a1,a2,a3] becomes [a1,a2,a3,a0]
+
+ // Function RotWord()
+ {
+ const uint8_t u8tmp = tempa[0];
+ tempa[0] = tempa[1];
+ tempa[1] = tempa[2];
+ tempa[2] = tempa[3];
+ tempa[3] = u8tmp;
+ }
+
+ // SubWord() is a function that takes a four-byte input word and applies
+ // the S-box to each of the four bytes to produce an output word.
+
+ // Function Subword()
+ {
+ tempa[0] = getSBoxValue(tempa[0]);
+ tempa[1] = getSBoxValue(tempa[1]);
+ tempa[2] = getSBoxValue(tempa[2]);
+ tempa[3] = getSBoxValue(tempa[3]);
+ }
+
+ tempa[0] = tempa[0] ^ Rcon[i / ctx->Nk];
+ }
+ #if defined(AES256) && (AES256 == 1)
+ if (ctx->KeyLength == 32) {
+ if (i % ctx->Nk == 4) {
+ // Function Subword()
+ {
+ tempa[0] = getSBoxValue(tempa[0]);
+ tempa[1] = getSBoxValue(tempa[1]);
+ tempa[2] = getSBoxValue(tempa[2]);
+ tempa[3] = getSBoxValue(tempa[3]);
+ }
+ }
+ }
+ #endif
+ j = i * 4;
+ k = (i - ctx->Nk) * 4;
+ RoundKey[j + 0] = RoundKey[k + 0] ^ tempa[0];
+ RoundKey[j + 1] = RoundKey[k + 1] ^ tempa[1];
+ RoundKey[j + 2] = RoundKey[k + 2] ^ tempa[2];
+ RoundKey[j + 3] = RoundKey[k + 3] ^ tempa[3];
+ }
+}
+
+void AES_init_ctx(struct AES_ctx *ctx, const uint8_t *key, uint32_t keylen) {
+ ctx->KeyLength = keylen;
+ switch (ctx->KeyLength) {
+ #if defined(AES128) && (AES128 == 1)
+ case 16:
+ ctx->Nr = Nr128;
+ ctx->Nk = Nk128;
+ break;
+ #endif
+ #if defined(AES192) && (AES192 == 1)
+ case 24:
+ ctx->Nr = Nr192;
+ ctx->Nk = Nk192;
+ break;
+ #endif
+ #if defined(AES256) && (AES256 == 1)
+ case 32:
+ ctx->Nr = Nr256;
+ ctx->Nk = Nk256;
+ break;
+ #endif
+ default:
+ ctx->Nr = 0;
+ ctx->Nk = 0;
+ break;
+ }
+ KeyExpansion(ctx, key);
+}
+#if (defined(CBC) && (CBC == 1)) || (defined(CTR) && (CTR == 1))
+void AES_init_ctx_iv(struct AES_ctx *ctx, const uint8_t *key, uint32_t keylen, const uint8_t *iv) {
+ AES_init_ctx(ctx, key, keylen);
+ memcpy(ctx->Iv, iv, AES_BLOCKLEN);
+}
+void AES_ctx_set_iv(struct AES_ctx *ctx, const uint8_t *iv) {
+ memcpy(ctx->Iv, iv, AES_BLOCKLEN);
+}
+#endif
+
+// This function adds the round key to state. The round key is added to the
+// state by an XOR function.
+static void AddRoundKey(uint8_t round, state_t *state, const uint8_t *RoundKey) {
+ uint8_t i,j;
+ for (i = 0; i < 4; ++i)
+ {
+ for (j = 0; j < 4; ++j)
+ {
+ (*state)[i][j] ^= RoundKey[(round * Nb * 4) + (i * Nb) + j];
+ }
+ }
+}
+
+// The SubBytes Function Substitutes the values in the state matrix with values
+// in an S-box.
+static void SubBytes(state_t *state) {
+ uint8_t i, j;
+ for (i = 0; i < 4; ++i)
+ {
+ for (j = 0; j < 4; ++j)
+ {
+ (*state)[j][i] = getSBoxValue((*state)[j][i]);
+ }
+ }
+}
+
+// The ShiftRows() function shifts the rows in the state to the left. Each row
+// is shifted with different offset. Offset = Row number. So the first row is
+// not shifted.
+static void ShiftRows(state_t *state) {
+ uint8_t temp;
+
+ // Rotate first row 1 columns to left
+ temp = (*state)[0][1];
+ (*state)[0][1] = (*state)[1][1];
+ (*state)[1][1] = (*state)[2][1];
+ (*state)[2][1] = (*state)[3][1];
+ (*state)[3][1] = temp;
+
+ // Rotate second row 2 columns to left
+ temp = (*state)[0][2];
+ (*state)[0][2] = (*state)[2][2];
+ (*state)[2][2] = temp;
+
+ temp = (*state)[1][2];
+ (*state)[1][2] = (*state)[3][2];
+ (*state)[3][2] = temp;
+
+ // Rotate third row 3 columns to left
+ temp = (*state)[0][3];
+ (*state)[0][3] = (*state)[3][3];
+ (*state)[3][3] = (*state)[2][3];
+ (*state)[2][3] = (*state)[1][3];
+ (*state)[1][3] = temp;
+}
+
+static uint8_t xtime(uint8_t x) {
+ return (x << 1) ^ (((x >> 7) & 1) * 0x1b);
+}
+
+// MixColumns function mixes the columns of the state matrix
+static void MixColumns(state_t *state) {
+ uint8_t i;
+ uint8_t Tmp, Tm, t;
+ for (i = 0; i < 4; ++i)
+ {
+ t = (*state)[i][0];
+ Tmp = (*state)[i][0] ^ (*state)[i][1] ^ (*state)[i][2] ^ (*state)[i][3];
+ Tm = (*state)[i][0] ^ (*state)[i][1];
+ Tm = xtime(Tm);
+ (*state)[i][0] ^= Tm ^ Tmp;
+ Tm = (*state)[i][1] ^ (*state)[i][2];
+ Tm = xtime(Tm);
+ (*state)[i][1] ^= Tm ^ Tmp;
+ Tm = (*state)[i][2] ^ (*state)[i][3];
+ Tm = xtime(Tm);
+ (*state)[i][2] ^= Tm ^ Tmp;
+ Tm = (*state)[i][3] ^ t;
+ Tm = xtime(Tm);
+ (*state)[i][3] ^= Tm ^ Tmp;
+ }
+}
+
+// Multiply is used to multiply numbers in the field GF(2^8)
+// Note: The last call to xtime() is unneeded, but often ends up generating a smaller binary
+// The compiler seems to be able to vectorize the operation better this way.
+// See https://github.com/kokke/tiny-AES-c/pull/34
+#if MULTIPLY_AS_A_FUNCTION
+static uint8_t Multiply(uint8_t x, uint8_t y) {
+ return ((y & 1) * x) ^
+ ((y >> 1 & 1) * xtime(x)) ^
+ ((y >> 2 & 1) * xtime(xtime(x))) ^
+ ((y >> 3 & 1) * xtime(xtime(xtime(x)))) ^
+ ((y >> 4 & 1) * xtime(xtime(xtime(xtime(x))))); /* this last call to xtime() can be omitted */
+}
+#else
+#define Multiply(x, y) \
+ (((y & 1) * x) ^ \
+ ((y >> 1 & 1) * xtime(x)) ^ \
+ ((y >> 2 & 1) * xtime(xtime(x))) ^ \
+ ((y >> 3 & 1) * xtime(xtime(xtime(x)))) ^ \
+ ((y >> 4 & 1) * xtime(xtime(xtime(xtime(x)))))) \
+
+#endif
+
+#if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1)
+// MixColumns function mixes the columns of the state matrix. The method used to
+// multiply may be difficult to understand for the inexperienced. Please use the
+// references to gain more information.
+static void InvMixColumns(state_t *state) {
+ int i;
+ uint8_t a, b, c, d;
+ for (i = 0; i < 4; ++i)
+ {
+ a = (*state)[i][0];
+ b = (*state)[i][1];
+ c = (*state)[i][2];
+ d = (*state)[i][3];
+
+ (*state)[i][0] = Multiply(a, 0x0e) ^ Multiply(b, 0x0b) ^ Multiply(c, 0x0d) ^ Multiply(d, 0x09);
+ (*state)[i][1] = Multiply(a, 0x09) ^ Multiply(b, 0x0e) ^ Multiply(c, 0x0b) ^ Multiply(d, 0x0d);
+ (*state)[i][2] = Multiply(a, 0x0d) ^ Multiply(b, 0x09) ^ Multiply(c, 0x0e) ^ Multiply(d, 0x0b);
+ (*state)[i][3] = Multiply(a, 0x0b) ^ Multiply(b, 0x0d) ^ Multiply(c, 0x09) ^ Multiply(d, 0x0e);
+ }
+}
+
+
+// The SubBytes Function Substitutes the values in the state matrix with values
+// in an S-box.
+static void InvSubBytes(state_t *state) {
+ uint8_t i, j;
+ for (i = 0; i < 4; ++i)
+ {
+ for (j = 0; j < 4; ++j)
+ {
+ (*state)[j][i] = getSBoxInvert((*state)[j][i]);
+ }
+ }
+}
+
+static void InvShiftRows(state_t *state) {
+ uint8_t temp;
+
+ // Rotate first row 1 columns to right
+ temp = (*state)[3][1];
+ (*state)[3][1] = (*state)[2][1];
+ (*state)[2][1] = (*state)[1][1];
+ (*state)[1][1] = (*state)[0][1];
+ (*state)[0][1] = temp;
+
+ // Rotate second row 2 columns to right
+ temp = (*state)[0][2];
+ (*state)[0][2] = (*state)[2][2];
+ (*state)[2][2] = temp;
+
+ temp = (*state)[1][2];
+ (*state)[1][2] = (*state)[3][2];
+ (*state)[3][2] = temp;
+
+ // Rotate third row 3 columns to right
+ temp = (*state)[0][3];
+ (*state)[0][3] = (*state)[1][3];
+ (*state)[1][3] = (*state)[2][3];
+ (*state)[2][3] = (*state)[3][3];
+ (*state)[3][3] = temp;
+}
+#endif // #if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1)
+
+// Cipher is the main function that encrypts the PlainText.
+static void Cipher(state_t *state, const struct AES_ctx *ctx) {
+ const uint8_t *RoundKey = GetRoundKey(ctx);
+ uint8_t round = 0;
+
+ // Add the First round key to the state before starting the rounds.
+ AddRoundKey(0, state, RoundKey);
+
+ // There will be Nr rounds. The first Nr-1 rounds are identical. These Nr
+ // rounds are executed in the loop below. Last one without MixColumns()
+ for (round = 1; ; ++round)
+ {
+ SubBytes(state);
+ ShiftRows(state);
+ if (round == ctx->Nr) {
+ break;
+ }
+ MixColumns(state);
+ AddRoundKey(round, state, RoundKey);
+ }
+ // Add round key to last round
+ AddRoundKey(ctx->Nr, state, RoundKey);
+}
+
+#if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1)
+static void InvCipher(state_t *state, const struct AES_ctx *ctx) {
+ const uint8_t *RoundKey = GetRoundKey(ctx);
+ uint8_t round = 0;
+
+ // Add the First round key to the state before starting the rounds.
+ AddRoundKey(ctx->Nr, state, RoundKey);
+
+ // There will be Nr rounds. The first Nr-1 rounds are identical. These Nr
+ // rounds are executed in the loop below. Last one without InvMixColumn()
+ for (round = (ctx->Nr - 1); ; --round)
+ {
+ InvShiftRows(state);
+ InvSubBytes(state);
+ AddRoundKey(round, state, RoundKey);
+ if (round == 0) {
+ break;
+ }
+ InvMixColumns(state);
+ }
+
+}
+#endif // #if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1)
+
+/*****************************************************************************/
+/* Public functions: */
+/*****************************************************************************/
+#if defined(ECB) && (ECB == 1)
+
+
+void AES_ECB_encrypt(const struct AES_ctx *ctx, uint8_t *buf) {
+ // The next function call encrypts the PlainText with the Key using AES
+ // algorithm.
+ Cipher((state_t *)buf, ctx);
+}
+
+void AES_ECB_decrypt(const struct AES_ctx *ctx, uint8_t *buf) {
+ // The next function call decrypts the PlainText with the Key using AES
+ // algorithm.
+ InvCipher((state_t *)buf, ctx);
+}
+
+
+#endif // #if defined(ECB) && (ECB == 1)
+
+
+
+
+
+#if defined(CBC) && (CBC == 1)
+
+
+static void XorWithIv(uint8_t *buf, const uint8_t *Iv) {
+ uint8_t i;
+ for (i = 0; i < AES_BLOCKLEN; ++i) // The block in AES is always 128bit no matter the key size
+ {
+ buf[i] ^= Iv[i];
+ }
+}
+
+void AES_CBC_encrypt_buffer(struct AES_ctx *ctx, uint8_t *buf, uint32_t length) {
+ uintptr_t i;
+ uint8_t *Iv = ctx->Iv;
+ for (i = 0; i < length; i += AES_BLOCKLEN)
+ {
+ XorWithIv(buf, Iv);
+ Cipher((state_t *)buf, ctx);
+ Iv = buf;
+ buf += AES_BLOCKLEN;
+ }
+ /* store Iv in ctx for next call */
+ memcpy(ctx->Iv, Iv, AES_BLOCKLEN);
+}
+
+void AES_CBC_decrypt_buffer(struct AES_ctx *ctx, uint8_t *buf, uint32_t length) {
+ uintptr_t i;
+ uint8_t storeNextIv[AES_BLOCKLEN];
+ for (i = 0; i < length; i += AES_BLOCKLEN)
+ {
+ memcpy(storeNextIv, buf, AES_BLOCKLEN);
+ InvCipher((state_t *)buf, ctx);
+ XorWithIv(buf, ctx->Iv);
+ memcpy(ctx->Iv, storeNextIv, AES_BLOCKLEN);
+ buf += AES_BLOCKLEN;
+ }
+
+}
+
+#endif // #if defined(CBC) && (CBC == 1)
+
+
+
+#if defined(CTR) && (CTR == 1)
+
+/* Symmetrical operation: same function for encrypting as for decrypting. Note
+any IV/nonce should never be reused with the same key */
+void AES_CTR_xcrypt_buffer(struct AES_ctx *ctx, uint8_t *buf, uint32_t length) {
+ uint8_t buffer[AES_BLOCKLEN];
+
+ unsigned i;
+ int bi;
+ for (i = 0, bi = AES_BLOCKLEN; i < length; ++i, ++bi)
+ {
+ if (bi == AES_BLOCKLEN) { /* we need to regen xor compliment in buffer */
+ memcpy(buffer, ctx->Iv, AES_BLOCKLEN);
+ Cipher((state_t *)buffer, ctx);
+
+ /* Increment Iv and handle overflow */
+ for (bi = (AES_BLOCKLEN - 1); bi >= 0; --bi)
+ {
+ /* inc will overflow */
+ if (ctx->Iv[bi] == 255) {
+ ctx->Iv[bi] = 0;
+ continue;
+ }
+ ctx->Iv[bi] += 1;
+ break;
+ }
+ bi = 0;
+ }
+
+ buf[i] = (buf[i] ^ buffer[bi]);
+ }
+}
+
+#endif // #if defined(CTR) && (CTR == 1)
diff --git a/circuitpython/shared-module/aesio/aes.h b/circuitpython/shared-module/aesio/aes.h
new file mode 100644
index 0000000..92539f1
--- /dev/null
+++ b/circuitpython/shared-module/aesio/aes.h
@@ -0,0 +1,105 @@
+#ifndef _AES_H_
+#define _AES_H_
+
+#include <stdint.h>
+
+// #define the macros below to 1/0 to enable/disable the mode of operation.
+//
+// CBC enables AES encryption in CBC-mode of operation.
+// CTR enables encryption in counter-mode.
+// ECB enables the basic ECB 16-byte block algorithm. All can be enabled simultaneously.
+
+// The #ifndef-guard allows it to be configured before #include'ing or at compile time.
+#ifndef CBC
+ #define CBC 1
+#endif
+
+#ifndef ECB
+ #define ECB 1
+#endif
+
+#ifndef CTR
+ #define CTR 1
+#endif
+
+
+#define AES128 1
+#define AES192 1
+#define AES256 1
+
+#define AES_BLOCKLEN 16 // Block length in bytes - AES is 128b block only
+
+#if defined(AES256) && (AES256 == 1)
+ #define AES_KEYLEN256 32
+ #define AES_keyExpSize256 240
+#endif
+#if defined(AES192) && (AES192 == 1)
+ #define AES_KEYLEN192 24
+ #define AES_keyExpSize192 208
+#endif
+#if defined(AES128) && (AES128 == 1)
+ #define AES_KEYLEN128 16 // Key length in bytes
+ #define AES_keyExpSize128 176
+#endif
+
+struct AES_ctx
+{
+ union {
+ #if defined(AES256) && (AES256 == 1)
+ uint8_t RoundKey256[AES_keyExpSize256];
+ #endif
+ #if defined(AES192) && (AES192 == 1)
+ uint8_t RoundKey192[AES_keyExpSize192];
+ #endif
+ #if defined(AES128) && (AES128 == 1)
+ uint8_t RoundKey128[AES_keyExpSize128];
+ #endif
+ };
+ #if (defined(CBC) && (CBC == 1)) || (defined(CTR) && (CTR == 1))
+ uint8_t Iv[AES_BLOCKLEN];
+ #endif
+ uint32_t KeyLength;
+ uint8_t Nr;
+ uint8_t Nk;
+};
+
+void AES_init_ctx(struct AES_ctx *ctx, const uint8_t *key, uint32_t keylen);
+#if (defined(CBC) && (CBC == 1)) || (defined(CTR) && (CTR == 1))
+void AES_init_ctx_iv(struct AES_ctx *ctx, const uint8_t *key, uint32_t keylen, const uint8_t *iv);
+void AES_ctx_set_iv(struct AES_ctx *ctx, const uint8_t *iv);
+#endif
+
+#if defined(ECB) && (ECB == 1)
+// buffer size is exactly AES_BLOCKLEN bytes;
+// you need only AES_init_ctx as IV is not used in ECB
+// NB: ECB is considered insecure for most uses
+void AES_ECB_encrypt(const struct AES_ctx *ctx, uint8_t *buf);
+void AES_ECB_decrypt(const struct AES_ctx *ctx, uint8_t *buf);
+
+#endif // #if defined(ECB) && (ECB == !)
+
+
+#if defined(CBC) && (CBC == 1)
+// buffer size MUST be mutile of AES_BLOCKLEN;
+// Suggest https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7 for padding scheme
+// NOTES: you need to set IV in ctx via AES_init_ctx_iv() or AES_ctx_set_iv()
+// no IV should ever be reused with the same key
+void AES_CBC_encrypt_buffer(struct AES_ctx *ctx, uint8_t *buf, uint32_t length);
+void AES_CBC_decrypt_buffer(struct AES_ctx *ctx, uint8_t *buf, uint32_t length);
+
+#endif // #if defined(CBC) && (CBC == 1)
+
+
+#if defined(CTR) && (CTR == 1)
+
+// Same function for encrypting as for decrypting.
+// IV is incremented for every block, and used after encryption as XOR-compliment for output
+// Suggesting https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7 for padding scheme
+// NOTES: you need to set IV in ctx with AES_init_ctx_iv() or AES_ctx_set_iv()
+// no IV should ever be reused with the same key
+void AES_CTR_xcrypt_buffer(struct AES_ctx *ctx, uint8_t *buf, uint32_t length);
+
+#endif // #if defined(CTR) && (CTR == 1)
+
+
+#endif // _AES_H_
diff --git a/circuitpython/shared-module/atexit/__init__.c b/circuitpython/shared-module/atexit/__init__.c
new file mode 100644
index 0000000..56271bb
--- /dev/null
+++ b/circuitpython/shared-module/atexit/__init__.c
@@ -0,0 +1,92 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 microDev
+ *
+ * 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.
+ */
+
+#include "py/gc.h"
+#include "py/runtime.h"
+#include "shared-module/atexit/__init__.h"
+
+static size_t callback_len = 0;
+static atexit_callback_t *callback = NULL;
+
+void atexit_reset(void) {
+ callback_len = 0;
+ m_free(callback);
+ callback = NULL;
+}
+
+void atexit_gc_collect(void) {
+ gc_collect_ptr(callback);
+}
+
+void shared_module_atexit_register(mp_obj_t *func, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+ if (!mp_obj_is_callable(func)) {
+ mp_raise_TypeError_varg(translate("'%q' object is not callable"), mp_obj_get_type_qstr(func));
+ }
+ size_t n_kw_args = (kw_args) ? kw_args->used : 0;
+ atexit_callback_t cb = {
+ .n_pos = 0,
+ .n_kw = 0,
+ .func = func,
+ .args = (n_args + n_kw_args) ? m_malloc((n_args + (n_kw_args * 2)) * sizeof(mp_obj_t), false) : NULL
+ };
+ for (; cb.n_pos < n_args; cb.n_pos++) {
+ cb.args[cb.n_pos] = pos_args[cb.n_pos];
+ }
+ for (size_t i = cb.n_pos; cb.n_kw < n_kw_args; i++, cb.n_kw++) {
+ cb.args[i] = kw_args->table[cb.n_kw].key;
+ cb.args[i += 1] = kw_args->table[cb.n_kw].value;
+ }
+ callback = (atexit_callback_t *)m_realloc(callback, (callback_len + 1) * sizeof(cb));
+ callback[callback_len++] = cb;
+}
+
+void shared_module_atexit_unregister(const mp_obj_t *func) {
+ for (size_t i = 0; i < callback_len; i++) {
+ if (callback[i].func == *func) {
+ callback[i].n_pos = 0;
+ callback[i].n_kw = 0;
+ callback[i].func = mp_const_none;
+ callback[i].args = NULL;
+ }
+ }
+}
+
+void shared_module_atexit_execute(pyexec_result_t *result) {
+ if (callback) {
+ for (size_t i = callback_len; i-- > 0;) {
+ if (callback[i].func != mp_const_none) {
+ if (result != NULL) {
+ pyexec_result_t res;
+ if (pyexec_exit_handler(&callback[i], &res) == PYEXEC_DEEP_SLEEP) {
+ *result = res;
+ }
+ } else {
+ pyexec_exit_handler(&callback[i], NULL);
+ }
+ }
+ }
+ }
+}
diff --git a/circuitpython/shared-module/atexit/__init__.h b/circuitpython/shared-module/atexit/__init__.h
new file mode 100644
index 0000000..d88d066
--- /dev/null
+++ b/circuitpython/shared-module/atexit/__init__.h
@@ -0,0 +1,46 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 microDev
+ *
+ * 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.
+ */
+
+#ifndef MICROPY_INCLUDED_SHARED_MODULE_ATEXIT___INIT___H
+#define MICROPY_INCLUDED_SHARED_MODULE_ATEXIT___INIT___H
+
+#include "py/obj.h"
+#include "shared/runtime/pyexec.h"
+
+typedef struct _atexit_callback_t {
+ size_t n_pos, n_kw;
+ mp_obj_t func, *args;
+} atexit_callback_t;
+
+extern void atexit_reset(void);
+extern void atexit_gc_collect(void);
+
+extern void shared_module_atexit_register(mp_obj_t *func,
+ size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
+extern void shared_module_atexit_unregister(const mp_obj_t *func);
+extern void shared_module_atexit_execute(pyexec_result_t *result);
+
+#endif // MICROPY_INCLUDED_SHARED_MODULE_ATEXIT___INIT___H
diff --git a/circuitpython/shared-module/audiocore/RawSample.c b/circuitpython/shared-module/audiocore/RawSample.c
new file mode 100644
index 0000000..e7d765e
--- /dev/null
+++ b/circuitpython/shared-module/audiocore/RawSample.c
@@ -0,0 +1,99 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Scott Shawcroft 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.
+ */
+
+#include "shared-bindings/audiocore/RawSample.h"
+
+#include <stdint.h>
+
+#include "shared-module/audiocore/RawSample.h"
+
+void common_hal_audioio_rawsample_construct(audioio_rawsample_obj_t *self,
+ uint8_t *buffer,
+ uint32_t len,
+ uint8_t bytes_per_sample,
+ bool samples_signed,
+ uint8_t channel_count,
+ uint32_t sample_rate) {
+ self->buffer = buffer;
+ self->bits_per_sample = bytes_per_sample * 8;
+ self->samples_signed = samples_signed;
+ self->len = len;
+ self->channel_count = channel_count;
+ self->sample_rate = sample_rate;
+}
+
+void common_hal_audioio_rawsample_deinit(audioio_rawsample_obj_t *self) {
+ self->buffer = NULL;
+}
+bool common_hal_audioio_rawsample_deinited(audioio_rawsample_obj_t *self) {
+ return self->buffer == NULL;
+}
+
+uint32_t common_hal_audioio_rawsample_get_sample_rate(audioio_rawsample_obj_t *self) {
+ return self->sample_rate;
+}
+void common_hal_audioio_rawsample_set_sample_rate(audioio_rawsample_obj_t *self,
+ uint32_t sample_rate) {
+ self->sample_rate = sample_rate;
+}
+uint8_t common_hal_audioio_rawsample_get_bits_per_sample(audioio_rawsample_obj_t *self) {
+ return self->bits_per_sample;
+}
+uint8_t common_hal_audioio_rawsample_get_channel_count(audioio_rawsample_obj_t *self) {
+ return self->channel_count;
+}
+
+void audioio_rawsample_reset_buffer(audioio_rawsample_obj_t *self,
+ bool single_channel_output,
+ uint8_t channel) {
+}
+
+audioio_get_buffer_result_t audioio_rawsample_get_buffer(audioio_rawsample_obj_t *self,
+ bool single_channel_output,
+ uint8_t channel,
+ uint8_t **buffer,
+ uint32_t *buffer_length) {
+ *buffer_length = self->len;
+ if (single_channel_output) {
+ *buffer = self->buffer + (channel % self->channel_count) * (self->bits_per_sample / 8);
+ } else {
+ *buffer = self->buffer;
+ }
+ return GET_BUFFER_DONE;
+}
+
+void audioio_rawsample_get_buffer_structure(audioio_rawsample_obj_t *self, bool single_channel_output,
+ bool *single_buffer, bool *samples_signed,
+ uint32_t *max_buffer_length, uint8_t *spacing) {
+ *single_buffer = true;
+ *samples_signed = self->samples_signed;
+ *max_buffer_length = self->len;
+ if (single_channel_output) {
+ *spacing = self->channel_count;
+ } else {
+ *spacing = 1;
+ }
+}
diff --git a/circuitpython/shared-module/audiocore/RawSample.h b/circuitpython/shared-module/audiocore/RawSample.h
new file mode 100644
index 0000000..10d395b
--- /dev/null
+++ b/circuitpython/shared-module/audiocore/RawSample.h
@@ -0,0 +1,58 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Scott Shawcroft
+ *
+ * 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.
+ */
+
+#ifndef MICROPY_INCLUDED_SHARED_MODULE_AUDIOIO_RAWSAMPLE_H
+#define MICROPY_INCLUDED_SHARED_MODULE_AUDIOIO_RAWSAMPLE_H
+
+#include "py/obj.h"
+
+#include "shared-module/audiocore/__init__.h"
+
+typedef struct {
+ mp_obj_base_t base;
+ uint8_t *buffer;
+ uint32_t len;
+ uint8_t bits_per_sample;
+ bool samples_signed;
+ uint8_t channel_count;
+ uint32_t sample_rate;
+} audioio_rawsample_obj_t;
+
+
+// These are not available from Python because it may be called in an interrupt.
+void audioio_rawsample_reset_buffer(audioio_rawsample_obj_t *self,
+ bool single_channel_output,
+ uint8_t channel);
+audioio_get_buffer_result_t audioio_rawsample_get_buffer(audioio_rawsample_obj_t *self,
+ bool single_channel_output,
+ uint8_t channel,
+ uint8_t **buffer,
+ uint32_t *buffer_length); // length in bytes
+void audioio_rawsample_get_buffer_structure(audioio_rawsample_obj_t *self, bool single_channel_output,
+ bool *single_buffer, bool *samples_signed,
+ uint32_t *max_buffer_length, uint8_t *spacing);
+
+#endif // MICROPY_INCLUDED_SHARED_MODULE_AUDIOIO_RAWSAMPLE_H
diff --git a/circuitpython/shared-module/audiocore/WaveFile.c b/circuitpython/shared-module/audiocore/WaveFile.c
new file mode 100644
index 0000000..0cceb97
--- /dev/null
+++ b/circuitpython/shared-module/audiocore/WaveFile.c
@@ -0,0 +1,272 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Scott Shawcroft 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.
+ */
+
+#include "shared-bindings/audiocore/WaveFile.h"
+
+#include <stdint.h>
+#include <string.h>
+
+#include "py/mperrno.h"
+#include "py/runtime.h"
+
+#include "shared-module/audiocore/WaveFile.h"
+#include "supervisor/shared/translate.h"
+
+struct wave_format_chunk {
+ uint16_t audio_format;
+ uint16_t num_channels;
+ uint32_t sample_rate;
+ uint32_t byte_rate;
+ uint16_t block_align;
+ uint16_t bits_per_sample;
+ uint16_t extra_params; // Assumed to be zero below.
+};
+
+void common_hal_audioio_wavefile_construct(audioio_wavefile_obj_t *self,
+ pyb_file_obj_t *file,
+ uint8_t *buffer,
+ size_t buffer_size) {
+ // Load the wave
+ self->file = file;
+ uint8_t chunk_header[16];
+ f_rewind(&self->file->fp);
+ UINT bytes_read;
+ if (f_read(&self->file->fp, chunk_header, 16, &bytes_read) != FR_OK) {
+ mp_raise_OSError(MP_EIO);
+ }
+ if (bytes_read != 16 ||
+ memcmp(chunk_header, "RIFF", 4) != 0 ||
+ memcmp(chunk_header + 8, "WAVEfmt ", 8) != 0) {
+ mp_raise_ValueError(translate("Invalid wave file"));
+ }
+ uint32_t format_size;
+ if (f_read(&self->file->fp, &format_size, 4, &bytes_read) != FR_OK) {
+ mp_raise_OSError(MP_EIO);
+ }
+ if (bytes_read != 4 ||
+ format_size > sizeof(struct wave_format_chunk)) {
+ mp_raise_ValueError(translate("Invalid format chunk size"));
+ }
+ struct wave_format_chunk format;
+ if (f_read(&self->file->fp, &format, format_size, &bytes_read) != FR_OK) {
+ mp_raise_OSError(MP_EIO);
+ }
+ if (bytes_read != format_size) {
+ }
+
+ if (format.audio_format != 1 ||
+ format.num_channels > 2 ||
+ format.bits_per_sample > 16 ||
+ (format_size == 18 &&
+ format.extra_params != 0)) {
+ mp_raise_ValueError(translate("Unsupported format"));
+ }
+ // Get the sample_rate
+ self->sample_rate = format.sample_rate;
+ self->channel_count = format.num_channels;
+ self->bits_per_sample = format.bits_per_sample;
+
+ // TODO(tannewt): Skip any extra chunks that occur before the data section.
+
+ uint8_t data_tag[4];
+ if (f_read(&self->file->fp, &data_tag, 4, &bytes_read) != FR_OK) {
+ mp_raise_OSError(MP_EIO);
+ }
+ if (bytes_read != 4 ||
+ memcmp((uint8_t *)data_tag, "data", 4) != 0) {
+ mp_raise_ValueError(translate("Data chunk must follow fmt chunk"));
+ }
+
+ uint32_t data_length;
+ if (f_read(&self->file->fp, &data_length, 4, &bytes_read) != FR_OK) {
+ mp_raise_OSError(MP_EIO);
+ }
+ if (bytes_read != 4) {
+ mp_raise_ValueError(translate("Invalid file"));
+ }
+ self->file_length = data_length;
+ self->data_start = self->file->fp.fptr;
+
+ // Try to allocate two buffers, one will be loaded from file and the other
+ // DMAed to DAC.
+ if (buffer_size) {
+ self->len = buffer_size / 2;
+ self->buffer = buffer;
+ self->second_buffer = buffer + self->len;
+ } else {
+ self->len = 256;
+ self->buffer = m_malloc(self->len, false);
+ if (self->buffer == NULL) {
+ common_hal_audioio_wavefile_deinit(self);
+ mp_raise_msg(&mp_type_MemoryError,
+ translate("Couldn't allocate first buffer"));
+ }
+
+ self->second_buffer = m_malloc(self->len, false);
+ if (self->second_buffer == NULL) {
+ common_hal_audioio_wavefile_deinit(self);
+ mp_raise_msg(&mp_type_MemoryError,
+ translate("Couldn't allocate second buffer"));
+ }
+ }
+}
+
+void common_hal_audioio_wavefile_deinit(audioio_wavefile_obj_t *self) {
+ self->buffer = NULL;
+ self->second_buffer = NULL;
+}
+
+bool common_hal_audioio_wavefile_deinited(audioio_wavefile_obj_t *self) {
+ return self->buffer == NULL;
+}
+
+uint32_t common_hal_audioio_wavefile_get_sample_rate(audioio_wavefile_obj_t *self) {
+ return self->sample_rate;
+}
+
+void common_hal_audioio_wavefile_set_sample_rate(audioio_wavefile_obj_t *self,
+ uint32_t sample_rate) {
+ self->sample_rate = sample_rate;
+}
+
+uint8_t common_hal_audioio_wavefile_get_bits_per_sample(audioio_wavefile_obj_t *self) {
+ return self->bits_per_sample;
+}
+
+uint8_t common_hal_audioio_wavefile_get_channel_count(audioio_wavefile_obj_t *self) {
+ return self->channel_count;
+}
+
+void audioio_wavefile_reset_buffer(audioio_wavefile_obj_t *self,
+ bool single_channel_output,
+ uint8_t channel) {
+ if (single_channel_output && channel == 1) {
+ return;
+ }
+ // We don't reset the buffer index in case we're looping and we have an odd number of buffer
+ // loads
+ self->bytes_remaining = self->file_length;
+ f_lseek(&self->file->fp, self->data_start);
+ self->read_count = 0;
+ self->left_read_count = 0;
+ self->right_read_count = 0;
+}
+
+audioio_get_buffer_result_t audioio_wavefile_get_buffer(audioio_wavefile_obj_t *self,
+ bool single_channel_output,
+ uint8_t channel,
+ uint8_t **buffer,
+ uint32_t *buffer_length) {
+ if (!single_channel_output) {
+ channel = 0;
+ }
+
+ uint32_t channel_read_count = self->left_read_count;
+ if (channel == 1) {
+ channel_read_count = self->right_read_count;
+ }
+
+ bool need_more_data = self->read_count == channel_read_count;
+
+ if (self->bytes_remaining == 0 && need_more_data) {
+ *buffer = NULL;
+ *buffer_length = 0;
+ return GET_BUFFER_DONE;
+ }
+
+ if (need_more_data) {
+ uint32_t num_bytes_to_load = self->len;
+ if (num_bytes_to_load > self->bytes_remaining) {
+ num_bytes_to_load = self->bytes_remaining;
+ }
+ UINT length_read;
+ if (self->buffer_index % 2 == 1) {
+ *buffer = self->second_buffer;
+ } else {
+ *buffer = self->buffer;
+ }
+ if (f_read(&self->file->fp, *buffer, num_bytes_to_load, &length_read) != FR_OK || length_read != num_bytes_to_load) {
+ return GET_BUFFER_ERROR;
+ }
+ self->bytes_remaining -= length_read;
+ // Pad the last buffer to word align it.
+ if (self->bytes_remaining == 0 && length_read % sizeof(uint32_t) != 0) {
+ uint32_t pad = length_read % sizeof(uint32_t);
+ length_read += pad;
+ if (self->bits_per_sample == 8) {
+ for (uint32_t i = 0; i < pad; i++) {
+ ((uint8_t *)(*buffer))[length_read / sizeof(uint8_t) - i - 1] = 0x80;
+ }
+ } else if (self->bits_per_sample == 16) {
+ // We know the buffer is aligned because we allocated it onto the heap ourselves.
+ #pragma GCC diagnostic push
+ #pragma GCC diagnostic ignored "-Wcast-align"
+ ((int16_t *)(*buffer))[length_read / sizeof(int16_t) - 1] = 0;
+ #pragma GCC diagnostic pop
+ }
+ }
+ *buffer_length = length_read;
+ if (self->buffer_index % 2 == 1) {
+ self->second_buffer_length = length_read;
+ } else {
+ self->buffer_length = length_read;
+ }
+ self->buffer_index += 1;
+ self->read_count += 1;
+ }
+
+ uint32_t buffers_back = self->read_count - 1 - channel_read_count;
+ if ((self->buffer_index - buffers_back) % 2 == 0) {
+ *buffer = self->second_buffer;
+ *buffer_length = self->second_buffer_length;
+ } else {
+ *buffer = self->buffer;
+ *buffer_length = self->buffer_length;
+ }
+
+ if (channel == 0) {
+ self->left_read_count += 1;
+ } else if (channel == 1) {
+ self->right_read_count += 1;
+ *buffer = *buffer + self->bits_per_sample / 8;
+ }
+
+ return self->bytes_remaining == 0 ? GET_BUFFER_DONE : GET_BUFFER_MORE_DATA;
+}
+
+void audioio_wavefile_get_buffer_structure(audioio_wavefile_obj_t *self, bool single_channel_output,
+ bool *single_buffer, bool *samples_signed,
+ uint32_t *max_buffer_length, uint8_t *spacing) {
+ *single_buffer = false;
+ // In WAV files, 8-bit samples are always unsigned, and larger samples are always signed.
+ *samples_signed = self->bits_per_sample > 8;
+ *max_buffer_length = 512;
+ if (single_channel_output) {
+ *spacing = self->channel_count;
+ } else {
+ *spacing = 1;
+ }
+}
diff --git a/circuitpython/shared-module/audiocore/WaveFile.h b/circuitpython/shared-module/audiocore/WaveFile.h
new file mode 100644
index 0000000..986359e
--- /dev/null
+++ b/circuitpython/shared-module/audiocore/WaveFile.h
@@ -0,0 +1,71 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Scott Shawcroft
+ *
+ * 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.
+ */
+
+#ifndef MICROPY_INCLUDED_SHARED_MODULE_AUDIOIO_WAVEFILE_H
+#define MICROPY_INCLUDED_SHARED_MODULE_AUDIOIO_WAVEFILE_H
+
+#include "extmod/vfs_fat.h"
+#include "py/obj.h"
+
+#include "shared-module/audiocore/__init__.h"
+
+typedef struct {
+ mp_obj_base_t base;
+ uint8_t *buffer;
+ uint32_t buffer_length;
+ uint8_t *second_buffer;
+ uint32_t second_buffer_length;
+ uint32_t file_length; // In bytes
+ uint16_t data_start; // Where the data values start
+ uint8_t bits_per_sample;
+ uint16_t buffer_index;
+ uint32_t bytes_remaining;
+
+ uint8_t channel_count;
+ uint32_t sample_rate;
+
+ uint32_t len;
+ pyb_file_obj_t *file;
+
+ uint32_t read_count;
+ uint32_t left_read_count;
+ uint32_t right_read_count;
+} audioio_wavefile_obj_t;
+
+// These are not available from Python because it may be called in an interrupt.
+void audioio_wavefile_reset_buffer(audioio_wavefile_obj_t *self,
+ bool single_channel_output,
+ uint8_t channel);
+audioio_get_buffer_result_t audioio_wavefile_get_buffer(audioio_wavefile_obj_t *self,
+ bool single_channel_output,
+ uint8_t channel,
+ uint8_t **buffer,
+ uint32_t *buffer_length); // length in bytes
+void audioio_wavefile_get_buffer_structure(audioio_wavefile_obj_t *self, bool single_channel_output,
+ bool *single_buffer, bool *samples_signed,
+ uint32_t *max_buffer_length, uint8_t *spacing);
+
+#endif // MICROPY_INCLUDED_SHARED_MODULE_AUDIOIO_WAVEFILE_H
diff --git a/circuitpython/shared-module/audiocore/__init__.c b/circuitpython/shared-module/audiocore/__init__.c
new file mode 100644
index 0000000..b855868
--- /dev/null
+++ b/circuitpython/shared-module/audiocore/__init__.c
@@ -0,0 +1,132 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Scott Shawcroft 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.
+ */
+
+#include "shared-module/audioio/__init__.h"
+
+#include "py/obj.h"
+#include "shared-bindings/audiocore/RawSample.h"
+#include "shared-bindings/audiocore/WaveFile.h"
+#include "shared-module/audiocore/RawSample.h"
+#include "shared-module/audiocore/WaveFile.h"
+
+#include "shared-bindings/audiomixer/Mixer.h"
+#include "shared-module/audiomixer/Mixer.h"
+
+uint32_t audiosample_sample_rate(mp_obj_t sample_obj) {
+ const audiosample_p_t *proto = mp_proto_get_or_throw(MP_QSTR_protocol_audiosample, sample_obj);
+ return proto->sample_rate(MP_OBJ_TO_PTR(sample_obj));
+}
+
+uint8_t audiosample_bits_per_sample(mp_obj_t sample_obj) {
+ const audiosample_p_t *proto = mp_proto_get_or_throw(MP_QSTR_protocol_audiosample, sample_obj);
+ return proto->bits_per_sample(MP_OBJ_TO_PTR(sample_obj));
+}
+
+uint8_t audiosample_channel_count(mp_obj_t sample_obj) {
+ const audiosample_p_t *proto = mp_proto_get_or_throw(MP_QSTR_protocol_audiosample, sample_obj);
+ return proto->channel_count(MP_OBJ_TO_PTR(sample_obj));
+}
+
+void audiosample_reset_buffer(mp_obj_t sample_obj, bool single_channel_output, uint8_t audio_channel) {
+ const audiosample_p_t *proto = mp_proto_get_or_throw(MP_QSTR_protocol_audiosample, sample_obj);
+ proto->reset_buffer(MP_OBJ_TO_PTR(sample_obj), single_channel_output, audio_channel);
+}
+
+audioio_get_buffer_result_t audiosample_get_buffer(mp_obj_t sample_obj,
+ bool single_channel_output,
+ uint8_t channel,
+ uint8_t **buffer, uint32_t *buffer_length) {
+ const audiosample_p_t *proto = mp_proto_get_or_throw(MP_QSTR_protocol_audiosample, sample_obj);
+ return proto->get_buffer(MP_OBJ_TO_PTR(sample_obj), single_channel_output, channel, buffer, buffer_length);
+}
+
+void audiosample_get_buffer_structure(mp_obj_t sample_obj, bool single_channel_output,
+ bool *single_buffer, bool *samples_signed,
+ uint32_t *max_buffer_length, uint8_t *spacing) {
+ const audiosample_p_t *proto = mp_proto_get_or_throw(MP_QSTR_protocol_audiosample, sample_obj);
+ proto->get_buffer_structure(MP_OBJ_TO_PTR(sample_obj), single_channel_output, single_buffer,
+ samples_signed, max_buffer_length, spacing);
+}
+
+void audiosample_convert_u8m_s16s(int16_t *buffer_out, const uint8_t *buffer_in, size_t nframes) {
+ for (; nframes--;) {
+ int16_t sample = (*buffer_in++ - 0x80) << 8;
+ *buffer_out++ = sample;
+ *buffer_out++ = sample;
+ }
+}
+
+
+void audiosample_convert_u8s_s16s(int16_t *buffer_out, const uint8_t *buffer_in, size_t nframes) {
+ size_t nsamples = 2 * nframes;
+ for (; nsamples--;) {
+ int16_t sample = (*buffer_in++ - 0x80) << 8;
+ *buffer_out++ = sample;
+ }
+}
+
+void audiosample_convert_s8m_s16s(int16_t *buffer_out, const int8_t *buffer_in, size_t nframes) {
+ for (; nframes--;) {
+ int16_t sample = (*buffer_in++) << 8;
+ *buffer_out++ = sample;
+ *buffer_out++ = sample;
+ }
+}
+
+
+void audiosample_convert_s8s_s16s(int16_t *buffer_out, const int8_t *buffer_in, size_t nframes) {
+ size_t nsamples = 2 * nframes;
+ for (; nsamples--;) {
+ int16_t sample = (*buffer_in++) << 8;
+ *buffer_out++ = sample;
+ }
+}
+
+
+void audiosample_convert_u16m_s16s(int16_t *buffer_out, const uint16_t *buffer_in, size_t nframes) {
+ for (; nframes--;) {
+ int16_t sample = *buffer_in++ - 0x8000;
+ *buffer_out++ = sample;
+ *buffer_out++ = sample;
+ }
+}
+
+
+void audiosample_convert_u16s_s16s(int16_t *buffer_out, const uint16_t *buffer_in, size_t nframes) {
+ size_t nsamples = 2 * nframes;
+ for (; nsamples--;) {
+ int16_t sample = *buffer_in++ - 0x8000;
+ *buffer_out++ = sample;
+ }
+}
+
+void audiosample_convert_s16m_s16s(int16_t *buffer_out, const int16_t *buffer_in, size_t nframes) {
+ for (; nframes--;) {
+ int16_t sample = *buffer_in++;
+ *buffer_out++ = sample;
+ *buffer_out++ = sample;
+ }
+}
diff --git a/circuitpython/shared-module/audiocore/__init__.h b/circuitpython/shared-module/audiocore/__init__.h
new file mode 100644
index 0000000..e576025
--- /dev/null
+++ b/circuitpython/shared-module/audiocore/__init__.h
@@ -0,0 +1,85 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * 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.
+ */
+
+#ifndef MICROPY_INCLUDED_SHARED_MODULE_AUDIOCORE__INIT__H
+#define MICROPY_INCLUDED_SHARED_MODULE_AUDIOCORE__INIT__H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "py/obj.h"
+#include "py/proto.h"
+
+typedef enum {
+ GET_BUFFER_DONE, // No more data to read
+ GET_BUFFER_MORE_DATA, // More data to read.
+ GET_BUFFER_ERROR, // Error while reading data.
+} audioio_get_buffer_result_t;
+
+typedef uint32_t (*audiosample_sample_rate_fun)(mp_obj_t);
+typedef uint8_t (*audiosample_bits_per_sample_fun)(mp_obj_t);
+typedef uint8_t (*audiosample_channel_count_fun)(mp_obj_t);
+typedef void (*audiosample_reset_buffer_fun)(mp_obj_t,
+ bool single_channel_output, uint8_t audio_channel);
+typedef audioio_get_buffer_result_t (*audiosample_get_buffer_fun)(mp_obj_t,
+ bool single_channel_output, uint8_t channel, uint8_t **buffer,
+ uint32_t *buffer_length);
+typedef void (*audiosample_get_buffer_structure_fun)(mp_obj_t,
+ bool single_channel_output, bool *single_buffer,
+ bool *samples_signed, uint32_t *max_buffer_length,
+ uint8_t *spacing);
+
+typedef struct _audiosample_p_t {
+ MP_PROTOCOL_HEAD // MP_QSTR_protocol_audiosample
+ audiosample_sample_rate_fun sample_rate;
+ audiosample_bits_per_sample_fun bits_per_sample;
+ audiosample_channel_count_fun channel_count;
+ audiosample_reset_buffer_fun reset_buffer;
+ audiosample_get_buffer_fun get_buffer;
+ audiosample_get_buffer_structure_fun get_buffer_structure;
+} audiosample_p_t;
+
+uint32_t audiosample_sample_rate(mp_obj_t sample_obj);
+uint8_t audiosample_bits_per_sample(mp_obj_t sample_obj);
+uint8_t audiosample_channel_count(mp_obj_t sample_obj);
+void audiosample_reset_buffer(mp_obj_t sample_obj, bool single_channel_output, uint8_t audio_channel);
+audioio_get_buffer_result_t audiosample_get_buffer(mp_obj_t sample_obj,
+ bool single_channel_output,
+ uint8_t channel,
+ uint8_t **buffer, uint32_t *buffer_length);
+void audiosample_get_buffer_structure(mp_obj_t sample_obj, bool single_channel_output,
+ bool *single_buffer, bool *samples_signed,
+ uint32_t *max_buffer_length, uint8_t *spacing);
+
+void audiosample_convert_u8m_s16s(int16_t *buffer_out, const uint8_t *buffer_in, size_t nframes);
+void audiosample_convert_u8s_s16s(int16_t *buffer_out, const uint8_t *buffer_in, size_t nframes);
+void audiosample_convert_s8m_s16s(int16_t *buffer_out, const int8_t *buffer_in, size_t nframes);
+void audiosample_convert_s8s_s16s(int16_t *buffer_out, const int8_t *buffer_in, size_t nframes);
+void audiosample_convert_u16m_s16s(int16_t *buffer_out, const uint16_t *buffer_in, size_t nframes);
+void audiosample_convert_u16s_s16s(int16_t *buffer_out, const uint16_t *buffer_in, size_t nframes);
+void audiosample_convert_s16m_s16s(int16_t *buffer_out, const int16_t *buffer_in, size_t nframes);
+
+#endif // MICROPY_INCLUDED_SHARED_MODULE_AUDIOCORE__INIT__H
diff --git a/circuitpython/shared-module/audioio/__init__.c b/circuitpython/shared-module/audioio/__init__.c
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/circuitpython/shared-module/audioio/__init__.c
diff --git a/circuitpython/shared-module/audioio/__init__.h b/circuitpython/shared-module/audioio/__init__.h
new file mode 100644
index 0000000..53b2d4a
--- /dev/null
+++ b/circuitpython/shared-module/audioio/__init__.h
@@ -0,0 +1,32 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * 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.
+ */
+
+#ifndef MICROPY_INCLUDED_SHARED_MODULE_AUDIOIO__INIT__H
+#define MICROPY_INCLUDED_SHARED_MODULE_AUDIOIO__INIT__H
+
+#include "shared-module/audiocore/__init__.h"
+
+#endif // MICROPY_INCLUDED_SHARED_MODULE_AUDIOIO__INIT__H
diff --git a/circuitpython/shared-module/audiomixer/Mixer.c b/circuitpython/shared-module/audiomixer/Mixer.c
new file mode 100644
index 0000000..d6569c6
--- /dev/null
+++ b/circuitpython/shared-module/audiomixer/Mixer.c
@@ -0,0 +1,365 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Scott Shawcroft for Adafruit Industries
+ * 2018 DeanM for Adafruit Industries
+ * 2019 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.
+ */
+
+#include "shared-bindings/audiomixer/Mixer.h"
+#include "shared-bindings/audiomixer/MixerVoice.h"
+
+#include <stdint.h>
+
+#include "py/runtime.h"
+#include "shared-module/audiocore/__init__.h"
+#include "shared-module/audiocore/RawSample.h"
+
+void common_hal_audiomixer_mixer_construct(audiomixer_mixer_obj_t *self,
+ uint8_t voice_count,
+ uint32_t buffer_size,
+ uint8_t bits_per_sample,
+ bool samples_signed,
+ uint8_t channel_count,
+ uint32_t sample_rate) {
+ self->len = buffer_size / 2 / sizeof(uint32_t) * sizeof(uint32_t);
+
+ self->first_buffer = m_malloc(self->len, false);
+ if (self->first_buffer == NULL) {
+ common_hal_audiomixer_mixer_deinit(self);
+ mp_raise_msg(&mp_type_MemoryError, translate("Couldn't allocate first buffer"));
+ }
+
+ self->second_buffer = m_malloc(self->len, false);
+ if (self->second_buffer == NULL) {
+ common_hal_audiomixer_mixer_deinit(self);
+ mp_raise_msg(&mp_type_MemoryError, translate("Couldn't allocate second buffer"));
+ }
+
+ self->bits_per_sample = bits_per_sample;
+ self->samples_signed = samples_signed;
+ self->channel_count = channel_count;
+ self->sample_rate = sample_rate;
+ self->voice_count = voice_count;
+}
+
+void common_hal_audiomixer_mixer_deinit(audiomixer_mixer_obj_t *self) {
+ self->first_buffer = NULL;
+ self->second_buffer = NULL;
+}
+
+bool common_hal_audiomixer_mixer_deinited(audiomixer_mixer_obj_t *self) {
+ return self->first_buffer == NULL;
+}
+
+uint32_t common_hal_audiomixer_mixer_get_sample_rate(audiomixer_mixer_obj_t *self) {
+ return self->sample_rate;
+}
+
+uint8_t common_hal_audiomixer_mixer_get_channel_count(audiomixer_mixer_obj_t *self) {
+ return self->channel_count;
+}
+
+uint8_t common_hal_audiomixer_mixer_get_bits_per_sample(audiomixer_mixer_obj_t *self) {
+ return self->bits_per_sample;
+}
+
+bool common_hal_audiomixer_mixer_get_playing(audiomixer_mixer_obj_t *self) {
+ for (uint8_t v = 0; v < self->voice_count; v++) {
+ if (common_hal_audiomixer_mixervoice_get_playing(MP_OBJ_TO_PTR(self->voice[v]))) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void audiomixer_mixer_reset_buffer(audiomixer_mixer_obj_t *self,
+ bool single_channel_output,
+ uint8_t channel) {
+ for (uint8_t i = 0; i < self->voice_count; i++) {
+ common_hal_audiomixer_mixervoice_stop(self->voice[i]);
+ }
+}
+
+__attribute__((always_inline))
+static inline uint32_t add16signed(uint32_t a, uint32_t b) {
+ #if (defined(__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1))
+ return __QADD16(a, b);
+ #else
+ uint32_t result = 0;
+ for (int8_t i = 0; i < 2; i++) {
+ int16_t ai = a >> (sizeof(int16_t) * 8 * i);
+ int16_t bi = b >> (sizeof(int16_t) * 8 * i);
+ int32_t intermediate = (int32_t)ai + bi;
+ if (intermediate > SHRT_MAX) {
+ intermediate = SHRT_MAX;
+ } else if (intermediate < SHRT_MIN) {
+ intermediate = SHRT_MIN;
+ }
+ result |= (((uint32_t)intermediate) & 0xffff) << (sizeof(int16_t) * 8 * i);
+ }
+ return result;
+ #endif
+}
+
+__attribute__((always_inline))
+static inline uint32_t mult16signed(uint32_t val, int32_t mul) {
+ #if (defined(__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1))
+ mul <<= 16;
+ int32_t hi, lo;
+ enum { bits = 16 }; // saturate to 16 bits
+ enum { shift = 15 }; // shift is done automatically
+ asm volatile ("smulwb %0, %1, %2" : "=r" (lo) : "r" (mul), "r" (val));
+ asm volatile ("smulwt %0, %1, %2" : "=r" (hi) : "r" (mul), "r" (val));
+ asm volatile ("ssat %0, %1, %2, asr %3" : "=r" (lo) : "I" (bits), "r" (lo), "I" (shift));
+ asm volatile ("ssat %0, %1, %2, asr %3" : "=r" (hi) : "I" (bits), "r" (hi), "I" (shift));
+ asm volatile ("pkhbt %0, %1, %2, lsl #16" : "=r" (val) : "r" (lo), "r" (hi)); // pack
+ return val;
+ #else
+ uint32_t result = 0;
+ float mod_mul = (float)mul / (float)((1 << 15) - 1);
+ for (int8_t i = 0; i < 2; i++) {
+ int16_t ai = (val >> (sizeof(uint16_t) * 8 * i));
+ int32_t intermediate = ai * mod_mul;
+ if (intermediate > SHRT_MAX) {
+ intermediate = SHRT_MAX;
+ } else if (intermediate < SHRT_MIN) {
+ intermediate = SHRT_MIN;
+ }
+ intermediate &= 0x0000FFFF;
+ result |= (((uint32_t)intermediate)) << (sizeof(int16_t) * 8 * i);
+ }
+ return result;
+ #endif
+}
+
+static inline uint32_t tounsigned8(uint32_t val) {
+ #if (defined(__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1))
+ return __UADD8(val, 0x80808080);
+ #else
+ return val ^ 0x80808080;
+ #endif
+}
+
+static inline uint32_t tounsigned16(uint32_t val) {
+ #if (defined(__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1))
+ return __UADD16(val, 0x80008000);
+ #else
+ return val ^ 0x80008000;
+ #endif
+}
+
+static inline uint32_t tosigned16(uint32_t val) {
+ #if (defined(__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1))
+ return __UADD16(val, 0x80008000);
+ #else
+ return val ^ 0x80008000;
+ #endif
+}
+
+static inline uint32_t unpack8(uint16_t val) {
+ return ((val & 0xff00) << 16) | ((val & 0x00ff) << 8);
+}
+
+static inline uint32_t pack8(uint32_t val) {
+ return ((val & 0xff000000) >> 16) | ((val & 0xff00) >> 8);
+}
+
+static void mix_down_one_voice(audiomixer_mixer_obj_t *self,
+ audiomixer_mixervoice_obj_t *voice, bool voices_active,
+ uint32_t *word_buffer, uint32_t length) {
+ while (length != 0) {
+ if (voice->buffer_length == 0) {
+ if (!voice->more_data) {
+ if (voice->loop) {
+ audiosample_reset_buffer(voice->sample, false, 0);
+ } else {
+ voice->sample = NULL;
+ break;
+ }
+ }
+ if (voice->sample) {
+ // Load another buffer
+ audioio_get_buffer_result_t result = audiosample_get_buffer(voice->sample, false, 0, (uint8_t **)&voice->remaining_buffer, &voice->buffer_length);
+ // Track length in terms of words.
+ voice->buffer_length /= sizeof(uint32_t);
+ voice->more_data = result == GET_BUFFER_MORE_DATA;
+ }
+ }
+
+ uint32_t n = MIN(voice->buffer_length, length);
+ uint32_t *src = voice->remaining_buffer;
+ uint16_t level = voice->level;
+
+ // First active voice gets copied over verbatim.
+ if (!voices_active) {
+ if (MP_LIKELY(self->bits_per_sample == 16)) {
+ if (MP_LIKELY(self->samples_signed)) {
+ for (uint32_t i = 0; i < n; i++) {
+ uint32_t v = src[i];
+ word_buffer[i] = mult16signed(v, level);
+ }
+ } else {
+ for (uint32_t i = 0; i < n; i++) {
+ uint32_t v = src[i];
+ v = tosigned16(v);
+ word_buffer[i] = mult16signed(v, level);
+ }
+ }
+ } else {
+ uint16_t *hword_buffer = (uint16_t *)word_buffer;
+ uint16_t *hsrc = (uint16_t *)src;
+ for (uint32_t i = 0; i < n * 2; i++) {
+ uint32_t word = unpack8(hsrc[i]);
+ if (MP_LIKELY(!self->samples_signed)) {
+ word = tosigned16(word);
+ }
+ word = mult16signed(word, level);
+ hword_buffer[i] = pack8(word);
+ }
+ }
+ } else {
+ if (MP_LIKELY(self->bits_per_sample == 16)) {
+ if (MP_LIKELY(self->samples_signed)) {
+ for (uint32_t i = 0; i < n; i++) {
+ uint32_t word = src[i];
+ word_buffer[i] = add16signed(mult16signed(word, level), word_buffer[i]);
+ }
+ } else {
+ for (uint32_t i = 0; i < n; i++) {
+ uint32_t word = src[i];
+ word = tosigned16(word);
+ word_buffer[i] = add16signed(mult16signed(word, level), word_buffer[i]);
+ }
+ }
+ } else {
+ uint16_t *hword_buffer = (uint16_t *)word_buffer;
+ uint16_t *hsrc = (uint16_t *)src;
+ for (uint32_t i = 0; i < n * 2; i++) {
+ uint32_t word = unpack8(hsrc[i]);
+ if (MP_LIKELY(!self->samples_signed)) {
+ word = tosigned16(word);
+ }
+ word = mult16signed(word, level);
+ word = add16signed(word, unpack8(hword_buffer[i]));
+ hword_buffer[i] = pack8(word);
+ }
+ }
+ }
+ length -= n;
+ word_buffer += n;
+ voice->remaining_buffer += n;
+ voice->buffer_length -= n;
+ }
+
+ if (length && !voices_active) {
+ for (uint32_t i = 0; i < length; i++) {
+ word_buffer[i] = 0;
+ }
+ }
+}
+
+audioio_get_buffer_result_t audiomixer_mixer_get_buffer(audiomixer_mixer_obj_t *self,
+ bool single_channel_output,
+ uint8_t channel,
+ uint8_t **buffer,
+ uint32_t *buffer_length) {
+ if (!single_channel_output) {
+ channel = 0;
+ }
+
+ uint32_t channel_read_count = self->left_read_count;
+ if (channel == 1) {
+ channel_read_count = self->right_read_count;
+ }
+ *buffer_length = self->len;
+
+ bool need_more_data = self->read_count == channel_read_count;
+ if (need_more_data) {
+ uint32_t *word_buffer;
+ if (self->use_first_buffer) {
+ *buffer = (uint8_t *)self->first_buffer;
+ word_buffer = self->first_buffer;
+ } else {
+ *buffer = (uint8_t *)self->second_buffer;
+ word_buffer = self->second_buffer;
+ }
+ self->use_first_buffer = !self->use_first_buffer;
+ bool voices_active = false;
+ uint32_t length = self->len / sizeof(uint32_t);
+
+ for (int32_t v = 0; v < self->voice_count; v++) {
+ audiomixer_mixervoice_obj_t *voice = MP_OBJ_TO_PTR(self->voice[v]);
+ if (voice->sample) {
+ mix_down_one_voice(self, voice, voices_active, word_buffer, length);
+ voices_active = true;
+ }
+ }
+
+ if (!voices_active) {
+ for (uint32_t i = 0; i < length; i++) {
+ word_buffer[i] = 0;
+ }
+ }
+
+ if (!self->samples_signed) {
+ if (self->bits_per_sample == 16) {
+ for (uint32_t i = 0; i < length; i++) {
+ word_buffer[i] = tounsigned16(word_buffer[i]);
+ }
+ } else {
+ for (uint32_t i = 0; i < length; i++) {
+ word_buffer[i] = tounsigned8(word_buffer[i]);
+ }
+ }
+ }
+
+ self->read_count += 1;
+ } else if (!self->use_first_buffer) {
+ *buffer = (uint8_t *)self->first_buffer;
+ } else {
+ *buffer = (uint8_t *)self->second_buffer;
+ }
+
+
+ if (channel == 0) {
+ self->left_read_count += 1;
+ } else if (channel == 1) {
+ self->right_read_count += 1;
+ *buffer = *buffer + self->bits_per_sample / 8;
+ }
+ return GET_BUFFER_MORE_DATA;
+}
+
+void audiomixer_mixer_get_buffer_structure(audiomixer_mixer_obj_t *self, bool single_channel_output,
+ bool *single_buffer, bool *samples_signed,
+ uint32_t *max_buffer_length, uint8_t *spacing) {
+ *single_buffer = false;
+ *samples_signed = self->samples_signed;
+ *max_buffer_length = self->len;
+ if (single_channel_output) {
+ *spacing = self->channel_count;
+ } else {
+ *spacing = 1;
+ }
+}
diff --git a/circuitpython/shared-module/audiomixer/Mixer.h b/circuitpython/shared-module/audiomixer/Mixer.h
new file mode 100644
index 0000000..c9228d9
--- /dev/null
+++ b/circuitpython/shared-module/audiomixer/Mixer.h
@@ -0,0 +1,69 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Scott Shawcroft
+ *
+ * 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.
+ */
+
+#ifndef MICROPY_INCLUDED_SHARED_MODULE_AUDIOMIXER_MIXER_H
+#define MICROPY_INCLUDED_SHARED_MODULE_AUDIOMIXER_MIXER_H
+
+#include "py/obj.h"
+#include "py/objtuple.h"
+
+#include "shared-module/audiocore/__init__.h"
+
+typedef struct {
+ mp_obj_base_t base;
+ uint32_t *first_buffer;
+ uint32_t *second_buffer;
+ uint32_t len; // in words
+ uint8_t bits_per_sample;
+ bool use_first_buffer;
+ bool samples_signed;
+ uint8_t channel_count;
+ uint32_t sample_rate;
+
+ uint32_t read_count;
+ uint32_t left_read_count;
+ uint32_t right_read_count;
+
+ uint8_t voice_count;
+ mp_obj_tuple_t *voice_tuple;
+ mp_obj_t voice[];
+} audiomixer_mixer_obj_t;
+
+
+// These are not available from Python because it may be called in an interrupt.
+void audiomixer_mixer_reset_buffer(audiomixer_mixer_obj_t *self,
+ bool single_channel_output,
+ uint8_t channel);
+audioio_get_buffer_result_t audiomixer_mixer_get_buffer(audiomixer_mixer_obj_t *self,
+ bool single_channel_output,
+ uint8_t channel,
+ uint8_t **buffer,
+ uint32_t *buffer_length); // length in bytes
+void audiomixer_mixer_get_buffer_structure(audiomixer_mixer_obj_t *self, bool single_channel_output,
+ bool *single_buffer, bool *samples_signed,
+ uint32_t *max_buffer_length, uint8_t *spacing);
+
+#endif // MICROPY_INCLUDED_SHARED_MODULE_AUDIOMIXER_MIXER_H
diff --git a/circuitpython/shared-module/audiomixer/MixerVoice.c b/circuitpython/shared-module/audiomixer/MixerVoice.c
new file mode 100644
index 0000000..bb58b0c
--- /dev/null
+++ b/circuitpython/shared-module/audiomixer/MixerVoice.c
@@ -0,0 +1,88 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 DeanM 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.
+ */
+#include "shared-bindings/audiomixer/Mixer.h"
+#include "shared-bindings/audiomixer/MixerVoice.h"
+#include "shared-module/audiomixer/MixerVoice.h"
+
+#include <stdint.h>
+
+#include "py/runtime.h"
+#include "shared-module/audiomixer/__init__.h"
+#include "shared-module/audiocore/RawSample.h"
+
+void common_hal_audiomixer_mixervoice_construct(audiomixer_mixervoice_obj_t *self) {
+ self->sample = NULL;
+ self->level = 1 << 15;
+}
+
+void common_hal_audiomixer_mixervoice_set_parent(audiomixer_mixervoice_obj_t *self, audiomixer_mixer_obj_t *parent) {
+ self->parent = parent;
+}
+
+float common_hal_audiomixer_mixervoice_get_level(audiomixer_mixervoice_obj_t *self) {
+ return (float)self->level / (1 << 15);
+}
+
+void common_hal_audiomixer_mixervoice_set_level(audiomixer_mixervoice_obj_t *self, float level) {
+ self->level = level * (1 << 15);
+}
+
+void common_hal_audiomixer_mixervoice_play(audiomixer_mixervoice_obj_t *self, mp_obj_t sample, bool loop) {
+ if (audiosample_sample_rate(sample) != self->parent->sample_rate) {
+ mp_raise_ValueError(translate("The sample's sample rate does not match the mixer's"));
+ }
+ if (audiosample_channel_count(sample) != self->parent->channel_count) {
+ mp_raise_ValueError(translate("The sample's channel count does not match the mixer's"));
+ }
+ if (audiosample_bits_per_sample(sample) != self->parent->bits_per_sample) {
+ mp_raise_ValueError(translate("The sample's bits_per_sample does not match the mixer's"));
+ }
+ bool single_buffer;
+ bool samples_signed;
+ uint32_t max_buffer_length;
+ uint8_t spacing;
+ audiosample_get_buffer_structure(sample, false, &single_buffer, &samples_signed,
+ &max_buffer_length, &spacing);
+ if (samples_signed != self->parent->samples_signed) {
+ mp_raise_ValueError(translate("The sample's signedness does not match the mixer's"));
+ }
+ self->sample = sample;
+ self->loop = loop;
+
+ audiosample_reset_buffer(sample, false, 0);
+ audioio_get_buffer_result_t result = audiosample_get_buffer(sample, false, 0, (uint8_t **)&self->remaining_buffer, &self->buffer_length);
+ // Track length in terms of words.
+ self->buffer_length /= sizeof(uint32_t);
+ self->more_data = result == GET_BUFFER_MORE_DATA;
+}
+
+bool common_hal_audiomixer_mixervoice_get_playing(audiomixer_mixervoice_obj_t *self) {
+ return self->sample != NULL;
+}
+
+void common_hal_audiomixer_mixervoice_stop(audiomixer_mixervoice_obj_t *self) {
+ self->sample = NULL;
+}
diff --git a/circuitpython/shared-module/audiomixer/MixerVoice.h b/circuitpython/shared-module/audiomixer/MixerVoice.h
new file mode 100644
index 0000000..1fdc91d
--- /dev/null
+++ b/circuitpython/shared-module/audiomixer/MixerVoice.h
@@ -0,0 +1,46 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 DeanM 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.
+ */
+#ifndef SHARED_MODULE_AUDIOMIXER_MIXERVOICE_H_
+#define SHARED_MODULE_AUDIOMIXER_MIXERVOICE_H_
+
+#include "py/obj.h"
+
+#include "shared-module/audiomixer/__init__.h"
+#include "shared-module/audiomixer/Mixer.h"
+
+typedef struct {
+ mp_obj_base_t base;
+ audiomixer_mixer_obj_t *parent;
+ mp_obj_t sample;
+ bool loop;
+ bool more_data;
+ uint32_t *remaining_buffer;
+ uint32_t buffer_length;
+ uint16_t level;
+} audiomixer_mixervoice_obj_t;
+
+
+#endif /* SHARED_MODULE_AUDIOMIXER_MIXERVOICE_H_ */
diff --git a/circuitpython/shared-module/audiomixer/__init__.c b/circuitpython/shared-module/audiomixer/__init__.c
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/circuitpython/shared-module/audiomixer/__init__.c
diff --git a/circuitpython/shared-module/audiomixer/__init__.h b/circuitpython/shared-module/audiomixer/__init__.h
new file mode 100644
index 0000000..35b7315
--- /dev/null
+++ b/circuitpython/shared-module/audiomixer/__init__.h
@@ -0,0 +1,32 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * 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.
+ */
+
+#ifndef MICROPY_INCLUDED_SHARED_MODULE_AUDIOMIXER__INIT__H
+#define MICROPY_INCLUDED_SHARED_MODULE_AUDIOMIXER__INIT__H
+
+#include "shared-module/audiocore/__init__.h"
+
+#endif // MICROPY_INCLUDED_SHARED_MODULE_AUDIOMIXER__INIT__H
diff --git a/circuitpython/shared-module/audiomp3/MP3Decoder.c b/circuitpython/shared-module/audiomp3/MP3Decoder.c
new file mode 100644
index 0000000..bbc7e1c
--- /dev/null
+++ b/circuitpython/shared-module/audiomp3/MP3Decoder.c
@@ -0,0 +1,399 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Scott Shawcroft for Adafruit Industries
+ * Copyright (c) 2019 Jeff Epler 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.
+ */
+
+#include "shared-bindings/audiomp3/MP3Decoder.h"
+
+#include <stdint.h>
+#include <string.h>
+#include <math.h>
+
+#include "py/mperrno.h"
+#include "py/runtime.h"
+
+#include "shared-module/audiomp3/MP3Decoder.h"
+#include "supervisor/shared/translate.h"
+#include "supervisor/background_callback.h"
+#include "lib/mp3/src/mp3common.h"
+
+#define MAX_BUFFER_LEN (MAX_NSAMP * MAX_NGRAN * MAX_NCHAN * sizeof(int16_t))
+
+/** Fill the input buffer unconditionally.
+ *
+ * Returns true if the input buffer contains any useful data,
+ * false otherwise. (The input buffer will be padded to the end with
+ * 0 bytes, which do not interfere with MP3 decoding)
+ *
+ * Raises OSError if f_read fails.
+ *
+ * Sets self->eof if any read of the file returns 0 bytes
+ */
+STATIC bool mp3file_update_inbuf_always(audiomp3_mp3file_obj_t *self) {
+ // If we didn't previously reach the end of file, we can try reading now
+ if (!self->eof && self->inbuf_offset != 0) {
+
+ // Move the unconsumed portion of the buffer to the start
+ uint8_t *end_of_buffer = self->inbuf + self->inbuf_length;
+ uint8_t *new_end_of_data = self->inbuf + self->inbuf_length - self->inbuf_offset;
+ memmove(self->inbuf, self->inbuf + self->inbuf_offset,
+ self->inbuf_length - self->inbuf_offset);
+ self->inbuf_offset = 0;
+
+ UINT to_read = end_of_buffer - new_end_of_data;
+ UINT bytes_read = 0;
+ memset(new_end_of_data, 0, to_read);
+ if (f_read(&self->file->fp, new_end_of_data, to_read, &bytes_read) != FR_OK) {
+ self->eof = true;
+ mp_raise_OSError(MP_EIO);
+ }
+
+ if (bytes_read == 0) {
+ self->eof = true;
+ }
+
+ if (to_read != bytes_read) {
+ new_end_of_data += bytes_read;
+ memset(new_end_of_data, 0, end_of_buffer - new_end_of_data);
+ }
+
+ }
+
+ // Return true iff there are at least some useful bytes in the buffer
+ return self->inbuf_offset < self->inbuf_length;
+}
+
+/** Update the inbuf from a background callback.
+ *
+ * This variant is introduced so that at the site of the
+ * add_background_callback_core call, the prototype matches.
+ */
+STATIC void mp3file_update_inbuf_cb(void *self) {
+ mp3file_update_inbuf_always(self);
+}
+
+/** Fill the input buffer if it is less than half full.
+ *
+ * Returns the same as mp3file_update_inbuf_always.
+ */
+STATIC bool mp3file_update_inbuf_half(audiomp3_mp3file_obj_t *self) {
+ // If buffer is over half full, do nothing
+ if (self->inbuf_offset < self->inbuf_length / 2) {
+ return true;
+ }
+
+ return mp3file_update_inbuf_always(self);
+}
+
+#define READ_PTR(self) (self->inbuf + self->inbuf_offset)
+#define BYTES_LEFT(self) (self->inbuf_length - self->inbuf_offset)
+#define CONSUME(self, n) (self->inbuf_offset += n)
+
+// http://id3.org/d3v2.3.0
+// http://id3.org/id3v2.3.0
+STATIC void mp3file_skip_id3v2(audiomp3_mp3file_obj_t *self) {
+ mp3file_update_inbuf_half(self);
+ if (BYTES_LEFT(self) < 10) {
+ return;
+ }
+ uint8_t *data = READ_PTR(self);
+ if (!(
+ data[0] == 'I' &&
+ data[1] == 'D' &&
+ data[2] == '3' &&
+ data[3] != 0xff &&
+ data[4] != 0xff &&
+ (data[5] & 0x1f) == 0 &&
+ (data[6] & 0x80) == 0 &&
+ (data[7] & 0x80) == 0 &&
+ (data[8] & 0x80) == 0 &&
+ (data[9] & 0x80) == 0)) {
+ return;
+ }
+ uint32_t size = (data[6] << 21) | (data[7] << 14) | (data[8] << 7) | (data[9]);
+ size += 10; // size excludes the "header" (but not the "extended header")
+ // First, deduct from size whatever is left in buffer
+ uint32_t to_consume = MIN(size, BYTES_LEFT(self));
+ CONSUME(self, to_consume);
+ size -= to_consume;
+
+ // Next, seek in the file after the header
+ f_lseek(&self->file->fp, f_tell(&self->file->fp) + size);
+ return;
+}
+
+/* If a sync word can be found, advance to it and return true. Otherwise,
+ * return false.
+ */
+STATIC bool mp3file_find_sync_word(audiomp3_mp3file_obj_t *self) {
+ do {
+ mp3file_update_inbuf_half(self);
+ int offset = MP3FindSyncWord(READ_PTR(self), BYTES_LEFT(self));
+ if (offset >= 0) {
+ CONSUME(self, offset);
+ mp3file_update_inbuf_half(self);
+ return true;
+ }
+ CONSUME(self, MAX(0, BYTES_LEFT(self) - 16));
+ } while (!self->eof);
+ return false;
+}
+
+STATIC bool mp3file_get_next_frame_info(audiomp3_mp3file_obj_t *self, MP3FrameInfo *fi) {
+ int err;
+ do {
+ err = MP3GetNextFrameInfo(self->decoder, fi, READ_PTR(self));
+ if (err == ERR_MP3_NONE) {
+ break;
+ }
+ CONSUME(self, 1);
+ mp3file_find_sync_word(self);
+ } while (!self->eof);
+ return err == ERR_MP3_NONE;
+}
+
+void common_hal_audiomp3_mp3file_construct(audiomp3_mp3file_obj_t *self,
+ pyb_file_obj_t *file,
+ uint8_t *buffer,
+ size_t buffer_size) {
+ // XXX Adafruit_MP3 uses a 2kB input buffer and two 4kB output buffers.
+ // for a whopping total of 10kB buffers (+mp3 decoder state and frame buffer)
+ // At 44kHz, that's 23ms of output audio data.
+ //
+ // We will choose a slightly different allocation strategy for the output:
+ // Make sure the buffers are sized exactly to match (a multiple of) the
+ // frame size; this is typically 2304 * 2 bytes, so a little bit bigger
+ // than the two 4kB output buffers, except that the alignment allows to
+ // never allocate that extra frame buffer.
+
+ self->inbuf_length = 2048;
+ self->inbuf_offset = self->inbuf_length;
+ self->inbuf = m_malloc(self->inbuf_length, false);
+ if (self->inbuf == NULL) {
+ common_hal_audiomp3_mp3file_deinit(self);
+ mp_raise_msg(&mp_type_MemoryError,
+ translate("Couldn't allocate input buffer"));
+ }
+ self->decoder = MP3InitDecoder();
+ if (self->decoder == NULL) {
+ common_hal_audiomp3_mp3file_deinit(self);
+ mp_raise_msg(&mp_type_MemoryError,
+ translate("Couldn't allocate decoder"));
+ }
+
+ if ((intptr_t)buffer & 1) {
+ buffer += 1;
+ buffer_size -= 1;
+ }
+ if (buffer_size >= 2 * MAX_BUFFER_LEN) {
+ self->buffers[0] = (int16_t *)(void *)buffer;
+ self->buffers[1] = (int16_t *)(void *)(buffer + MAX_BUFFER_LEN);
+ } else {
+ self->buffers[0] = m_malloc(MAX_BUFFER_LEN, false);
+ if (self->buffers[0] == NULL) {
+ common_hal_audiomp3_mp3file_deinit(self);
+ mp_raise_msg(&mp_type_MemoryError,
+ translate("Couldn't allocate first buffer"));
+ }
+
+ self->buffers[1] = m_malloc(MAX_BUFFER_LEN, false);
+ if (self->buffers[1] == NULL) {
+ common_hal_audiomp3_mp3file_deinit(self);
+ mp_raise_msg(&mp_type_MemoryError,
+ translate("Couldn't allocate second buffer"));
+ }
+ }
+
+ common_hal_audiomp3_mp3file_set_file(self, file);
+}
+
+void common_hal_audiomp3_mp3file_set_file(audiomp3_mp3file_obj_t *self, pyb_file_obj_t *file) {
+ background_callback_begin_critical_section();
+
+ self->file = file;
+ f_lseek(&self->file->fp, 0);
+ self->inbuf_offset = self->inbuf_length;
+ self->eof = 0;
+ self->other_channel = -1;
+ mp3file_update_inbuf_half(self);
+ mp3file_find_sync_word(self);
+ // It **SHOULD** not be necessary to do this; the buffer should be filled
+ // with fresh content before it is returned by get_buffer(). The fact that
+ // this is necessary to avoid a glitch at the start of playback of a second
+ // track using the same decoder object means there's still a bug in
+ // get_buffer() that I didn't understand.
+ memset(self->buffers[0], 0, MAX_BUFFER_LEN);
+ memset(self->buffers[1], 0, MAX_BUFFER_LEN);
+ MP3FrameInfo fi;
+ bool result = mp3file_get_next_frame_info(self, &fi);
+ background_callback_end_critical_section();
+ if (!result) {
+ mp_raise_msg(&mp_type_RuntimeError,
+ translate("Failed to parse MP3 file"));
+ }
+
+ self->sample_rate = fi.samprate;
+ self->channel_count = fi.nChans;
+ self->frame_buffer_size = fi.outputSamps * sizeof(int16_t);
+ self->len = 2 * self->frame_buffer_size;
+ self->samples_decoded = 0;
+}
+
+void common_hal_audiomp3_mp3file_deinit(audiomp3_mp3file_obj_t *self) {
+ MP3FreeDecoder(self->decoder);
+ self->decoder = NULL;
+ self->inbuf = NULL;
+ self->buffers[0] = NULL;
+ self->buffers[1] = NULL;
+ self->file = NULL;
+ self->samples_decoded = 0;
+}
+
+bool common_hal_audiomp3_mp3file_deinited(audiomp3_mp3file_obj_t *self) {
+ return self->buffers[0] == NULL;
+}
+
+uint32_t common_hal_audiomp3_mp3file_get_sample_rate(audiomp3_mp3file_obj_t *self) {
+ return self->sample_rate;
+}
+
+void common_hal_audiomp3_mp3file_set_sample_rate(audiomp3_mp3file_obj_t *self,
+ uint32_t sample_rate) {
+ self->sample_rate = sample_rate;
+}
+
+uint8_t common_hal_audiomp3_mp3file_get_bits_per_sample(audiomp3_mp3file_obj_t *self) {
+ return 16;
+}
+
+uint8_t common_hal_audiomp3_mp3file_get_channel_count(audiomp3_mp3file_obj_t *self) {
+ return self->channel_count;
+}
+
+void audiomp3_mp3file_reset_buffer(audiomp3_mp3file_obj_t *self,
+ bool single_channel_output,
+ uint8_t channel) {
+ if (single_channel_output && channel == 1) {
+ return;
+ }
+ // We don't reset the buffer index in case we're looping and we have an odd number of buffer
+ // loads
+ background_callback_begin_critical_section();
+ f_lseek(&self->file->fp, 0);
+ self->inbuf_offset = self->inbuf_length;
+ self->eof = 0;
+ self->samples_decoded = 0;
+ self->other_channel = -1;
+ mp3file_update_inbuf_half(self);
+ mp3file_skip_id3v2(self);
+ mp3file_find_sync_word(self);
+ background_callback_end_critical_section();
+}
+
+audioio_get_buffer_result_t audiomp3_mp3file_get_buffer(audiomp3_mp3file_obj_t *self,
+ bool single_channel_output,
+ uint8_t channel,
+ uint8_t **bufptr,
+ uint32_t *buffer_length) {
+ if (!self->inbuf) {
+ *buffer_length = 0;
+ return GET_BUFFER_ERROR;
+ }
+ if (!single_channel_output) {
+ channel = 0;
+ }
+
+ *buffer_length = self->frame_buffer_size;
+
+ if (channel == self->other_channel) {
+ *bufptr = (uint8_t *)(self->buffers[self->other_buffer_index] + channel);
+ self->other_channel = -1;
+ self->samples_decoded += *buffer_length / sizeof(int16_t);
+ return GET_BUFFER_MORE_DATA;
+ }
+
+
+ self->buffer_index = !self->buffer_index;
+ self->other_channel = 1 - channel;
+ self->other_buffer_index = self->buffer_index;
+ int16_t *buffer = (int16_t *)(void *)self->buffers[self->buffer_index];
+ *bufptr = (uint8_t *)buffer;
+
+ mp3file_skip_id3v2(self);
+ if (!mp3file_find_sync_word(self)) {
+ *buffer_length = 0;
+ return self->eof ? GET_BUFFER_DONE : GET_BUFFER_ERROR;
+ }
+ int bytes_left = BYTES_LEFT(self);
+ uint8_t *inbuf = READ_PTR(self);
+ int err = MP3Decode(self->decoder, &inbuf, &bytes_left, buffer, 0);
+ CONSUME(self, BYTES_LEFT(self) - bytes_left);
+
+ if (err) {
+ *buffer_length = 0;
+ return GET_BUFFER_DONE;
+ }
+
+ self->samples_decoded += *buffer_length / sizeof(int16_t);
+
+ mp3file_skip_id3v2(self);
+ int result = mp3file_find_sync_word(self) ? GET_BUFFER_MORE_DATA : GET_BUFFER_DONE;
+
+ if (self->inbuf_offset >= 512) {
+ background_callback_add(
+ &self->inbuf_fill_cb,
+ mp3file_update_inbuf_cb,
+ self);
+ }
+
+ return result;
+}
+
+void audiomp3_mp3file_get_buffer_structure(audiomp3_mp3file_obj_t *self, bool single_channel_output,
+ bool *single_buffer, bool *samples_signed,
+ uint32_t *max_buffer_length, uint8_t *spacing) {
+ *single_buffer = false;
+ *samples_signed = true;
+ *max_buffer_length = self->frame_buffer_size;
+ if (single_channel_output) {
+ *spacing = self->channel_count;
+ } else {
+ *spacing = 1;
+ }
+}
+
+float common_hal_audiomp3_mp3file_get_rms_level(audiomp3_mp3file_obj_t *self) {
+ float sumsq = 0.f;
+ // Assumes no DC component to the audio. Is that a safe assumption?
+ int16_t *buffer = (int16_t *)(void *)self->buffers[self->buffer_index];
+ for (size_t i = 0; i < self->frame_buffer_size / sizeof(int16_t); i++) {
+ sumsq += (float)buffer[i] * buffer[i];
+ }
+ return sqrtf(sumsq) / (self->frame_buffer_size / sizeof(int16_t));
+}
+
+uint32_t common_hal_audiomp3_mp3file_get_samples_decoded(audiomp3_mp3file_obj_t *self) {
+ return self->samples_decoded;
+}
diff --git a/circuitpython/shared-module/audiomp3/MP3Decoder.h b/circuitpython/shared-module/audiomp3/MP3Decoder.h
new file mode 100644
index 0000000..17616d4
--- /dev/null
+++ b/circuitpython/shared-module/audiomp3/MP3Decoder.h
@@ -0,0 +1,78 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Scott Shawcroft
+ * Copyright (c) 2019 Jeff Epler 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.
+ */
+
+#ifndef MICROPY_INCLUDED_SHARED_MODULE_AUDIOIO_MP3FILE_H
+#define MICROPY_INCLUDED_SHARED_MODULE_AUDIOIO_MP3FILE_H
+
+#include "supervisor/background_callback.h"
+#include "extmod/vfs_fat.h"
+#include "py/obj.h"
+
+#include "shared-module/audiocore/__init__.h"
+
+typedef struct {
+ mp_obj_base_t base;
+ struct _MP3DecInfo *decoder;
+ background_callback_t inbuf_fill_cb;
+ uint8_t *inbuf;
+ uint32_t inbuf_length;
+ uint32_t inbuf_offset;
+ int16_t *buffers[2];
+ uint32_t len;
+ uint32_t frame_buffer_size;
+
+ uint32_t sample_rate;
+ pyb_file_obj_t *file;
+
+ uint8_t buffer_index;
+ uint8_t channel_count;
+ bool eof;
+
+ int8_t other_channel;
+ int8_t other_buffer_index;
+
+ uint32_t samples_decoded;
+} audiomp3_mp3file_obj_t;
+
+// These are not available from Python because it may be called in an interrupt.
+void audiomp3_mp3file_reset_buffer(audiomp3_mp3file_obj_t *self,
+ bool single_channel_output,
+ uint8_t channel);
+audioio_get_buffer_result_t audiomp3_mp3file_get_buffer(audiomp3_mp3file_obj_t *self,
+ bool single_channel_output,
+ uint8_t channel,
+ uint8_t **buffer,
+ uint32_t *buffer_length); // length in bytes
+void audiomp3_mp3file_get_buffer_structure(audiomp3_mp3file_obj_t *self, bool single_channel_output,
+ bool *single_buffer, bool *samples_signed,
+ uint32_t *max_buffer_length, uint8_t *spacing);
+
+float audiomp3_mp3file_get_rms_level(audiomp3_mp3file_obj_t *self);
+
+uint32_t common_hal_audiomp3_mp3file_get_samples_decoded(audiomp3_mp3file_obj_t *self);
+
+#endif // MICROPY_INCLUDED_SHARED_MODULE_AUDIOIO_MP3FILE_H
diff --git a/circuitpython/shared-module/audiomp3/__init__.c b/circuitpython/shared-module/audiomp3/__init__.c
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/circuitpython/shared-module/audiomp3/__init__.c
diff --git a/circuitpython/shared-module/audiomp3/__init__.h b/circuitpython/shared-module/audiomp3/__init__.h
new file mode 100644
index 0000000..e7b1f3a
--- /dev/null
+++ b/circuitpython/shared-module/audiomp3/__init__.h
@@ -0,0 +1,30 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Jeff Epler 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.
+ */
+
+#ifndef MICROPY_INCLUDED_SHARED_MODULE_AUDIOMP3__INIT__H
+#define MICROPY_INCLUDED_SHARED_MODULE_AUDIOMP3__INIT__H
+
+#endif // MICROPY_INCLUDED_SHARED_MODULE_AUDIOMP3__INIT__H
diff --git a/circuitpython/shared-module/audiopwmio/__init__.c b/circuitpython/shared-module/audiopwmio/__init__.c
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/circuitpython/shared-module/audiopwmio/__init__.c
diff --git a/circuitpython/shared-module/audiopwmio/__init__.h b/circuitpython/shared-module/audiopwmio/__init__.h
new file mode 100644
index 0000000..964e4aa
--- /dev/null
+++ b/circuitpython/shared-module/audiopwmio/__init__.h
@@ -0,0 +1,32 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * 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.
+ */
+
+#ifndef MICROPY_INCLUDED_SHARED_MODULE_AUDIOPWMIO__INIT__H
+#define MICROPY_INCLUDED_SHARED_MODULE_AUDIOPWMIO__INIT__H
+
+#include "shared-module/audiocore/__init__.h"
+
+#endif // MICROPY_INCLUDED_SHARED_MODULE_AUDIOPWMIO__INIT__H
diff --git a/circuitpython/shared-module/bitbangio/I2C.c b/circuitpython/shared-module/bitbangio/I2C.c
new file mode 100644
index 0000000..eb897e1
--- /dev/null
+++ b/circuitpython/shared-module/bitbangio/I2C.c
@@ -0,0 +1,256 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * SPDX-FileCopyrightText: Copyright (c) 2016 Damien P. George, Scott Shawcroft
+ *
+ * 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.
+ */
+
+#include "shared-bindings/bitbangio/I2C.h"
+#include "py/mperrno.h"
+#include "py/obj.h"
+#include "py/runtime.h"
+
+#include "common-hal/microcontroller/Pin.h"
+#include "shared-bindings/microcontroller/__init__.h"
+#include "shared-bindings/digitalio/DigitalInOut.h"
+#include "supervisor/shared/translate.h"
+
+STATIC void delay(bitbangio_i2c_obj_t *self) {
+ // We need to use an accurate delay to get acceptable I2C
+ // speeds (eg 1us should be not much more than 1us).
+ common_hal_mcu_delay_us(self->us_delay);
+}
+
+STATIC void scl_low(bitbangio_i2c_obj_t *self) {
+ common_hal_digitalio_digitalinout_set_value(&self->scl, false);
+}
+
+STATIC void scl_release(bitbangio_i2c_obj_t *self) {
+ common_hal_digitalio_digitalinout_set_value(&self->scl, true);
+ uint32_t count = self->us_timeout;
+ delay(self);
+ // For clock stretching, wait for the SCL pin to be released, with timeout.
+ common_hal_digitalio_digitalinout_switch_to_input(&self->scl, PULL_UP);
+ for (; !common_hal_digitalio_digitalinout_get_value(&self->scl) && count; --count) {
+ common_hal_mcu_delay_us(1);
+ }
+ common_hal_digitalio_digitalinout_switch_to_output(&self->scl, true, DRIVE_MODE_OPEN_DRAIN);
+ // raise exception on timeout
+ if (count == 0) {
+ mp_raise_msg(&mp_type_TimeoutError, translate("Clock stretch too long"));
+ }
+}
+
+STATIC void sda_low(bitbangio_i2c_obj_t *self) {
+ common_hal_digitalio_digitalinout_set_value(&self->sda, false);
+}
+
+STATIC void sda_release(bitbangio_i2c_obj_t *self) {
+ common_hal_digitalio_digitalinout_set_value(&self->sda, true);
+}
+
+STATIC bool sda_read(bitbangio_i2c_obj_t *self) {
+ common_hal_digitalio_digitalinout_switch_to_input(&self->sda, PULL_UP);
+ bool value = common_hal_digitalio_digitalinout_get_value(&self->sda);
+ common_hal_digitalio_digitalinout_switch_to_output(&self->sda, true, DRIVE_MODE_OPEN_DRAIN);
+ return value;
+}
+
+STATIC void start(bitbangio_i2c_obj_t *self) {
+ sda_release(self);
+ delay(self);
+ scl_release(self);
+ sda_low(self);
+ delay(self);
+}
+
+STATIC void stop(bitbangio_i2c_obj_t *self) {
+ delay(self);
+ sda_low(self);
+ delay(self);
+ scl_release(self);
+ sda_release(self);
+ delay(self);
+}
+
+STATIC int write_byte(bitbangio_i2c_obj_t *self, uint8_t val) {
+ delay(self);
+ scl_low(self);
+
+ for (int i = 7; i >= 0; i--) {
+ if ((val >> i) & 1) {
+ sda_release(self);
+ } else {
+ sda_low(self);
+ }
+ delay(self);
+ scl_release(self);
+ scl_low(self);
+ }
+
+ sda_release(self);
+ delay(self);
+ scl_release(self);
+
+ int ret = sda_read(self);
+ delay(self);
+ scl_low(self);
+
+ return !ret;
+}
+
+STATIC bool read_byte(bitbangio_i2c_obj_t *self, uint8_t *val, bool ack) {
+ delay(self);
+ scl_low(self);
+ delay(self);
+
+ uint8_t data = 0;
+ for (int i = 7; i >= 0; i--) {
+ scl_release(self);
+ data = (data << 1) | sda_read(self);
+ scl_low(self);
+ delay(self);
+ }
+ *val = data;
+
+ // send ack/nack bit
+ if (ack) {
+ sda_low(self);
+ }
+ delay(self);
+ scl_release(self);
+ scl_low(self);
+ sda_release(self);
+
+ return true;
+}
+
+void shared_module_bitbangio_i2c_construct(bitbangio_i2c_obj_t *self,
+ const mcu_pin_obj_t *scl,
+ const mcu_pin_obj_t *sda,
+ uint32_t frequency,
+ uint32_t us_timeout) {
+
+ self->us_timeout = us_timeout;
+ self->us_delay = 500000 / frequency;
+ if (self->us_delay == 0) {
+ self->us_delay = 1;
+ }
+ digitalinout_result_t result = common_hal_digitalio_digitalinout_construct(&self->scl, scl);
+ if (result != DIGITALINOUT_OK) {
+ return;
+ }
+ result = common_hal_digitalio_digitalinout_construct(&self->sda, sda);
+ if (result != DIGITALINOUT_OK) {
+ common_hal_digitalio_digitalinout_deinit(&self->scl);
+ return;
+ }
+ common_hal_digitalio_digitalinout_switch_to_output(&self->scl, true, DRIVE_MODE_OPEN_DRAIN);
+ common_hal_digitalio_digitalinout_switch_to_output(&self->sda, true, DRIVE_MODE_OPEN_DRAIN);
+
+ stop(self);
+}
+
+bool shared_module_bitbangio_i2c_deinited(bitbangio_i2c_obj_t *self) {
+ // If one is deinited, both will be.
+ return common_hal_digitalio_digitalinout_deinited(&self->scl);
+}
+
+void shared_module_bitbangio_i2c_deinit(bitbangio_i2c_obj_t *self) {
+ if (shared_module_bitbangio_i2c_deinited(self)) {
+ return;
+ }
+ common_hal_digitalio_digitalinout_deinit(&self->scl);
+ common_hal_digitalio_digitalinout_deinit(&self->sda);
+}
+
+bool shared_module_bitbangio_i2c_try_lock(bitbangio_i2c_obj_t *self) {
+ bool success = false;
+ common_hal_mcu_disable_interrupts();
+ if (!self->locked) {
+ self->locked = true;
+ success = true;
+ }
+ common_hal_mcu_enable_interrupts();
+ return success;
+}
+
+bool shared_module_bitbangio_i2c_has_lock(bitbangio_i2c_obj_t *self) {
+ return self->locked;
+}
+
+void shared_module_bitbangio_i2c_unlock(bitbangio_i2c_obj_t *self) {
+ self->locked = false;
+}
+
+bool shared_module_bitbangio_i2c_probe(bitbangio_i2c_obj_t *self, uint8_t addr) {
+ start(self);
+ bool ok = write_byte(self, addr << 1);
+ stop(self);
+ return ok;
+}
+
+uint8_t shared_module_bitbangio_i2c_write(bitbangio_i2c_obj_t *self, uint16_t addr,
+ const uint8_t *data, size_t len, bool transmit_stop_bit) {
+ // start the I2C transaction
+ start(self);
+ uint8_t status = 0;
+ if (!write_byte(self, addr << 1)) {
+ status = MP_ENODEV;
+ }
+
+ if (status == 0) {
+ for (uint32_t i = 0; i < len; i++) {
+ if (!write_byte(self, data[i])) {
+ status = MP_EIO;
+ break;
+ }
+ }
+ }
+
+ if (transmit_stop_bit) {
+ stop(self);
+ }
+ return status;
+}
+
+uint8_t shared_module_bitbangio_i2c_read(bitbangio_i2c_obj_t *self, uint16_t addr,
+ uint8_t *data, size_t len) {
+ // start the I2C transaction
+ start(self);
+ uint8_t status = 0;
+ if (!write_byte(self, (addr << 1) | 1)) {
+ status = MP_ENODEV;
+ }
+
+ if (status == 0) {
+ for (uint32_t i = 0; i < len; i++) {
+ if (!read_byte(self, data + i, i < len - 1)) {
+ status = MP_EIO;
+ break;
+ }
+ }
+ }
+
+ stop(self);
+ return status;
+}
diff --git a/circuitpython/shared-module/bitbangio/I2C.h b/circuitpython/shared-module/bitbangio/I2C.h
new file mode 100644
index 0000000..763c03a
--- /dev/null
+++ b/circuitpython/shared-module/bitbangio/I2C.h
@@ -0,0 +1,43 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Scott Shawcroft
+ *
+ * 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.
+ */
+
+#ifndef MICROPY_INCLUDED_SHARED_MODULE_BITBANGIO_I2C_H
+#define MICROPY_INCLUDED_SHARED_MODULE_BITBANGIO_I2C_H
+
+#include "common-hal/digitalio/DigitalInOut.h"
+
+#include "py/obj.h"
+
+typedef struct {
+ mp_obj_base_t base;
+ digitalio_digitalinout_obj_t scl;
+ digitalio_digitalinout_obj_t sda;
+ uint32_t us_delay;
+ uint32_t us_timeout;
+ volatile bool locked;
+} bitbangio_i2c_obj_t;
+
+#endif // MICROPY_INCLUDED_SHARED_MODULE_BITBANGIO_I2C_H
diff --git a/circuitpython/shared-module/bitbangio/SPI.c b/circuitpython/shared-module/bitbangio/SPI.c
new file mode 100644
index 0000000..6551196
--- /dev/null
+++ b/circuitpython/shared-module/bitbangio/SPI.c
@@ -0,0 +1,309 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * SPDX-FileCopyrightText: Copyright (c) 2013, 2014 Damien P. George
+ *
+ * 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.
+ */
+
+#include "py/mpconfig.h"
+#include "py/obj.h"
+#include "py/runtime.h"
+
+#include "common-hal/microcontroller/Pin.h"
+#include "shared-bindings/bitbangio/SPI.h"
+#include "shared-bindings/digitalio/DigitalInOut.h"
+#include "shared-bindings/microcontroller/__init__.h"
+#include "supervisor/shared/translate.h"
+
+#define MAX_BAUDRATE (common_hal_mcu_get_clock_frequency() / 48)
+
+void shared_module_bitbangio_spi_construct(bitbangio_spi_obj_t *self,
+ const mcu_pin_obj_t *clock, const mcu_pin_obj_t *mosi,
+ const mcu_pin_obj_t *miso) {
+ digitalinout_result_t result = common_hal_digitalio_digitalinout_construct(&self->clock, clock);
+ if (result != DIGITALINOUT_OK) {
+ mp_raise_ValueError(translate("Clock pin init failed."));
+ }
+ common_hal_digitalio_digitalinout_switch_to_output(&self->clock, self->polarity == 1, DRIVE_MODE_PUSH_PULL);
+
+ if (mosi != NULL) {
+ result = common_hal_digitalio_digitalinout_construct(&self->mosi, mosi);
+ if (result != DIGITALINOUT_OK) {
+ common_hal_digitalio_digitalinout_deinit(&self->clock);
+ mp_raise_ValueError(translate("MOSI pin init failed."));
+ }
+ self->has_mosi = true;
+ common_hal_digitalio_digitalinout_switch_to_output(&self->mosi, false, DRIVE_MODE_PUSH_PULL);
+ }
+
+ if (miso != NULL) {
+ // Starts out as input by default, no need to change.
+ result = common_hal_digitalio_digitalinout_construct(&self->miso, miso);
+ if (result != DIGITALINOUT_OK) {
+ common_hal_digitalio_digitalinout_deinit(&self->clock);
+ if (mosi != NULL) {
+ common_hal_digitalio_digitalinout_deinit(&self->mosi);
+ }
+ mp_raise_ValueError(translate("MISO pin init failed."));
+ }
+ self->has_miso = true;
+ }
+ self->delay_half = 5;
+ self->polarity = 0;
+ self->phase = 0;
+}
+
+bool shared_module_bitbangio_spi_deinited(bitbangio_spi_obj_t *self) {
+ return common_hal_digitalio_digitalinout_deinited(&self->clock);
+}
+
+void shared_module_bitbangio_spi_deinit(bitbangio_spi_obj_t *self) {
+ if (shared_module_bitbangio_spi_deinited(self)) {
+ return;
+ }
+ common_hal_digitalio_digitalinout_deinit(&self->clock);
+ if (self->has_mosi) {
+ common_hal_digitalio_digitalinout_deinit(&self->mosi);
+ }
+ if (self->has_miso) {
+ common_hal_digitalio_digitalinout_deinit(&self->miso);
+ }
+}
+
+void shared_module_bitbangio_spi_configure(bitbangio_spi_obj_t *self,
+ uint32_t baudrate, uint8_t polarity, uint8_t phase, uint8_t bits) {
+ self->delay_half = 500000 / baudrate;
+ // round delay_half up so that: actual_baudrate <= requested_baudrate
+ if (500000 % baudrate != 0) {
+ self->delay_half += 1;
+ }
+
+ self->polarity = polarity;
+ self->phase = phase;
+}
+
+bool shared_module_bitbangio_spi_try_lock(bitbangio_spi_obj_t *self) {
+ bool success = false;
+ common_hal_mcu_disable_interrupts();
+ if (!self->locked) {
+ self->locked = true;
+ success = true;
+ }
+ common_hal_mcu_enable_interrupts();
+ return success;
+}
+
+bool shared_module_bitbangio_spi_has_lock(bitbangio_spi_obj_t *self) {
+ return self->locked;
+}
+
+void shared_module_bitbangio_spi_unlock(bitbangio_spi_obj_t *self) {
+ self->locked = false;
+}
+
+// Writes out the given data.
+bool shared_module_bitbangio_spi_write(bitbangio_spi_obj_t *self, const uint8_t *data, size_t len) {
+ if (len > 0 && !self->has_mosi) {
+ mp_raise_ValueError(translate("Cannot write without MOSI pin."));
+ }
+ uint32_t delay_half = self->delay_half;
+
+ // only MSB transfer is implemented
+
+ // If a port defines MICROPY_PY_MACHINE_SPI_MIN_DELAY, and the configured
+ // delay_half is equal to this value, then the software SPI implementation
+ // will run as fast as possible, limited only by CPU speed and GPIO time.
+ #ifdef MICROPY_PY_MACHINE_SPI_MIN_DELAY
+ if (delay_half <= MICROPY_PY_MACHINE_SPI_MIN_DELAY) {
+ for (size_t i = 0; i < len; ++i) {
+ uint8_t data_out = data[i];
+ for (int j = 0; j < 8; ++j, data_out <<= 1) {
+ common_hal_digitalio_digitalinout_set_value(&self->mosi, (data_out >> 7) & 1);
+ common_hal_digitalio_digitalinout_set_value(&self->clock, 1 - self->polarity);
+ common_hal_digitalio_digitalinout_set_value(&self->clock, self->polarity);
+ }
+ if (dest != NULL) {
+ dest[i] = data_in;
+ }
+ }
+ return true;
+ }
+ #endif
+
+ for (size_t i = 0; i < len; ++i) {
+ uint8_t data_out = data[i];
+ for (int j = 0; j < 8; ++j, data_out <<= 1) {
+ common_hal_digitalio_digitalinout_set_value(&self->mosi, (data_out >> 7) & 1);
+ if (self->phase == 0) {
+ common_hal_mcu_delay_us(delay_half);
+ common_hal_digitalio_digitalinout_set_value(&self->clock, 1 - self->polarity);
+ common_hal_mcu_delay_us(delay_half);
+ common_hal_digitalio_digitalinout_set_value(&self->clock, self->polarity);
+ } else {
+ common_hal_digitalio_digitalinout_set_value(&self->clock, 1 - self->polarity);
+ common_hal_mcu_delay_us(delay_half);
+ common_hal_digitalio_digitalinout_set_value(&self->clock, self->polarity);
+ common_hal_mcu_delay_us(delay_half);
+ }
+ }
+
+ // Some ports need a regular callback, but probably we don't need
+ // to do this every byte, or even at all.
+ #ifdef MICROPY_EVENT_POLL_HOOK
+ MICROPY_EVENT_POLL_HOOK;
+ #endif
+ }
+ return true;
+}
+
+// Reads in len bytes while outputting zeroes.
+bool shared_module_bitbangio_spi_read(bitbangio_spi_obj_t *self, uint8_t *data, size_t len, uint8_t write_data) {
+ if (len > 0 && !self->has_miso) {
+ mp_raise_ValueError(translate("Cannot read without MISO pin."));
+ }
+
+ uint32_t delay_half = self->delay_half;
+
+ // only MSB transfer is implemented
+
+ // If a port defines MICROPY_PY_MACHINE_SPI_MIN_DELAY, and the configured
+ // delay_half is equal to this value, then the software SPI implementation
+ // will run as fast as possible, limited only by CPU speed and GPIO time.
+ #ifdef MICROPY_PY_MACHINE_SPI_MIN_DELAY
+ if (delay_half <= MICROPY_PY_MACHINE_SPI_MIN_DELAY) {
+ // Clock out zeroes while we read.
+ if (self->has_mosi) {
+ common_hal_digitalio_digitalinout_set_value(&self->mosi, false);
+ }
+ for (size_t i = 0; i < len; ++i) {
+ uint8_t data_in = 0;
+ for (int j = 0; j < 8; ++j, data_out <<= 1) {
+ common_hal_digitalio_digitalinout_set_value(&self->clock, 1 - self->polarity);
+ data_in = (data_in << 1) | common_hal_digitalio_digitalinout_get_value(&self->miso);
+ common_hal_digitalio_digitalinout_set_value(&self->clock, self->polarity);
+ }
+ data[i] = data_in;
+ }
+ return true;
+ }
+ #endif
+ if (self->has_mosi) {
+ common_hal_digitalio_digitalinout_set_value(&self->mosi, false);
+ }
+ for (size_t i = 0; i < len; ++i) {
+ uint8_t data_out = write_data;
+ uint8_t data_in = 0;
+ for (int j = 0; j < 8; ++j, data_out <<= 1) {
+ if (self->has_mosi) {
+ common_hal_digitalio_digitalinout_set_value(&self->mosi, (data_out >> 7) & 1);
+ }
+ if (self->phase == 0) {
+ common_hal_mcu_delay_us(delay_half);
+ common_hal_digitalio_digitalinout_set_value(&self->clock, 1 - self->polarity);
+ } else {
+ common_hal_digitalio_digitalinout_set_value(&self->clock, 1 - self->polarity);
+ common_hal_mcu_delay_us(delay_half);
+ }
+ data_in = (data_in << 1) | common_hal_digitalio_digitalinout_get_value(&self->miso);
+ if (self->phase == 0) {
+ common_hal_mcu_delay_us(delay_half);
+ common_hal_digitalio_digitalinout_set_value(&self->clock, self->polarity);
+ } else {
+ common_hal_digitalio_digitalinout_set_value(&self->clock, self->polarity);
+ common_hal_mcu_delay_us(delay_half);
+ }
+ }
+ data[i] = data_in;
+
+ // Some ports need a regular callback, but probably we don't need
+ // to do this every byte, or even at all.
+ #ifdef MICROPY_EVENT_POLL_HOOK
+ MICROPY_EVENT_POLL_HOOK;
+ #endif
+ }
+ return true;
+}
+
+// transfer
+bool shared_module_bitbangio_spi_transfer(bitbangio_spi_obj_t *self, const uint8_t *dout, uint8_t *din, size_t len) {
+ if (len > 0 && (!self->has_mosi || !self->has_miso)) {
+ mp_raise_ValueError(translate("Cannot transfer without MOSI and MISO pins."));
+ }
+ uint32_t delay_half = self->delay_half;
+
+ // only MSB transfer is implemented
+
+ // If a port defines MICROPY_PY_MACHINE_SPI_MIN_DELAY, and the configured
+ // delay_half is equal to this value, then the software SPI implementation
+ // will run as fast as possible, limited only by CPU speed and GPIO time.
+ #ifdef MICROPY_PY_MACHINE_SPI_MIN_DELAY
+ if (delay_half <= MICROPY_PY_MACHINE_SPI_MIN_DELAY) {
+ for (size_t i = 0; i < len; ++i) {
+ uint8_t data_out = dout[i];
+ uint8_t data_in = 0;
+ for (int j = 0; j < 8; ++j, data_out <<= 1) {
+ common_hal_digitalio_digitalinout_set_value(&self->mosi, (data_out >> 7) & 1);
+ common_hal_digitalio_digitalinout_set_value(&self->clock, 1 - self->polarity);
+ data_in = (data_in << 1) | common_hal_digitalio_digitalinout_get_value(&self->miso);
+ common_hal_digitalio_digitalinout_set_value(&self->clock, self->polarity);
+ }
+ din[i] = data_in;
+
+ if (dest != NULL) {
+ dest[i] = data_in;
+ }
+ }
+ return true;
+ }
+ #endif
+
+ for (size_t i = 0; i < len; ++i) {
+ uint8_t data_out = dout[i];
+ uint8_t data_in = 0;
+ for (int j = 0; j < 8; ++j, data_out <<= 1) {
+ common_hal_digitalio_digitalinout_set_value(&self->mosi, (data_out >> 7) & 1);
+ if (self->phase == 0) {
+ common_hal_mcu_delay_us(delay_half);
+ common_hal_digitalio_digitalinout_set_value(&self->clock, 1 - self->polarity);
+ } else {
+ common_hal_digitalio_digitalinout_set_value(&self->clock, 1 - self->polarity);
+ common_hal_mcu_delay_us(delay_half);
+ }
+ data_in = (data_in << 1) | common_hal_digitalio_digitalinout_get_value(&self->miso);
+ if (self->phase == 0) {
+ common_hal_mcu_delay_us(delay_half);
+ common_hal_digitalio_digitalinout_set_value(&self->clock, self->polarity);
+ } else {
+ common_hal_digitalio_digitalinout_set_value(&self->clock, self->polarity);
+ common_hal_mcu_delay_us(delay_half);
+ }
+ }
+ din[i] = data_in;
+
+ // Some ports need a regular callback, but probably we don't need
+ // to do this every byte, or even at all.
+ #ifdef MICROPY_EVENT_POLL_HOOK
+ MICROPY_EVENT_POLL_HOOK;
+ #endif
+ }
+ return true;
+}
diff --git a/circuitpython/shared-module/bitbangio/SPI.h b/circuitpython/shared-module/bitbangio/SPI.h
new file mode 100644
index 0000000..cc71a03
--- /dev/null
+++ b/circuitpython/shared-module/bitbangio/SPI.h
@@ -0,0 +1,47 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Scott Shawcroft
+ *
+ * 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.
+ */
+
+#ifndef MICROPY_INCLUDED_SHARED_MODULE_BITBANGIO_SPI_H
+#define MICROPY_INCLUDED_SHARED_MODULE_BITBANGIO_SPI_H
+
+#include "common-hal/digitalio/DigitalInOut.h"
+
+#include "py/obj.h"
+
+typedef struct {
+ mp_obj_base_t base;
+ digitalio_digitalinout_obj_t clock;
+ digitalio_digitalinout_obj_t mosi;
+ digitalio_digitalinout_obj_t miso;
+ uint32_t delay_half;
+ bool has_miso : 1;
+ bool has_mosi : 1;
+ uint8_t polarity : 1;
+ uint8_t phase : 1;
+ volatile bool locked : 1;
+} bitbangio_spi_obj_t;
+
+#endif // MICROPY_INCLUDED_SHARED_MODULE_BITBANGIO_SPI_H
diff --git a/circuitpython/shared-module/bitbangio/__init__.c b/circuitpython/shared-module/bitbangio/__init__.c
new file mode 100644
index 0000000..674343c
--- /dev/null
+++ b/circuitpython/shared-module/bitbangio/__init__.c
@@ -0,0 +1,27 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Scott Shawcroft
+ *
+ * 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.
+ */
+
+// Nothing now.
diff --git a/circuitpython/shared-module/bitmaptools/__init__.c b/circuitpython/shared-module/bitmaptools/__init__.c
new file mode 100644
index 0000000..4c73d8f
--- /dev/null
+++ b/circuitpython/shared-module/bitmaptools/__init__.c
@@ -0,0 +1,859 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 Kevin Matocha
+ *
+ * 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.
+ */
+
+#include "shared-bindings/bitmaptools/__init__.h"
+#include "shared-bindings/displayio/Bitmap.h"
+#include "shared-bindings/displayio/Palette.h"
+#include "shared-bindings/displayio/ColorConverter.h"
+#include "shared-module/displayio/Bitmap.h"
+
+#include "py/mperrno.h"
+#include "py/runtime.h"
+#include "py/stream.h"
+
+#include <math.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+void common_hal_bitmaptools_rotozoom(displayio_bitmap_t *self, int16_t ox, int16_t oy,
+ int16_t dest_clip0_x, int16_t dest_clip0_y,
+ int16_t dest_clip1_x, int16_t dest_clip1_y,
+ displayio_bitmap_t *source, int16_t px, int16_t py,
+ int16_t source_clip0_x, int16_t source_clip0_y,
+ int16_t source_clip1_x, int16_t source_clip1_y,
+ mp_float_t angle,
+ mp_float_t scale,
+ uint32_t skip_index, bool skip_index_none) {
+
+ // Copies region from source to the destination bitmap, including rotation,
+ // scaling and clipping of either the source or destination regions
+ //
+ // *self: destination bitmap
+ // ox: the (ox, oy) destination point where the source (px,py) point is placed
+ // oy:
+ // dest_clip0: (x,y) is the corner of the clip window on the destination bitmap
+ // dest_clip1: (x,y) is the other corner of the clip window of the destination bitmap
+ // *source: the source bitmap
+ // px: the (px, py) point of rotation of the source bitmap
+ // py:
+ // source_clip0: (x,y) is the corner of the clip window on the source bitmap
+ // source_clip1: (x,y) is the other of the clip window on the source bitmap
+ // angle: angle of rotation in radians, positive is clockwise
+ // scale: scale factor
+ // skip_index: color index that should be ignored (and not copied over)
+ // skip_index_none: if skip_index_none is True, then all color indexes should be copied
+ // (that is, no color indexes should be skipped)
+
+
+ // Copy complete "source" bitmap into "self" bitmap at location x,y in the "self"
+ // Add a boolean to determine if all values are copied, or only if non-zero
+ // If skip_value is encountered in the source bitmap, it will not be copied.
+ // If skip_value is `None`, then all pixels are copied.
+
+
+ // # Credit from https://github.com/wernsey/bitmap
+ // # MIT License from
+ // # * Copyright (c) 2017 Werner Stoop <wstoop@gmail.com>
+ // #
+ // # *
+ // # * #### `void bm_rotate_blit(Bitmap *dst, int ox, int oy, Bitmap *src, int px, int py, double angle, double scale);`
+ // # *
+ // # * Rotates a source bitmap `src` around a pivot point `px,py` and blits it onto a destination bitmap `dst`.
+ // # *
+ // # * The bitmap is positioned such that the point `px,py` on the source is at the offset `ox,oy` on the destination.
+ // # *
+ // # * The `angle` is clockwise, in radians. The bitmap is also scaled by the factor `scale`.
+ // #
+ // # void bm_rotate_blit(Bitmap *dst, int ox, int oy, Bitmap *src, int px, int py, double angle, double scale);
+
+
+ // # /*
+ // # Reference:
+ // # "Fast Bitmap Rotation and Scaling" By Steven Mortimer, Dr Dobbs' Journal, July 01, 2001
+ // # http://www.drdobbs.com/architecture-and-design/fast-bitmap-rotation-and-scaling/184416337
+ // # See also http://www.efg2.com/Lab/ImageProcessing/RotateScanline.htm
+ // # */
+
+
+ int16_t x,y;
+
+ int16_t minx = dest_clip1_x;
+ int16_t miny = dest_clip1_y;
+ int16_t maxx = dest_clip0_x;
+ int16_t maxy = dest_clip0_y;
+
+ mp_float_t sinAngle = MICROPY_FLOAT_C_FUN(sin)(angle);
+ mp_float_t cosAngle = MICROPY_FLOAT_C_FUN(cos)(angle);
+
+ mp_float_t dx, dy;
+
+ /* Compute the position of where each corner on the source bitmap
+ will be on the destination to get a bounding box for scanning */
+ dx = -cosAngle * px * scale + sinAngle * py * scale + ox;
+ dy = -sinAngle * px * scale - cosAngle * py * scale + oy;
+ if (dx < minx) {
+ minx = (int16_t)dx;
+ }
+ if (dx > maxx) {
+ maxx = (int16_t)dx;
+ }
+ if (dy < miny) {
+ miny = (int16_t)dy;
+ }
+ if (dy > maxy) {
+ maxy = (int16_t)dy;
+ }
+
+ dx = cosAngle * (source->width - px) * scale + sinAngle * py * scale + ox;
+ dy = sinAngle * (source->width - px) * scale - cosAngle * py * scale + oy;
+ if (dx < minx) {
+ minx = (int16_t)dx;
+ }
+ if (dx > maxx) {
+ maxx = (int16_t)dx;
+ }
+ if (dy < miny) {
+ miny = (int16_t)dy;
+ }
+ if (dy > maxy) {
+ maxy = (int16_t)dy;
+ }
+
+ dx = cosAngle * (source->width - px) * scale - sinAngle * (source->height - py) * scale + ox;
+ dy = sinAngle * (source->width - px) * scale + cosAngle * (source->height - py) * scale + oy;
+ if (dx < minx) {
+ minx = (int16_t)dx;
+ }
+ if (dx > maxx) {
+ maxx = (int16_t)dx;
+ }
+ if (dy < miny) {
+ miny = (int16_t)dy;
+ }
+ if (dy > maxy) {
+ maxy = (int16_t)dy;
+ }
+
+ dx = -cosAngle * px * scale - sinAngle * (source->height - py) * scale + ox;
+ dy = -sinAngle * px * scale + cosAngle * (source->height - py) * scale + oy;
+ if (dx < minx) {
+ minx = (int16_t)dx;
+ }
+ if (dx > maxx) {
+ maxx = (int16_t)dx;
+ }
+ if (dy < miny) {
+ miny = (int16_t)dy;
+ }
+ if (dy > maxy) {
+ maxy = (int16_t)dy;
+ }
+
+ /* Clipping */
+ if (minx < dest_clip0_x) {
+ minx = dest_clip0_x;
+ }
+ if (maxx > dest_clip1_x - 1) {
+ maxx = dest_clip1_x - 1;
+ }
+ if (miny < dest_clip0_y) {
+ miny = dest_clip0_y;
+ }
+ if (maxy > dest_clip1_y - 1) {
+ maxy = dest_clip1_y - 1;
+ }
+
+ mp_float_t dvCol = cosAngle / scale;
+ mp_float_t duCol = sinAngle / scale;
+
+ mp_float_t duRow = dvCol;
+ mp_float_t dvRow = -duCol;
+
+ mp_float_t startu = px - (ox * dvCol + oy * duCol);
+ mp_float_t startv = py - (ox * dvRow + oy * duRow);
+
+ mp_float_t rowu = startu + miny * duCol;
+ mp_float_t rowv = startv + miny * dvCol;
+
+ displayio_area_t dirty_area = {minx, miny, maxx + 1, maxy + 1, NULL};
+ displayio_bitmap_set_dirty_area(self, &dirty_area);
+
+ for (y = miny; y <= maxy; y++) {
+ mp_float_t u = rowu + minx * duRow;
+ mp_float_t v = rowv + minx * dvRow;
+ for (x = minx; x <= maxx; x++) {
+ if (u >= source_clip0_x && u < source_clip1_x && v >= source_clip0_y && v < source_clip1_y) {
+ uint32_t c = common_hal_displayio_bitmap_get_pixel(source, (int)u, (int)v);
+ if ((skip_index_none) || (c != skip_index)) {
+ displayio_bitmap_write_pixel(self, x, y, c);
+ }
+ }
+ u += duRow;
+ v += dvRow;
+ }
+ rowu += duCol;
+ rowv += dvCol;
+ }
+}
+
+void common_hal_bitmaptools_fill_region(displayio_bitmap_t *destination,
+ int16_t x1, int16_t y1,
+ int16_t x2, int16_t y2,
+ uint32_t value) {
+ // writes the value (a bitmap color index) into a bitmap in the specified rectangular region
+ //
+ // input checks should ensure that x1 < x2 and y1 < y2 and are within the bitmap region
+
+ displayio_area_t area = { x1, y1, x2, y2, NULL };
+ displayio_area_canon(&area);
+
+ displayio_area_t bitmap_area = { 0, 0, destination->width, destination->height, NULL };
+ displayio_area_compute_overlap(&area, &bitmap_area, &area);
+
+ // update the dirty rectangle
+ displayio_bitmap_set_dirty_area(destination, &area);
+
+ int16_t x, y;
+ for (x = area.x1; x < area.x2; x++) {
+ for (y = area.y1; y < area.y2; y++) {
+ displayio_bitmap_write_pixel(destination, x, y, value);
+ }
+ }
+}
+
+void common_hal_bitmaptools_boundary_fill(displayio_bitmap_t *destination,
+ int16_t x, int16_t y,
+ uint32_t fill_color_value, uint32_t replaced_color_value) {
+
+ if (fill_color_value == replaced_color_value) {
+ // There is nothing to do
+ return;
+ }
+
+ uint32_t current_point_color_value;
+
+ // the list of points that we'll check
+ mp_obj_t fill_area = mp_obj_new_list(0, NULL);
+
+ // first point is the one user passed in
+ mp_obj_t point[] = { mp_obj_new_int(x), mp_obj_new_int(y) };
+ mp_obj_list_append(
+ fill_area,
+ mp_obj_new_tuple(2, point)
+ );
+
+ int16_t minx = x;
+ int16_t miny = y;
+ int16_t maxx = x;
+ int16_t maxy = y;
+
+ if (replaced_color_value == INT_MAX) {
+ current_point_color_value = common_hal_displayio_bitmap_get_pixel(
+ destination,
+ mp_obj_get_int(point[0]),
+ mp_obj_get_int(point[1]));
+ replaced_color_value = (uint32_t)current_point_color_value;
+ }
+
+ mp_obj_t *fill_points;
+ size_t list_length = 0;
+ mp_obj_list_get(fill_area, &list_length, &fill_points);
+
+ mp_obj_t current_point;
+
+ size_t tuple_len = 0;
+ mp_obj_t *tuple_items;
+
+ int cur_x, cur_y;
+
+ // while there are still points to check
+ while (list_length > 0) {
+ mp_obj_list_get(fill_area, &list_length, &fill_points);
+ current_point = mp_obj_list_pop(fill_area, 0);
+ mp_obj_tuple_get(current_point, &tuple_len, &tuple_items);
+ current_point_color_value = common_hal_displayio_bitmap_get_pixel(
+ destination,
+ mp_obj_get_int(tuple_items[0]),
+ mp_obj_get_int(tuple_items[1]));
+
+ // if the current point is not background color ignore it
+ if (current_point_color_value != replaced_color_value) {
+ mp_obj_list_get(fill_area, &list_length, &fill_points);
+ continue;
+ }
+
+ cur_x = mp_obj_int_get_checked(tuple_items[0]);
+ cur_y = mp_obj_int_get_checked(tuple_items[1]);
+
+ if (cur_x < minx) {
+ minx = (int16_t)cur_x;
+ }
+ if (cur_x > maxx) {
+ maxx = (int16_t)cur_x;
+ }
+ if (cur_y < miny) {
+ miny = (int16_t)cur_y;
+ }
+ if (cur_y > maxy) {
+ maxy = (int16_t)cur_y;
+ }
+
+ // fill the current point with fill color
+ displayio_bitmap_write_pixel(
+ destination,
+ mp_obj_get_int(tuple_items[0]),
+ mp_obj_get_int(tuple_items[1]),
+ fill_color_value);
+
+ // add all 4 surrounding points to the list to check
+
+ // ignore points outside of the bitmap
+ if (mp_obj_int_get_checked(tuple_items[1]) - 1 >= 0) {
+ mp_obj_t above_point[] = {
+ tuple_items[0],
+ MP_OBJ_NEW_SMALL_INT(mp_obj_int_get_checked(tuple_items[1]) - 1)
+ };
+ mp_obj_list_append(
+ fill_area,
+ mp_obj_new_tuple(2, above_point));
+ }
+
+ // ignore points outside of the bitmap
+ if (mp_obj_int_get_checked(tuple_items[0]) - 1 >= 0) {
+ mp_obj_t left_point[] = {
+ MP_OBJ_NEW_SMALL_INT(mp_obj_int_get_checked(tuple_items[0]) - 1),
+ tuple_items[1]
+ };
+ mp_obj_list_append(
+ fill_area,
+ mp_obj_new_tuple(2, left_point));
+ }
+
+ // ignore points outside of the bitmap
+ if (mp_obj_int_get_checked(tuple_items[0]) + 1 < destination->width) {
+ mp_obj_t right_point[] = {
+ MP_OBJ_NEW_SMALL_INT(mp_obj_int_get_checked(tuple_items[0]) + 1),
+ tuple_items[1]
+ };
+ mp_obj_list_append(
+ fill_area,
+ mp_obj_new_tuple(2, right_point));
+ }
+
+ // ignore points outside of the bitmap
+ if (mp_obj_int_get_checked(tuple_items[1]) + 1 < destination->height) {
+ mp_obj_t below_point[] = {
+ tuple_items[0],
+ MP_OBJ_NEW_SMALL_INT(mp_obj_int_get_checked(tuple_items[1]) + 1)
+ };
+ mp_obj_list_append(
+ fill_area,
+ mp_obj_new_tuple(2, below_point));
+ }
+
+ mp_obj_list_get(fill_area, &list_length, &fill_points);
+ }
+
+ // set dirty the area so displayio will draw
+ displayio_area_t area = { minx, miny, maxx + 1, maxy + 1, NULL};
+ displayio_bitmap_set_dirty_area(destination, &area);
+
+}
+
+void common_hal_bitmaptools_draw_line(displayio_bitmap_t *destination,
+ int16_t x0, int16_t y0,
+ int16_t x1, int16_t y1,
+ uint32_t value) {
+
+ //
+ // adapted from Adafruit_CircuitPython_Display_Shapes.Polygon._line
+ //
+
+ // update the dirty rectangle
+ int16_t xbb0, xbb1, ybb0, ybb1;
+ if (x0 < x1) {
+ xbb0 = x0;
+ xbb1 = x1 + 1;
+ } else {
+ xbb0 = x1;
+ xbb1 = x0 + 1;
+ }
+ if (y0 < y1) {
+ ybb0 = y0;
+ ybb1 = y1 + 1;
+ } else {
+ ybb0 = y1;
+ ybb1 = y0 + 1;
+ }
+ displayio_area_t area = { xbb0, ybb0, xbb1, ybb1, NULL };
+ displayio_area_t bitmap_area = { 0, 0, destination->width, destination->height, NULL };
+ displayio_area_compute_overlap(&area, &bitmap_area, &area);
+
+ displayio_bitmap_set_dirty_area(destination, &area);
+
+ int16_t temp, x, y;
+
+ if (x0 == x1) { // vertical line
+ if (y0 > y1) { // ensure y1 > y0
+ temp = y0;
+ y0 = y1;
+ y1 = temp;
+ }
+ for (y = y0; y < (y1 + 1); y++) { // write a horizontal line
+ displayio_bitmap_write_pixel(destination, x0, y, value);
+ }
+ } else if (y0 == y1) { // horizontal line
+ if (x0 > x1) { // ensure y1 > y0
+ temp = x0;
+ x0 = x1;
+ x1 = temp;
+ }
+ for (x = x0; x < (x1 + 1); x++) { // write a horizontal line
+ displayio_bitmap_write_pixel(destination, x, y0, value);
+ }
+ } else {
+ bool steep;
+ steep = (abs(y1 - y0) > abs(x1 - x0));
+
+ if (steep) { // flip x0<->y0 and x1<->y1
+ temp = x0;
+ x0 = y0;
+ y0 = temp;
+ temp = x1;
+ x1 = y1;
+ y1 = temp;
+ }
+
+ if (x0 > x1) { // flip x0<->x1 and y0<->y1
+ temp = x0;
+ x0 = x1;
+ x1 = temp;
+ temp = y0;
+ y0 = y1;
+ y1 = temp;
+ }
+
+ int16_t dx, dy, ystep;
+ dx = x1 - x0;
+ dy = abs(y1 - y0);
+
+ mp_float_t err = dx / 2;
+
+ if (y0 < y1) {
+ ystep = 1;
+ } else {
+ ystep = -1;
+ }
+
+ for (x = x0; x < (x1 + 1); x++) {
+ if (steep) {
+ displayio_bitmap_write_pixel(destination, y0, x, value);
+ } else {
+ displayio_bitmap_write_pixel(destination, x, y0, value);
+ }
+ err -= dy;
+ if (err < 0) {
+ y0 += ystep;
+ err += dx;
+ }
+ }
+ }
+}
+
+void common_hal_bitmaptools_arrayblit(displayio_bitmap_t *self, void *data, int element_size, int x1, int y1, int x2, int y2, bool skip_specified, uint32_t skip_value) {
+ uint32_t mask = (1 << common_hal_displayio_bitmap_get_bits_per_value(self)) - 1;
+
+ for (int y = y1; y < y2; y++) {
+ for (int x = x1; x < x2; x++) {
+ uint32_t value;
+ switch (element_size) {
+ default:
+ case 1:
+ value = *(uint8_t *)data;
+ data = (void *)((uint8_t *)data + 1);
+ break;
+ case 2:
+ value = *(uint16_t *)data;
+ data = (void *)((uint16_t *)data + 1);
+ break;
+ case 4:
+ value = *(uint32_t *)data;
+ data = (void *)((uint32_t *)data + 1);
+ break;
+ }
+ if (!skip_specified || value != skip_value) {
+ displayio_bitmap_write_pixel(self, x, y, value & mask);
+ }
+ }
+ }
+ displayio_area_t area = { x1, y1, x2, y2, NULL };
+ displayio_bitmap_set_dirty_area(self, &area);
+}
+
+void common_hal_bitmaptools_readinto(displayio_bitmap_t *self, mp_obj_t *file, int element_size, int bits_per_pixel, bool reverse_pixels_in_element, bool swap_bytes, bool reverse_rows) {
+ uint32_t mask = (1 << common_hal_displayio_bitmap_get_bits_per_value(self)) - 1;
+
+ const mp_stream_p_t *file_proto = mp_get_stream_raise(file, MP_STREAM_OP_READ);
+
+ displayio_area_t a = {0, 0, self->width, self->height, NULL};
+ displayio_bitmap_set_dirty_area(self, &a);
+
+ size_t elements_per_row = (self->width * bits_per_pixel + element_size * 8 - 1) / (element_size * 8);
+ size_t rowsize = element_size * elements_per_row;
+ size_t rowsize_in_u32 = (rowsize + sizeof(uint32_t) - 1) / sizeof(uint32_t);
+ size_t rowsize_in_u16 = (rowsize + sizeof(uint16_t) - 1) / sizeof(uint16_t);
+
+ for (int y = 0; y < self->height; y++) {
+ uint32_t rowdata32[rowsize_in_u32];
+ uint16_t *rowdata16 = (uint16_t *)rowdata32;
+ uint8_t *rowdata8 = (uint8_t *)rowdata32;
+ const int y_draw = reverse_rows ? (self->height) - 1 - y : y;
+
+
+ int error = 0;
+ mp_uint_t bytes_read = file_proto->read(file, rowdata32, rowsize, &error);
+ if (error) {
+ mp_raise_OSError(error);
+ }
+ if (bytes_read != rowsize) {
+ mp_raise_msg(&mp_type_EOFError, NULL);
+ }
+
+ if (swap_bytes) {
+ switch (element_size) {
+ case 2:
+ for (size_t i = 0; i < rowsize_in_u16; i++) {
+ rowdata16[i] = __builtin_bswap16(rowdata16[i]);
+ }
+ break;
+ case 4:
+ for (size_t i = 0; i < rowsize_in_u32; i++) {
+ rowdata32[i] = __builtin_bswap32(rowdata32[i]);
+ }
+ default:
+ break;
+ }
+ }
+
+ for (int x = 0; x < self->width; x++) {
+ int value = 0;
+ switch (bits_per_pixel) {
+ case 1: {
+ int byte_offset = x / 8;
+ int bit_offset = reverse_pixels_in_element ? (7 - x % 8) : x % 8;
+
+ value = (rowdata8[byte_offset] >> bit_offset) & 1;
+ break;
+ }
+ case 2: {
+ int byte_offset = x / 4;
+ int bit_offset = 2 * (reverse_pixels_in_element ? (3 - x % 4) : x % 4);
+
+ value = (rowdata8[byte_offset] >> bit_offset) & 3;
+ break;
+ }
+ case 4: {
+ int byte_offset = x / 2;
+ int bit_offset = 4 * (reverse_pixels_in_element ? (1 - x % 2) : x % 2);
+
+ value = (rowdata8[byte_offset] >> bit_offset) & 0xf;
+ break;
+ }
+ case 8:
+ value = rowdata8[x];
+ break;
+
+ case 16:
+ value = rowdata16[x];
+ break;
+
+ case 24:
+ value = (rowdata8[x * 3] << 16) | (rowdata8[x * 3 + 1] << 8) | rowdata8[x * 3 + 2];
+ break;
+
+ case 32:
+ value = rowdata32[x];
+ break;
+ }
+ displayio_bitmap_write_pixel(self, x, y_draw, value & mask);
+ }
+ }
+}
+
+typedef struct {
+ uint8_t count; // The number of items in terms[]
+ uint8_t mx; // the maximum of the absolute value of the dx values
+ uint8_t dl; // the scaled dither value applied to the pixel at distance [1,0]
+ struct { // dl is the scaled dither values applied to the pixel at [dx,dy]
+ int8_t dx, dy, dl;
+ } terms[];
+} bitmaptools_dither_algorithm_info_t;
+
+static bitmaptools_dither_algorithm_info_t atkinson = {
+ 4, 2, 256 / 8, {
+ {2, 0, 256 / 8},
+ {-1, 1, 256 / 8},
+ {0, 1, 256 / 8},
+ {0, 2, 256 / 8},
+ }
+};
+
+static bitmaptools_dither_algorithm_info_t floyd_stenberg = {
+ 3, 1, 7 * 256 / 16,
+ {
+ {-1, 1, 3 * 256 / 16},
+ {0, 1, 5 * 256 / 16},
+ {1, 1, 1 * 256 / 16},
+ }
+};
+
+bitmaptools_dither_algorithm_info_t *algorithms[] = {
+ [DITHER_ALGORITHM_ATKINSON] = &atkinson,
+ [DITHER_ALGORITHM_FLOYD_STENBERG] = &floyd_stenberg,
+};
+
+enum {
+ SWAP_BYTES = 1 << 0,
+ SWAP_RB = 1 << 1,
+};
+
+STATIC void fill_row(displayio_bitmap_t *bitmap, int swap, int16_t *luminance_data, int y, int mx) {
+ if (y >= bitmap->height) {
+ return;
+ }
+
+ // zero out padding area
+ for (int i = 0; i < mx; i++) {
+ luminance_data[-mx + i] = 0;
+ luminance_data[bitmap->width + i] = 0;
+ }
+
+ if (bitmap->bits_per_value == 8) {
+ uint8_t *pixel_data = (uint8_t *)(bitmap->data + bitmap->stride * y);
+ for (int x = 0; x < bitmap->width; x++) {
+ *luminance_data++ = *pixel_data++;
+ }
+ } else {
+ uint16_t *pixel_data = (uint16_t *)(bitmap->data + bitmap->stride * y);
+ for (int x = 0; x < bitmap->width; x++) {
+ uint16_t pixel = *pixel_data++;
+ if (swap & SWAP_BYTES) {
+ pixel = __builtin_bswap16(pixel);
+ }
+ int r = (pixel >> 8) & 0xf8;
+ int g = (pixel >> 3) & 0xfc;
+ int b = (pixel << 3) & 0xf8;
+
+ if (swap & SWAP_RB) {
+ uint8_t tmp = r;
+ r = b;
+ b = tmp;
+ }
+
+ // ideal coefficients are around .299, .587, .114 (according to
+ // ppmtopnm), this differs from the 'other' luma-converting
+ // function in circuitpython (why?)
+
+ // we correct for the fact that the input ranges are 0..0xf8 (or
+ // 0xfc) rather than 0x00..0xff
+ // Check: (0xf8 * 78 + 0xfc * 154 + 0xf8 * 29) // 256 == 255
+ *luminance_data++ = (r * 78 + g * 154 + b * 29) / 256;
+ }
+ }
+}
+
+static void write_pixels(displayio_bitmap_t *bitmap, int y, bool *data) {
+ if (bitmap->bits_per_value == 1) {
+ uint32_t *pixel_data = (uint32_t *)(bitmap->data + bitmap->stride * y);
+ for (int i = 0; i < bitmap->stride; i++) {
+ uint32_t p = 0;
+ for (int j = 0; j < 32; j++) {
+ p = (p << 1);
+ if (*data++) {
+ p |= 1;
+ }
+ }
+ *pixel_data++ = p;
+ }
+ } else {
+ uint16_t *pixel_data = (uint16_t *)(bitmap->data + bitmap->stride * y);
+ for (int i = 0; i < bitmap->width; i++) {
+ *pixel_data++ = *data++ ? 65535 : 0;
+ }
+ }
+}
+
+void common_hal_bitmaptools_dither(displayio_bitmap_t *dest_bitmap, displayio_bitmap_t *source_bitmap, displayio_colorspace_t colorspace, bitmaptools_dither_algorithm_t algorithm) {
+ int height = dest_bitmap->height, width = dest_bitmap->width;
+
+ int swap = 0;
+ if (colorspace == DISPLAYIO_COLORSPACE_RGB565_SWAPPED || colorspace == DISPLAYIO_COLORSPACE_BGR565_SWAPPED) {
+ swap |= SWAP_BYTES;
+ }
+ if (colorspace == DISPLAYIO_COLORSPACE_BGR565 || colorspace == DISPLAYIO_COLORSPACE_BGR565_SWAPPED) {
+ swap |= SWAP_RB;
+ }
+
+ bitmaptools_dither_algorithm_info_t *info = algorithms[algorithm];
+ // rowdata holds 3 rows of data. Each one is larger than the input
+ // bitmap's width, beacuse `mx` extra pixels are allocated at the start and
+ // end of the row so that no conditionals are needed when storing the error data.
+ int16_t rowdata[(width + 2 * info->mx) * 3];
+ int16_t *rows[3] = {
+ rowdata + info->mx, rowdata + width + info->mx * 3, rowdata + 2 * width + info->mx * 5
+ };
+ // out holds one output row of pixels, and is padded to be a multiple of 32 so that the 1bpp storage loop can be simplified
+ bool out[(width + 31) / 32 * 32];
+
+ fill_row(source_bitmap, swap, rows[0], 0, info->mx);
+ fill_row(source_bitmap, swap, rows[1], 1, info->mx);
+ fill_row(source_bitmap, swap, rows[2], 2, info->mx);
+
+ int16_t err = 0;
+
+ for (int y = 0; y < height; y++) {
+
+ // Serpentine dither. Going left-to-right...
+ for (int x = 0; x < width; x++) {
+ int32_t pixel_in = rows[0][x] + err;
+ bool pixel_out = pixel_in >= 128;
+ out[x] = pixel_out;
+
+ err = pixel_in - (pixel_out ? 255 : 0);
+
+ for (int i = 0; i < info->count; i++) {
+ int x1 = x + info->terms[i].dx;
+ int dy = info->terms[i].dy;
+
+ rows[dy][x1] = ((info->terms[i].dl * err) / 256) + rows[dy][x1];
+ }
+ err = (err * info->dl) / 256;
+ }
+ write_pixels(dest_bitmap, y, out);
+
+ // Cycle the rows by shuffling pointers, this is faster than copying the data.
+ int16_t *tmp = rows[0];
+ rows[0] = rows[1];
+ rows[1] = rows[2];
+ rows[2] = tmp;
+
+ y++;
+ if (y == height) {
+ break;
+ }
+
+ fill_row(source_bitmap, swap, rows[2], y + 2, info->mx);
+
+ // Serpentine dither. Going right-to-left...
+ for (int x = width; x--;) {
+ int16_t pixel_in = rows[0][x] + err;
+ bool pixel_out = pixel_in >= 128;
+ out[x] = pixel_out;
+ err = pixel_in - (pixel_out ? 255 : 0);
+
+ for (int i = 0; i < info->count; i++) {
+ int x1 = x - info->terms[i].dx;
+ int dy = info->terms[i].dy;
+
+ rows[dy][x1] = ((info->terms[i].dl * err) / 256) + rows[dy][x1];
+ }
+ err = (err * info->dl) / 256;
+ }
+ write_pixels(dest_bitmap, y, out);
+
+ tmp = rows[0];
+ rows[0] = rows[1];
+ rows[1] = rows[2];
+ rows[2] = tmp;
+
+ fill_row(source_bitmap, swap, rows[2], y + 3, info->mx);
+ }
+
+ displayio_area_t a = { 0, 0, width, height, NULL };
+ displayio_bitmap_set_dirty_area(dest_bitmap, &a);
+}
+
+void common_hal_bitmaptools_alphablend(displayio_bitmap_t *dest, displayio_bitmap_t *source1, displayio_bitmap_t *source2, displayio_colorspace_t colorspace, mp_float_t factor1, mp_float_t factor2) {
+ displayio_area_t a = {0, 0, dest->width, dest->height, NULL};
+ displayio_bitmap_set_dirty_area(dest, &a);
+
+ int ifactor1 = (int)(factor1 * 256);
+ int ifactor2 = (int)(factor2 * 256);
+
+ if (colorspace == DISPLAYIO_COLORSPACE_L8) {
+ for (int y = 0; y < dest->height; y++) {
+ uint8_t *dptr = (uint8_t *)(dest->data + y * dest->stride);
+ uint8_t *sptr1 = (uint8_t *)(source1->data + y * source1->stride);
+ uint8_t *sptr2 = (uint8_t *)(source2->data + y * source2->stride);
+ for (int x = 0; x < dest->width; x++) {
+ // This is round(l1*f1 + l2*f2) & clip to range in fixed-point
+ int pixel = (*sptr1++ *ifactor1 + *sptr2++ *ifactor2 + 128) / 256;
+ *dptr++ = MIN(255, MAX(0, pixel));
+ }
+ }
+ } else {
+ bool swap = (colorspace == DISPLAYIO_COLORSPACE_RGB565_SWAPPED) || (colorspace == DISPLAYIO_COLORSPACE_BGR565_SWAPPED);
+ for (int y = 0; y < dest->height; y++) {
+ uint16_t *dptr = (uint16_t *)(dest->data + y * dest->stride);
+ uint16_t *sptr1 = (uint16_t *)(source1->data + y * source1->stride);
+ uint16_t *sptr2 = (uint16_t *)(source2->data + y * source2->stride);
+ for (int x = 0; x < dest->width; x++) {
+ int spix1 = *sptr1++;
+ int spix2 = *sptr2++;
+
+ if (swap) {
+ spix1 = __builtin_bswap16(spix1);
+ spix2 = __builtin_bswap16(spix2);
+ }
+ const int r_mask = 0xf800; // (or b mask, if BGR)
+ const int g_mask = 0x07e0;
+ const int b_mask = 0x001f; // (or r mask, if BGR)
+
+ // This is round(r1*f1 + r2*f2) & clip to range in fixed-point
+ // but avoiding shifting it down to start at bit 0
+ int r = ((spix1 & r_mask) * ifactor1
+ + (spix2 & r_mask) * ifactor2 + r_mask / 2) / 256;
+ r = MIN(r_mask, MAX(0, r)) & r_mask;
+
+ // ditto
+ int g = ((spix1 & g_mask) * ifactor1
+ + (spix2 & g_mask) * ifactor2 + g_mask / 2) / 256;
+ g = MIN(g_mask, MAX(0, g)) & g_mask;
+
+ int b = ((spix1 & b_mask) * ifactor1
+ + (spix2 & b_mask) * ifactor2 + b_mask / 2) / 256;
+ b = MIN(b_mask, MAX(0, b)) & b_mask;
+
+ uint16_t pixel = r | g | b;
+ if (swap) {
+ pixel = __builtin_bswap16(pixel);
+ }
+ *dptr++ = pixel;
+ }
+ }
+ }
+}
diff --git a/circuitpython/shared-module/bitops/__init__.c b/circuitpython/shared-module/bitops/__init__.c
new file mode 100644
index 0000000..7e1cf7d
--- /dev/null
+++ b/circuitpython/shared-module/bitops/__init__.c
@@ -0,0 +1,163 @@
+/*
+ * This file is part of the CircuitPython project, https://github.com/adafruit/circuitpython
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 Jeff Epler 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.
+ */
+
+#include "shared-bindings/bitops/__init__.h"
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "py/mpconfig.h"
+
+// adapted from "Hacker's Delight" - Figure 7-2 Transposing an 8x8-bit matrix
+// basic idea is:
+// > First, treat the 8x8-bit matrix as 16 2x2-bit matrices, and transpose each
+// > of the 16 2x2-bit matrices. Second, treat the matrix as four 2x2 submatrices
+// > whose elements are 2x2-bit matrices and transpose each of the four 2x2
+// > submatrices. Finally, treat the matrix as a 2x2 matrix whose elements are
+// > 4x4-bit matrices, and transpose the 2x2 matrix. These transformations are
+// > illustrated below.
+// We want a different definition of bit/byte order, deal with strides differently, etc.
+// so the code is heavily re-worked compared to the original.
+static void transpose_var(uint32_t *result, const uint8_t *src, int src_stride, int num_strands) {
+ uint32_t x = 0, y = 0, t;
+
+ src += (num_strands - 1) * src_stride;
+
+ switch (num_strands) {
+ case 7:
+ x |= *src << 16;
+ src -= src_stride;
+ MP_FALLTHROUGH;
+ case 6:
+ x |= *src << 8;
+ src -= src_stride;
+ MP_FALLTHROUGH;
+ case 5:
+ x |= *src;
+ src -= src_stride;
+ MP_FALLTHROUGH;
+ case 4:
+ y |= *src << 24;
+ src -= src_stride;
+ MP_FALLTHROUGH;
+ case 3:
+ y |= *src << 16;
+ src -= src_stride;
+ MP_FALLTHROUGH;
+ case 2:
+ y |= *src << 8;
+ src -= src_stride;
+ y |= *src;
+ }
+
+ t = (x ^ (x >> 7)) & 0x00AA00AA;
+ x = x ^ t ^ (t << 7);
+ t = (y ^ (y >> 7)) & 0x00AA00AA;
+ y = y ^ t ^ (t << 7);
+
+ t = (x ^ (x >> 14)) & 0x0000CCCC;
+ x = x ^ t ^ (t << 14);
+ t = (y ^ (y >> 14)) & 0x0000CCCC;
+ y = y ^ t ^ (t << 14);
+
+ t = (x & 0xF0F0F0F0) | ((y >> 4) & 0x0F0F0F0F);
+ y = ((x << 4) & 0xF0F0F0F0) | (y & 0x0F0F0F0F);
+ x = t;
+
+ #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+ x = __builtin_bswap32(x);
+ y = __builtin_bswap32(y);
+ #endif
+ result[0] = x;
+ result[1] = y;
+}
+
+static void transpose_8(uint32_t *result, const uint8_t *src, int src_stride) {
+ uint32_t x, y, t;
+
+ y = *src;
+ src += src_stride;
+ y |= (*src << 8);
+ src += src_stride;
+ y |= (*src << 16);
+ src += src_stride;
+ y |= (*src << 24);
+ src += src_stride;
+ x = *src;
+ src += src_stride;
+ x |= (*src << 8);
+ src += src_stride;
+ x |= (*src << 16);
+ src += src_stride;
+ x |= (*src << 24);
+ src += src_stride;
+
+ t = (x ^ (x >> 7)) & 0x00AA00AA;
+ x = x ^ t ^ (t << 7);
+ t = (y ^ (y >> 7)) & 0x00AA00AA;
+ y = y ^ t ^ (t << 7);
+
+ t = (x ^ (x >> 14)) & 0x0000CCCC;
+ x = x ^ t ^ (t << 14);
+ t = (y ^ (y >> 14)) & 0x0000CCCC;
+ y = y ^ t ^ (t << 14);
+
+ t = (x & 0xF0F0F0F0) | ((y >> 4) & 0x0F0F0F0F);
+ y = ((x << 4) & 0xF0F0F0F0) | (y & 0x0F0F0F0F);
+ x = t;
+
+ #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+ x = __builtin_bswap32(x);
+ y = __builtin_bswap32(y);
+ #endif
+ result[0] = x;
+ result[1] = y;
+}
+
+static void bit_transpose_8(uint32_t *result, const uint8_t *src, size_t src_stride, size_t n) {
+ for (size_t i = 0; i < n; i++) {
+ transpose_8(result, src, src_stride);
+ result += 2;
+ src += 1;
+ }
+}
+
+static void bit_transpose_var(uint32_t *result, const uint8_t *src, size_t src_stride, size_t n, int num_strands) {
+ for (size_t i = 0; i < n; i++) {
+ transpose_var(result, src, src_stride, num_strands);
+ result += 2;
+ src += 1;
+ }
+}
+
+void common_hal_bitops_bit_transpose(uint8_t *result, const uint8_t *src, size_t inlen, size_t num_strands) {
+ if (num_strands == 8) {
+ bit_transpose_8((uint32_t *)(void *)result, src, inlen / 8, inlen / 8);
+ } else {
+ bit_transpose_var((uint32_t *)(void *)result, src, inlen / num_strands, inlen / num_strands, num_strands);
+ }
+}
diff --git a/circuitpython/shared-module/bitops/__init__.h b/circuitpython/shared-module/bitops/__init__.h
new file mode 100644
index 0000000..79de80e
--- /dev/null
+++ b/circuitpython/shared-module/bitops/__init__.h
@@ -0,0 +1,27 @@
+/*
+ * This file is part of the CircuitPython project, https://github.com/adafruit/circuitpython
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 Jeff Epler
+ *
+ * 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.
+ */
+
+#pragma once
diff --git a/circuitpython/shared-module/board/__init__.c b/circuitpython/shared-module/board/__init__.c
new file mode 100644
index 0000000..7bd0d42
--- /dev/null
+++ b/circuitpython/shared-module/board/__init__.c
@@ -0,0 +1,245 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Scott Shawcroft 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.
+ */
+
+#include "shared-bindings/board/__init__.h"
+#include "shared-bindings/microcontroller/Pin.h"
+#include "shared-module/board/__init__.h"
+#include "supervisor/shared/translate.h"
+#include "mpconfigboard.h"
+#include "py/runtime.h"
+
+#if CIRCUITPY_BUSIO
+#include "shared-bindings/busio/I2C.h"
+#include "shared-bindings/busio/SPI.h"
+#include "shared-bindings/busio/UART.h"
+#endif
+
+#if CIRCUITPY_DISPLAYIO
+#include "shared-module/displayio/__init__.h"
+#endif
+
+#if CIRCUITPY_SHARPDISPLAY
+#include "shared-bindings/sharpdisplay/SharpMemoryFramebuffer.h"
+#include "shared-module/sharpdisplay/SharpMemoryFramebuffer.h"
+#endif
+
+#if CIRCUITPY_BOARD_I2C
+// Statically allocate the I2C object so it can live past the end of the heap and into the next VM.
+// That way it can be used by built-in I2CDisplay displays and be accessible through board.I2C().
+
+typedef struct {
+ const mcu_pin_obj_t *scl;
+ const mcu_pin_obj_t *sda;
+} board_i2c_pin_t;
+
+static const board_i2c_pin_t i2c_pin[CIRCUITPY_BOARD_I2C] = CIRCUITPY_BOARD_I2C_PIN;
+static busio_i2c_obj_t i2c_obj[CIRCUITPY_BOARD_I2C];
+static bool i2c_obj_created[CIRCUITPY_BOARD_I2C];
+
+bool common_hal_board_is_i2c(mp_obj_t obj) {
+ for (uint8_t instance = 0; instance < CIRCUITPY_BOARD_I2C; instance++) {
+ if (obj == &i2c_obj[instance]) {
+ return true;
+ }
+ }
+ return false;
+}
+
+mp_obj_t common_hal_board_get_i2c(const mp_int_t instance) {
+ return i2c_obj_created[instance] ? &i2c_obj[instance] : NULL;
+}
+
+mp_obj_t common_hal_board_create_i2c(const mp_int_t instance) {
+ const mp_obj_t singleton = common_hal_board_get_i2c(instance);
+ if (singleton != NULL && !common_hal_busio_i2c_deinited(singleton)) {
+ return singleton;
+ }
+
+ busio_i2c_obj_t *self = &i2c_obj[instance];
+ self->base.type = &busio_i2c_type;
+
+ assert_pin_free(i2c_pin[instance].scl);
+ assert_pin_free(i2c_pin[instance].sda);
+
+ common_hal_busio_i2c_construct(self, i2c_pin[instance].scl, i2c_pin[instance].sda, 100000, 255);
+
+ i2c_obj_created[instance] = true;
+ return &i2c_obj[instance];
+}
+#endif
+
+#if CIRCUITPY_BOARD_SPI
+// Statically allocate the SPI object so it can live past the end of the heap and into the next VM.
+// That way it can be used by built-in FourWire displays and be accessible through board.SPI().
+
+typedef struct {
+ const mcu_pin_obj_t *clock;
+ const mcu_pin_obj_t *mosi;
+ const mcu_pin_obj_t *miso;
+} board_spi_pin_t;
+
+static const board_spi_pin_t spi_pin[CIRCUITPY_BOARD_SPI] = CIRCUITPY_BOARD_SPI_PIN;
+static busio_spi_obj_t spi_obj[CIRCUITPY_BOARD_SPI];
+static bool spi_obj_created[CIRCUITPY_BOARD_SPI];
+
+bool common_hal_board_is_spi(mp_obj_t obj) {
+ for (uint8_t instance = 0; instance < CIRCUITPY_BOARD_SPI; instance++) {
+ if (obj == &spi_obj[instance]) {
+ return true;
+ }
+ }
+ return false;
+}
+
+mp_obj_t common_hal_board_get_spi(const mp_int_t instance) {
+ return spi_obj_created[instance] ? &spi_obj[instance] : NULL;
+}
+
+mp_obj_t common_hal_board_create_spi(const mp_int_t instance) {
+ const mp_obj_t singleton = common_hal_board_get_spi(instance);
+ if (singleton != NULL && !common_hal_busio_spi_deinited(singleton)) {
+ return singleton;
+ }
+
+ busio_spi_obj_t *self = &spi_obj[instance];
+ self->base.type = &busio_spi_type;
+
+ assert_pin_free(spi_pin[instance].clock);
+ assert_pin_free(spi_pin[instance].mosi);
+ assert_pin_free(spi_pin[instance].miso);
+
+ common_hal_busio_spi_construct(self, spi_pin[instance].clock, spi_pin[instance].mosi, spi_pin[instance].miso, false);
+
+ spi_obj_created[instance] = true;
+ return &spi_obj[instance];
+}
+#endif
+
+#if CIRCUITPY_BOARD_UART
+
+typedef struct {
+ const mcu_pin_obj_t *tx;
+ const mcu_pin_obj_t *rx;
+} board_uart_pin_t;
+
+static const board_uart_pin_t uart_pin[CIRCUITPY_BOARD_UART] = CIRCUITPY_BOARD_UART_PIN;
+static busio_uart_obj_t uart_obj[CIRCUITPY_BOARD_UART];
+static bool uart_obj_created[CIRCUITPY_BOARD_UART];
+
+bool common_hal_board_is_uart(mp_obj_t obj) {
+ for (uint8_t instance = 0; instance < CIRCUITPY_BOARD_UART; instance++) {
+ if (obj == &uart_obj[instance]) {
+ return true;
+ }
+ }
+ return false;
+}
+
+mp_obj_t common_hal_board_get_uart(const mp_int_t instance) {
+ return uart_obj_created[instance] ? &uart_obj[instance] : NULL;
+}
+
+mp_obj_t common_hal_board_create_uart(const mp_int_t instance) {
+ const mp_obj_t singleton = common_hal_board_get_uart(instance);
+ if (singleton != NULL && !common_hal_busio_uart_deinited(singleton)) {
+ return singleton;
+ }
+
+ busio_uart_obj_t *self = &uart_obj[instance];
+ self->base.type = &busio_uart_type;
+
+ MP_STATE_VM(board_uart_bus) = &uart_obj;
+
+ assert_pin_free(uart_pin[instance].tx);
+ assert_pin_free(uart_pin[instance].rx);
+
+ common_hal_busio_uart_construct(self, uart_pin[instance].tx, uart_pin[instance].rx,
+ NULL, NULL, NULL, false, 9600, 8, BUSIO_UART_PARITY_NONE, 1, 1.0f, 64, NULL, false);
+
+ uart_obj_created[instance] = true;
+ return &uart_obj[instance];
+}
+#endif
+
+void reset_board_buses(void) {
+ #if CIRCUITPY_BOARD_I2C
+ for (uint8_t instance = 0; instance < CIRCUITPY_BOARD_I2C; instance++) {
+ bool display_using_i2c = false;
+ #if CIRCUITPY_DISPLAYIO
+ for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) {
+ if (displays[i].bus_base.type == &displayio_i2cdisplay_type && displays[i].i2cdisplay_bus.bus == &i2c_obj[instance]) {
+ display_using_i2c = true;
+ break;
+ }
+ }
+ #endif
+ if (i2c_obj_created[instance]) {
+ // make sure I2C lock is not held over a soft reset
+ common_hal_busio_i2c_unlock(&i2c_obj[instance]);
+ if (!display_using_i2c) {
+ common_hal_busio_i2c_deinit(&i2c_obj[instance]);
+ i2c_obj_created[instance] = false;
+ }
+ }
+ }
+ #endif
+ #if CIRCUITPY_BOARD_SPI
+ for (uint8_t instance = 0; instance < CIRCUITPY_BOARD_SPI; instance++) {
+ bool display_using_spi = false;
+ #if CIRCUITPY_DISPLAYIO
+ for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) {
+ mp_const_obj_t bus_type = displays[i].bus_base.type;
+ if (bus_type == &displayio_fourwire_type && displays[i].fourwire_bus.bus == &spi_obj[instance]) {
+ display_using_spi = true;
+ break;
+ }
+ #if CIRCUITPY_SHARPDISPLAY
+ if (displays[i].bus_base.type == &sharpdisplay_framebuffer_type && displays[i].sharpdisplay.bus == &spi_obj[instance]) {
+ display_using_spi = true;
+ break;
+ }
+ #endif
+ }
+ #endif
+ if (spi_obj_created[instance]) {
+ // make sure SPI lock is not held over a soft reset
+ common_hal_busio_spi_unlock(&spi_obj[instance]);
+ if (!display_using_spi) {
+ common_hal_busio_spi_deinit(&spi_obj[instance]);
+ spi_obj_created[instance] = false;
+ }
+ }
+ }
+ #endif
+ #if CIRCUITPY_BOARD_UART
+ for (uint8_t instance = 0; instance < CIRCUITPY_BOARD_UART; instance++) {
+ if (uart_obj_created[instance]) {
+ common_hal_busio_uart_deinit(&uart_obj[instance]);
+ uart_obj_created[instance] = false;
+ }
+ }
+ #endif
+}
diff --git a/circuitpython/shared-module/board/__init__.h b/circuitpython/shared-module/board/__init__.h
new file mode 100644
index 0000000..8b3723b
--- /dev/null
+++ b/circuitpython/shared-module/board/__init__.h
@@ -0,0 +1,32 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Scott Shawcroft 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.
+ */
+
+#ifndef MICROPY_INCLUDED_SHARED_MODULE_BOARD__INIT__H
+#define MICROPY_INCLUDED_SHARED_MODULE_BOARD__INIT__H
+
+void reset_board_buses(void);
+
+#endif // MICROPY_INCLUDED_SHARED_MODULE_BOARD__INIT__H
diff --git a/circuitpython/shared-module/canio/Match.c b/circuitpython/shared-module/canio/Match.c
new file mode 100644
index 0000000..ae8fd40
--- /dev/null
+++ b/circuitpython/shared-module/canio/Match.c
@@ -0,0 +1,44 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Jeff Epler 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.
+ */
+
+#include "shared-module/canio/Match.h"
+#include "shared-bindings/canio/Match.h"
+
+void common_hal_canio_match_construct(canio_match_obj_t *self, int id, int mask, bool extended) {
+ self->id = id;
+ self->mask = mask;
+ self->extended = extended;
+}
+
+int common_hal_canio_match_get_id(const canio_match_obj_t *self) {
+ return self->id;
+}
+int common_hal_canio_match_get_mask(const canio_match_obj_t *self) {
+ return self->mask;
+}
+bool common_hal_canio_match_get_extended(const canio_match_obj_t *self) {
+ return self->extended;
+}
diff --git a/circuitpython/shared-module/canio/Match.h b/circuitpython/shared-module/canio/Match.h
new file mode 100644
index 0000000..b52191d
--- /dev/null
+++ b/circuitpython/shared-module/canio/Match.h
@@ -0,0 +1,36 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Jeff Epler 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.
+ */
+
+#pragma once
+
+#include "py/obj.h"
+
+typedef struct {
+ mp_obj_base_t base;
+ int id;
+ int mask;
+ bool extended;
+} canio_match_obj_t;
diff --git a/circuitpython/shared-module/canio/Message.c b/circuitpython/shared-module/canio/Message.c
new file mode 100644
index 0000000..7c0dcf7
--- /dev/null
+++ b/circuitpython/shared-module/canio/Message.c
@@ -0,0 +1,70 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Jeff Epler 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.
+ */
+
+#include "shared-module/canio/Message.h"
+#include "shared-bindings/canio/Message.h"
+
+#include <string.h>
+
+void common_hal_canio_message_construct(canio_message_obj_t *self, int id, void *data, size_t size, bool extended) {
+ self->id = id;
+ self->size = size;
+ self->extended = extended;
+ if (data) {
+ memcpy(self->data, data, size);
+ }
+}
+
+int common_hal_canio_message_get_id(const canio_message_obj_t *self) {
+ return self->id;
+}
+
+void common_hal_canio_message_set_id(canio_message_obj_t *self, int id) {
+ self->id = id;
+}
+
+
+const void *common_hal_canio_message_get_data(const canio_message_obj_t *self) {
+ return self->data;
+}
+
+const void common_hal_canio_message_set_data(canio_message_obj_t *self, const void *data, size_t size) {
+ self->size = size;
+ memcpy(self->data, data, size);
+}
+
+
+size_t common_hal_canio_message_get_length(const canio_message_obj_t *self) {
+ return self->size;
+}
+
+bool common_hal_canio_message_get_extended(const canio_message_obj_t *self) {
+ return self->extended;
+}
+
+void common_hal_canio_message_set_extended(canio_message_obj_t *self, bool extended) {
+ self->extended = extended;
+}
diff --git a/circuitpython/shared-module/canio/Message.h b/circuitpython/shared-module/canio/Message.h
new file mode 100644
index 0000000..fafcaff
--- /dev/null
+++ b/circuitpython/shared-module/canio/Message.h
@@ -0,0 +1,37 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Jeff Epler 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.
+ */
+
+#pragma once
+
+#include "py/obj.h"
+
+typedef struct {
+ mp_obj_base_t base;
+ int id;
+ uint8_t data[8];
+ size_t size : 4;
+ bool extended : 1;
+} canio_message_obj_t;
diff --git a/circuitpython/shared-module/canio/RemoteTransmissionRequest.c b/circuitpython/shared-module/canio/RemoteTransmissionRequest.c
new file mode 100644
index 0000000..55c0d3b
--- /dev/null
+++ b/circuitpython/shared-module/canio/RemoteTransmissionRequest.c
@@ -0,0 +1,60 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Jeff Epler 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.
+ */
+
+#include "shared-module/canio/RemoteTransmissionRequest.h"
+#include "shared-bindings/canio/RemoteTransmissionRequest.h"
+
+#include <string.h>
+
+void common_hal_canio_remote_transmission_request_construct(canio_remote_transmission_request_obj_t *self, int id, size_t size, bool extended) {
+ self->id = id;
+ self->size = size;
+ self->extended = extended;
+}
+
+int common_hal_canio_remote_transmission_request_get_id(const canio_remote_transmission_request_obj_t *self) {
+ return self->id;
+}
+
+void common_hal_canio_remote_transmission_request_set_id(canio_remote_transmission_request_obj_t *self, int id) {
+ self->id = id;
+}
+
+size_t common_hal_canio_remote_transmission_request_get_length(const canio_remote_transmission_request_obj_t *self) {
+ return self->size;
+}
+
+void common_hal_canio_remote_transmission_request_set_length(canio_remote_transmission_request_obj_t *self, size_t size) {
+ self->size = size;
+}
+
+bool common_hal_canio_remote_transmission_request_get_extended(const canio_remote_transmission_request_obj_t *self) {
+ return self->extended;
+}
+
+void common_hal_canio_remote_transmission_request_set_extended(canio_remote_transmission_request_obj_t *self, bool extended) {
+ self->extended = extended;
+}
diff --git a/circuitpython/shared-module/canio/RemoteTransmissionRequest.h b/circuitpython/shared-module/canio/RemoteTransmissionRequest.h
new file mode 100644
index 0000000..2f09b19
--- /dev/null
+++ b/circuitpython/shared-module/canio/RemoteTransmissionRequest.h
@@ -0,0 +1,33 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Jeff Epler 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.
+ */
+
+#pragma once
+
+#include "py/obj.h"
+
+#include "shared-bindings/canio/Message.h"
+
+typedef canio_message_obj_t canio_remote_transmission_request_obj_t;
diff --git a/circuitpython/shared-module/displayio/Bitmap.c b/circuitpython/shared-module/displayio/Bitmap.c
new file mode 100644
index 0000000..933d3a8
--- /dev/null
+++ b/circuitpython/shared-module/displayio/Bitmap.c
@@ -0,0 +1,256 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Scott Shawcroft 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.
+ */
+
+#include "shared-bindings/displayio/Bitmap.h"
+
+#include <string.h>
+
+#include "py/runtime.h"
+
+void common_hal_displayio_bitmap_construct(displayio_bitmap_t *self, uint32_t width,
+ uint32_t height, uint32_t bits_per_value) {
+ uint32_t row_width = width * bits_per_value;
+ // align to uint32_t
+ uint8_t align_bits = 8 * sizeof(uint32_t);
+ if (row_width % align_bits != 0) {
+ self->stride = (row_width / align_bits + 1);
+ } else {
+ self->stride = row_width / align_bits;
+ }
+ self->width = width;
+ self->height = height;
+ self->data = m_malloc(self->stride * height * sizeof(uint32_t), false);
+ self->read_only = false;
+ self->bits_per_value = bits_per_value;
+
+ if (bits_per_value > 8 && bits_per_value != 16 && bits_per_value != 32) {
+ mp_raise_NotImplementedError(translate("Invalid bits per value"));
+ }
+
+ // Division and modulus can be slow because it has to handle any integer. We know bits_per_value
+ // is a power of two. We divide and mod by bits_per_value to compute the offset into the byte
+ // array. So, we can the offset computation to simplify to a shift for division and mask for mod.
+
+ self->x_shift = 0; // Used to divide the index by the number of pixels per word. Its used in a
+ // shift which effectively divides by 2 ** x_shift.
+ uint32_t power_of_two = 1;
+ while (power_of_two < align_bits / bits_per_value) {
+ self->x_shift++;
+ power_of_two <<= 1;
+ }
+ self->x_mask = (1 << self->x_shift) - 1; // Used as a modulus on the x value
+ self->bitmask = (1 << bits_per_value) - 1;
+
+ self->dirty_area.x1 = 0;
+ self->dirty_area.x2 = width;
+ self->dirty_area.y1 = 0;
+ self->dirty_area.y2 = height;
+}
+
+uint16_t common_hal_displayio_bitmap_get_height(displayio_bitmap_t *self) {
+ return self->height;
+}
+
+uint16_t common_hal_displayio_bitmap_get_width(displayio_bitmap_t *self) {
+ return self->width;
+}
+
+uint32_t common_hal_displayio_bitmap_get_bits_per_value(displayio_bitmap_t *self) {
+ return self->bits_per_value;
+}
+
+uint32_t common_hal_displayio_bitmap_get_pixel(displayio_bitmap_t *self, int16_t x, int16_t y) {
+ if (x >= self->width || x < 0 || y >= self->height || y < 0) {
+ return 0;
+ }
+ int32_t row_start = y * self->stride;
+ uint32_t bytes_per_value = self->bits_per_value / 8;
+ if (bytes_per_value < 1) {
+ uint32_t word = self->data[row_start + (x >> self->x_shift)];
+
+ return (word >> (sizeof(uint32_t) * 8 - ((x & self->x_mask) + 1) * self->bits_per_value)) & self->bitmask;
+ } else {
+ uint32_t *row = self->data + row_start;
+ if (bytes_per_value == 1) {
+ return ((uint8_t *)row)[x];
+ } else if (bytes_per_value == 2) {
+ return ((uint16_t *)row)[x];
+ } else if (bytes_per_value == 4) {
+ return ((uint32_t *)row)[x];
+ }
+ }
+ return 0;
+}
+
+void displayio_bitmap_set_dirty_area(displayio_bitmap_t *self, const displayio_area_t *dirty_area) {
+ if (self->read_only) {
+ mp_raise_RuntimeError(translate("Read-only object"));
+ }
+
+ displayio_area_t area = *dirty_area;
+ displayio_area_canon(&area);
+ displayio_area_union(&area, &self->dirty_area, &area);
+ displayio_area_t bitmap_area = {0, 0, self->width, self->height, NULL};
+ displayio_area_compute_overlap(&area, &bitmap_area, &self->dirty_area);
+}
+
+void displayio_bitmap_write_pixel(displayio_bitmap_t *self, int16_t x, int16_t y, uint32_t value) {
+ // Writes the color index value into a pixel position
+ // Must update the dirty area separately
+
+ // Update one pixel of data
+ int32_t row_start = y * self->stride;
+ uint32_t bytes_per_value = self->bits_per_value / 8;
+ if (bytes_per_value < 1) {
+ uint32_t bit_position = (sizeof(uint32_t) * 8 - ((x & self->x_mask) + 1) * self->bits_per_value);
+ uint32_t index = row_start + (x >> self->x_shift);
+ uint32_t word = self->data[index];
+ word &= ~(self->bitmask << bit_position);
+ word |= (value & self->bitmask) << bit_position;
+ self->data[index] = word;
+ } else {
+ uint32_t *row = self->data + row_start;
+ if (bytes_per_value == 1) {
+ ((uint8_t *)row)[x] = value;
+ } else if (bytes_per_value == 2) {
+ ((uint16_t *)row)[x] = value;
+ } else if (bytes_per_value == 4) {
+ ((uint32_t *)row)[x] = value;
+ }
+ }
+}
+
+void common_hal_displayio_bitmap_blit(displayio_bitmap_t *self, int16_t x, int16_t y, displayio_bitmap_t *source,
+ int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint32_t skip_index, bool skip_index_none) {
+ // Copy region of "source" bitmap into "self" bitmap at location x,y in the "self"
+ // If skip_value is encountered in the source bitmap, it will not be copied.
+ // If skip_value is `None`, then all pixels are copied.
+ // This function assumes input checks were performed for pixel index entries.
+
+ // Update the dirty area
+ int16_t dirty_x_max = (x + (x2 - x1));
+ if (dirty_x_max > self->width) {
+ dirty_x_max = self->width;
+ }
+ int16_t dirty_y_max = y + (y2 - y1);
+ if (dirty_y_max > self->height) {
+ dirty_y_max = self->height;
+ }
+
+ displayio_area_t a = { x, y, dirty_x_max, dirty_y_max, NULL};
+ displayio_bitmap_set_dirty_area(self, &a);
+
+ bool x_reverse = false;
+ bool y_reverse = false;
+
+ // Add reverse direction option to protect blitting of self bitmap back into self bitmap
+ if (x > x1) {
+ x_reverse = true;
+ }
+ if (y > y1) {
+ y_reverse = true;
+ }
+
+ // simplest version - use internal functions for get/set pixels
+ for (int16_t i = 0; i < (x2 - x1); i++) {
+
+ const int xs_index = x_reverse ? ((x2) - i - 1) : x1 + i; // x-index into the source bitmap
+ const int xd_index = x_reverse ? ((x + (x2 - x1)) - i - 1) : x + i; // x-index into the destination bitmap
+
+ if ((xd_index >= 0) && (xd_index < self->width)) {
+ for (int16_t j = 0; j < (y2 - y1); j++) {
+
+ const int ys_index = y_reverse ? ((y2) - j - 1) : y1 + j; // y-index into the source bitmap
+ const int yd_index = y_reverse ? ((y + (y2 - y1)) - j - 1) : y + j; // y-index into the destination bitmap
+
+ if ((yd_index >= 0) && (yd_index < self->height)) {
+ uint32_t value = common_hal_displayio_bitmap_get_pixel(source, xs_index, ys_index);
+ if ((skip_index_none) || (value != skip_index)) { // write if skip_value_none is True
+ displayio_bitmap_write_pixel(self, xd_index, yd_index, value);
+ }
+ }
+ }
+ }
+ }
+}
+
+void common_hal_displayio_bitmap_set_pixel(displayio_bitmap_t *self, int16_t x, int16_t y, uint32_t value) {
+ // update the dirty region
+ displayio_area_t a = {x, y, x + 1, y + 1, NULL};
+ displayio_bitmap_set_dirty_area(self, &a);
+
+ // write the pixel
+ displayio_bitmap_write_pixel(self, x, y, value);
+
+}
+
+displayio_area_t *displayio_bitmap_get_refresh_areas(displayio_bitmap_t *self, displayio_area_t *tail) {
+ if (self->dirty_area.x1 == self->dirty_area.x2) {
+ return tail;
+ }
+ self->dirty_area.next = tail;
+ return &self->dirty_area;
+}
+
+void displayio_bitmap_finish_refresh(displayio_bitmap_t *self) {
+ self->dirty_area.x1 = 0;
+ self->dirty_area.x2 = 0;
+}
+
+void common_hal_displayio_bitmap_fill(displayio_bitmap_t *self, uint32_t value) {
+ displayio_area_t a = {0, 0, self->width, self->height, NULL};
+ displayio_bitmap_set_dirty_area(self, &a);
+
+ // build the packed word
+ uint32_t word = 0;
+ for (uint8_t i = 0; i < 32 / self->bits_per_value; i++) {
+ word |= (value & self->bitmask) << (32 - ((i + 1) * self->bits_per_value));
+ }
+ // copy it in
+ for (uint32_t i = 0; i < self->stride * self->height; i++) {
+ self->data[i] = word;
+ }
+}
+
+int common_hal_displayio_bitmap_get_buffer(displayio_bitmap_t *self, mp_buffer_info_t *bufinfo, mp_uint_t flags) {
+ if ((flags & MP_BUFFER_WRITE) && self->read_only) {
+ return 1;
+ }
+ bufinfo->len = self->stride * self->height * sizeof(uint32_t);
+ bufinfo->buf = self->data;
+ switch (self->bits_per_value) {
+ case 32:
+ bufinfo->typecode = 'I';
+ break;
+ case 16:
+ bufinfo->typecode = 'H';
+ break;
+ default:
+ bufinfo->typecode = 'B';
+ break;
+ }
+ return 0;
+}
diff --git a/circuitpython/shared-module/displayio/Bitmap.h b/circuitpython/shared-module/displayio/Bitmap.h
new file mode 100644
index 0000000..0373ae8
--- /dev/null
+++ b/circuitpython/shared-module/displayio/Bitmap.h
@@ -0,0 +1,55 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Scott Shawcroft 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.
+ */
+
+#ifndef MICROPY_INCLUDED_SHARED_MODULE_DISPLAYIO_BITMAP_H
+#define MICROPY_INCLUDED_SHARED_MODULE_DISPLAYIO_BITMAP_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "py/obj.h"
+#include "shared-module/displayio/area.h"
+
+typedef struct {
+ mp_obj_base_t base;
+ uint16_t width;
+ uint16_t height;
+ uint32_t *data;
+ uint16_t stride; // uint32_t's
+ uint8_t bits_per_value;
+ uint8_t x_shift;
+ size_t x_mask;
+ displayio_area_t dirty_area;
+ uint16_t bitmask;
+ bool read_only;
+} displayio_bitmap_t;
+
+void displayio_bitmap_finish_refresh(displayio_bitmap_t *self);
+displayio_area_t *displayio_bitmap_get_refresh_areas(displayio_bitmap_t *self, displayio_area_t *tail);
+void displayio_bitmap_set_dirty_area(displayio_bitmap_t *self, const displayio_area_t *area);
+void displayio_bitmap_write_pixel(displayio_bitmap_t *self, int16_t x, int16_t y, uint32_t value);
+
+#endif // MICROPY_INCLUDED_SHARED_MODULE_DISPLAYIO_BITMAP_H
diff --git a/circuitpython/shared-module/displayio/ColorConverter.c b/circuitpython/shared-module/displayio/ColorConverter.c
new file mode 100644
index 0000000..707601a
--- /dev/null
+++ b/circuitpython/shared-module/displayio/ColorConverter.c
@@ -0,0 +1,287 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Scott Shawcroft 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.
+ */
+
+#include "shared-bindings/displayio/ColorConverter.h"
+
+#include "py/misc.h"
+#include "py/runtime.h"
+
+#define NO_TRANSPARENT_COLOR (0x1000000)
+
+uint32_t displayio_colorconverter_dither_noise_1(uint32_t n) {
+ n = (n >> 13) ^ n;
+ int nn = (n * (n * n * 60493 + 19990303) + 1376312589) & 0x7fffffff;
+ return (uint32_t)(((float)nn / (1073741824.0f * 2)) * 255);
+}
+
+uint32_t displayio_colorconverter_dither_noise_2(uint32_t x, uint32_t y) {
+ return displayio_colorconverter_dither_noise_1(x + y * 0xFFFF);
+}
+
+void common_hal_displayio_colorconverter_construct(displayio_colorconverter_t *self, bool dither, displayio_colorspace_t input_colorspace) {
+ self->dither = dither;
+ self->transparent_color = NO_TRANSPARENT_COLOR;
+ self->input_colorspace = input_colorspace;
+}
+
+uint16_t displayio_colorconverter_compute_rgb565(uint32_t color_rgb888) {
+ uint32_t r5 = (color_rgb888 >> 19);
+ uint32_t g6 = (color_rgb888 >> 10) & 0x3f;
+ uint32_t b5 = (color_rgb888 >> 3) & 0x1f;
+ return r5 << 11 | g6 << 5 | b5;
+}
+
+uint8_t displayio_colorconverter_compute_luma(uint32_t color_rgb888) {
+ uint32_t r8 = (color_rgb888 >> 16);
+ uint32_t g8 = (color_rgb888 >> 8) & 0xff;
+ uint32_t b8 = color_rgb888 & 0xff;
+ return (r8 * 19 + g8 * 182 + b8 * 54) / 255;
+}
+
+uint8_t displayio_colorconverter_compute_chroma(uint32_t color_rgb888) {
+ uint32_t r8 = (color_rgb888 >> 16);
+ uint32_t g8 = (color_rgb888 >> 8) & 0xff;
+ uint32_t b8 = color_rgb888 & 0xff;
+ uint8_t max = MAX(r8, MAX(g8, b8));
+ uint8_t min = MIN(r8, MIN(g8, b8));
+ return max - min;
+}
+
+uint8_t displayio_colorconverter_compute_hue(uint32_t color_rgb888) {
+ uint32_t r8 = (color_rgb888 >> 16);
+ uint32_t g8 = (color_rgb888 >> 8) & 0xff;
+ uint32_t b8 = color_rgb888 & 0xff;
+ uint8_t max = MAX(r8, MAX(g8, b8));
+ uint8_t min = MIN(r8, MIN(g8, b8));
+ uint8_t c = max - min;
+ if (c == 0) {
+ return 0;
+ }
+
+ int32_t hue = 0;
+ if (max == r8) {
+ hue = (((int32_t)(g8 - b8) * 40) / c) % 240;
+ } else if (max == g8) {
+ hue = (((int32_t)(b8 - r8) + (2 * c)) * 40) / c;
+ } else if (max == b8) {
+ hue = (((int32_t)(r8 - g8) + (4 * c)) * 40) / c;
+ }
+ if (hue < 0) {
+ hue += 240;
+ }
+
+ return hue;
+}
+
+void displayio_colorconverter_compute_tricolor(const _displayio_colorspace_t *colorspace, uint8_t pixel_hue, uint32_t *color) {
+
+ int16_t hue_diff = colorspace->tricolor_hue - pixel_hue;
+ if ((-10 <= hue_diff && hue_diff <= 10) || hue_diff <= -220 || hue_diff >= 220) {
+ if (colorspace->grayscale) {
+ *color = 0;
+ } else {
+ *color = 1;
+ }
+ } else if (!colorspace->grayscale) {
+ *color = 0;
+ }
+}
+
+void common_hal_displayio_colorconverter_convert(displayio_colorconverter_t *self, const _displayio_colorspace_t *colorspace, uint32_t input_color, uint32_t *output_color) {
+ displayio_input_pixel_t input_pixel;
+ input_pixel.pixel = input_color;
+ input_pixel.x = input_pixel.y = input_pixel.tile = input_pixel.tile_x = input_pixel.tile_y = 0;
+
+ displayio_output_pixel_t output_pixel;
+ output_pixel.pixel = 0;
+ output_pixel.opaque = false;
+
+ displayio_colorconverter_convert(self, colorspace, &input_pixel, &output_pixel);
+
+ (*output_color) = output_pixel.pixel;
+}
+
+void common_hal_displayio_colorconverter_set_dither(displayio_colorconverter_t *self, bool dither) {
+ self->dither = dither;
+}
+
+bool common_hal_displayio_colorconverter_get_dither(displayio_colorconverter_t *self) {
+ return self->dither;
+}
+
+void common_hal_displayio_colorconverter_make_transparent(displayio_colorconverter_t *self, uint32_t transparent_color) {
+ if (self->transparent_color != NO_TRANSPARENT_COLOR) {
+ mp_raise_RuntimeError(translate("Only one color can be transparent at a time"));
+ }
+ self->transparent_color = transparent_color;
+}
+
+void common_hal_displayio_colorconverter_make_opaque(displayio_colorconverter_t *self, uint32_t transparent_color) {
+ (void)transparent_color;
+ // NO_TRANSPARENT_COLOR will never equal a valid color
+ self->transparent_color = NO_TRANSPARENT_COLOR;
+}
+
+
+// Convert a single input pixel to RGB888
+uint32_t displayio_colorconverter_convert_pixel(displayio_colorspace_t colorspace, uint32_t pixel) {
+ switch (colorspace) {
+ case DISPLAYIO_COLORSPACE_RGB565_SWAPPED:
+ pixel = __builtin_bswap16(pixel);
+ MP_FALLTHROUGH;
+ case DISPLAYIO_COLORSPACE_RGB565: {
+ uint32_t r8 = (pixel >> 11) << 3;
+ uint32_t g8 = ((pixel >> 5) << 2) & 0xff;
+ uint32_t b8 = (pixel << 3) & 0xff;
+ pixel = (r8 << 16) | (g8 << 8) | b8;
+ }
+ break;
+
+ case DISPLAYIO_COLORSPACE_RGB555_SWAPPED:
+ pixel = __builtin_bswap16(pixel);
+ MP_FALLTHROUGH;
+ case DISPLAYIO_COLORSPACE_RGB555: {
+ uint32_t r8 = (pixel >> 10) << 3;
+ uint32_t g8 = ((pixel >> 5) << 3) & 0xff;
+ uint32_t b8 = (pixel << 3) & 0xff;
+ pixel = (r8 << 16) | (g8 << 8) | b8;
+ }
+ break;
+
+ case DISPLAYIO_COLORSPACE_BGR565_SWAPPED:
+ pixel = __builtin_bswap16(pixel);
+ MP_FALLTHROUGH;
+ case DISPLAYIO_COLORSPACE_BGR565: {
+ uint32_t b8 = (pixel >> 11) << 3;
+ uint32_t g8 = ((pixel >> 5) << 2) & 0xff;
+ uint32_t r8 = (pixel << 3) & 0xff;
+ pixel = (r8 << 16) | (g8 << 8) | b8;
+ }
+ break;
+
+ case DISPLAYIO_COLORSPACE_BGR555_SWAPPED:
+ pixel = __builtin_bswap16(pixel);
+ MP_FALLTHROUGH;
+ case DISPLAYIO_COLORSPACE_BGR555: {
+ uint32_t b8 = (pixel >> 10) << 3;
+ uint32_t g8 = ((pixel >> 5) << 3) & 0xff;
+ uint32_t r8 = (pixel << 3) & 0xff;
+ pixel = (r8 << 16) | (g8 << 8) | b8;
+ }
+ break;
+
+ default:
+ case DISPLAYIO_COLORSPACE_RGB888:
+ break;
+
+ case DISPLAYIO_COLORSPACE_L8: {
+ uint32_t l8 = pixel & 0xff;
+ pixel = l8 * 0x010101;
+ }
+ break;
+ }
+
+ return pixel;
+}
+
+void displayio_colorconverter_convert(displayio_colorconverter_t *self, const _displayio_colorspace_t *colorspace, const displayio_input_pixel_t *input_pixel, displayio_output_pixel_t *output_color) {
+ uint32_t pixel = input_pixel->pixel;
+
+ if (self->transparent_color == pixel) {
+ output_color->opaque = false;
+ return;
+ }
+
+ pixel = displayio_colorconverter_convert_pixel(self->input_colorspace, pixel);
+
+
+ if (self->dither) {
+ uint8_t randr = (displayio_colorconverter_dither_noise_2(input_pixel->tile_x,input_pixel->tile_y));
+ uint8_t randg = (displayio_colorconverter_dither_noise_2(input_pixel->tile_x + 33,input_pixel->tile_y));
+ uint8_t randb = (displayio_colorconverter_dither_noise_2(input_pixel->tile_x,input_pixel->tile_y + 33));
+
+ uint32_t r8 = (pixel >> 16);
+ uint32_t g8 = (pixel >> 8) & 0xff;
+ uint32_t b8 = pixel & 0xff;
+
+ if (colorspace->depth == 16) {
+ b8 = MIN(255,b8 + (randb & 0x07));
+ r8 = MIN(255,r8 + (randr & 0x07));
+ g8 = MIN(255,g8 + (randg & 0x03));
+ } else {
+ int bitmask = 0xFF >> colorspace->depth;
+ b8 = MIN(255,b8 + (randb & bitmask));
+ r8 = MIN(255,r8 + (randr & bitmask));
+ g8 = MIN(255,g8 + (randg & bitmask));
+ }
+ pixel = r8 << 16 | g8 << 8 | b8;
+ }
+
+ if (colorspace->depth == 16) {
+ uint16_t packed = displayio_colorconverter_compute_rgb565(pixel);
+ if (colorspace->reverse_bytes_in_word) {
+ // swap bytes
+ packed = __builtin_bswap16(packed);
+ }
+ output_color->pixel = packed;
+ output_color->opaque = true;
+ return;
+ } else if (colorspace->tricolor) {
+ uint8_t luma = displayio_colorconverter_compute_luma(pixel);
+ output_color->pixel = luma >> (8 - colorspace->depth);
+ if (displayio_colorconverter_compute_chroma(pixel) <= 16) {
+ if (!colorspace->grayscale) {
+ output_color->pixel = 0;
+ }
+ output_color->opaque = true;
+ return;
+ }
+ uint8_t pixel_hue = displayio_colorconverter_compute_hue(pixel);
+ displayio_colorconverter_compute_tricolor(colorspace, pixel_hue, &output_color->pixel);
+ return;
+ } else if (colorspace->grayscale && colorspace->depth <= 8) {
+ uint8_t luma = displayio_colorconverter_compute_luma(pixel);
+ size_t bitmask = (1 << colorspace->depth) - 1;
+ output_color->pixel = (luma >> colorspace->grayscale_bit) & bitmask;
+ output_color->opaque = true;
+ return;
+ } else if (colorspace->depth == 32) {
+ output_color->pixel = pixel;
+ output_color->opaque = true;
+ return;
+ }
+ output_color->opaque = false;
+}
+
+
+
+// Currently no refresh logic is needed for a ColorConverter.
+bool displayio_colorconverter_needs_refresh(displayio_colorconverter_t *self) {
+ return false;
+}
+
+void displayio_colorconverter_finish_refresh(displayio_colorconverter_t *self) {
+}
diff --git a/circuitpython/shared-module/displayio/ColorConverter.h b/circuitpython/shared-module/displayio/ColorConverter.h
new file mode 100644
index 0000000..7e4e981
--- /dev/null
+++ b/circuitpython/shared-module/displayio/ColorConverter.h
@@ -0,0 +1,56 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Scott Shawcroft 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.
+ */
+
+#ifndef MICROPY_INCLUDED_SHARED_MODULE_DISPLAYIO_COLORCONVERTER_H
+#define MICROPY_INCLUDED_SHARED_MODULE_DISPLAYIO_COLORCONVERTER_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "py/obj.h"
+#include "shared-module/displayio/Palette.h"
+
+typedef struct displayio_colorconverter {
+ mp_obj_base_t base;
+ bool dither;
+ uint8_t input_colorspace;
+ uint32_t transparent_color;
+} displayio_colorconverter_t;
+
+bool displayio_colorconverter_needs_refresh(displayio_colorconverter_t *self);
+void displayio_colorconverter_finish_refresh(displayio_colorconverter_t *self);
+void displayio_colorconverter_convert(displayio_colorconverter_t *self, const _displayio_colorspace_t *colorspace, const displayio_input_pixel_t *input_pixel, displayio_output_pixel_t *output_color);
+
+uint32_t displayio_colorconverter_dither_noise_1(uint32_t n);
+uint32_t displayio_colorconverter_dither_noise_2(uint32_t x, uint32_t y);
+
+uint16_t displayio_colorconverter_compute_rgb565(uint32_t color_rgb888);
+uint8_t displayio_colorconverter_compute_luma(uint32_t color_rgb888);
+uint8_t displayio_colorconverter_compute_chroma(uint32_t color_rgb888);
+uint8_t displayio_colorconverter_compute_hue(uint32_t color_rgb888);
+void displayio_colorconverter_compute_tricolor(const _displayio_colorspace_t *colorspace, uint8_t pixel_hue, uint32_t *color);
+
+#endif // MICROPY_INCLUDED_SHARED_MODULE_DISPLAYIO_COLORCONVERTER_H
diff --git a/circuitpython/shared-module/displayio/Display.c b/circuitpython/shared-module/displayio/Display.c
new file mode 100644
index 0000000..255cd49
--- /dev/null
+++ b/circuitpython/shared-module/displayio/Display.c
@@ -0,0 +1,461 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Scott Shawcroft 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.
+ */
+
+#include "shared-bindings/displayio/Display.h"
+
+#include "py/runtime.h"
+#include "shared-bindings/displayio/FourWire.h"
+#include "shared-bindings/displayio/I2CDisplay.h"
+#if CIRCUITPY_PARALLELDISPLAY
+#include "shared-bindings/paralleldisplay/ParallelBus.h"
+#endif
+#include "shared-bindings/microcontroller/Pin.h"
+#include "shared-bindings/time/__init__.h"
+#include "shared-module/displayio/__init__.h"
+#include "shared-module/displayio/display_core.h"
+#include "supervisor/shared/display.h"
+#include "supervisor/shared/tick.h"
+#include "supervisor/usb.h"
+
+#include <stdint.h>
+#include <string.h>
+
+#define DELAY 0x80
+
+void common_hal_displayio_display_construct(displayio_display_obj_t *self,
+ mp_obj_t bus, uint16_t width, uint16_t height, int16_t colstart, int16_t rowstart,
+ uint16_t rotation, uint16_t color_depth, bool grayscale, bool pixels_in_byte_share_row,
+ uint8_t bytes_per_cell, bool reverse_pixels_in_byte, bool reverse_bytes_in_word, uint8_t set_column_command,
+ uint8_t set_row_command, uint8_t write_ram_command,
+ uint8_t *init_sequence, uint16_t init_sequence_len, const mcu_pin_obj_t *backlight_pin,
+ uint16_t brightness_command, mp_float_t brightness, bool auto_brightness,
+ bool single_byte_bounds, bool data_as_commands, bool auto_refresh, uint16_t native_frames_per_second,
+ bool backlight_on_high, bool SH1107_addressing) {
+
+ // Turn off auto-refresh as we init.
+ self->auto_refresh = false;
+ uint16_t ram_width = 0x100;
+ uint16_t ram_height = 0x100;
+ if (single_byte_bounds) {
+ ram_width = 0xff;
+ ram_height = 0xff;
+ }
+ displayio_display_core_construct(&self->core, bus, width, height, ram_width, ram_height, colstart, rowstart, rotation,
+ color_depth, grayscale, pixels_in_byte_share_row, bytes_per_cell, reverse_pixels_in_byte, reverse_bytes_in_word);
+
+ self->set_column_command = set_column_command;
+ self->set_row_command = set_row_command;
+ self->write_ram_command = write_ram_command;
+ self->brightness_command = brightness_command;
+ self->auto_brightness = auto_brightness;
+ self->first_manual_refresh = !auto_refresh;
+ self->data_as_commands = data_as_commands;
+ self->backlight_on_high = backlight_on_high;
+ self->SH1107_addressing = SH1107_addressing && color_depth == 1;
+
+ self->native_frames_per_second = native_frames_per_second;
+ self->native_ms_per_frame = 1000 / native_frames_per_second;
+
+ uint32_t i = 0;
+ while (i < init_sequence_len) {
+ uint8_t *cmd = init_sequence + i;
+ uint8_t data_size = *(cmd + 1);
+ bool delay = (data_size & DELAY) != 0;
+ data_size &= ~DELAY;
+ uint8_t *data = cmd + 2;
+ while (!displayio_display_core_begin_transaction(&self->core)) {
+ RUN_BACKGROUND_TASKS;
+ }
+ if (self->data_as_commands) {
+ uint8_t full_command[data_size + 1];
+ full_command[0] = cmd[0];
+ memcpy(full_command + 1, data, data_size);
+ self->core.send(self->core.bus, DISPLAY_COMMAND, CHIP_SELECT_TOGGLE_EVERY_BYTE, full_command, data_size + 1);
+ } else {
+ self->core.send(self->core.bus, DISPLAY_COMMAND, CHIP_SELECT_TOGGLE_EVERY_BYTE, cmd, 1);
+ self->core.send(self->core.bus, DISPLAY_DATA, CHIP_SELECT_UNTOUCHED, data, data_size);
+ }
+ displayio_display_core_end_transaction(&self->core);
+ uint16_t delay_length_ms = 10;
+ if (delay) {
+ data_size++;
+ delay_length_ms = *(cmd + 1 + data_size);
+ if (delay_length_ms == 255) {
+ delay_length_ms = 500;
+ }
+ }
+ common_hal_time_delay_ms(delay_length_ms);
+ i += 2 + data_size;
+ }
+
+ // Always set the backlight type in case we're reusing memory.
+ self->backlight_inout.base.type = &mp_type_NoneType;
+ if (backlight_pin != NULL && common_hal_mcu_pin_is_free(backlight_pin)) {
+ // Avoid PWM types and functions when the module isn't enabled
+ #if (CIRCUITPY_PWMIO)
+ pwmout_result_t result = common_hal_pwmio_pwmout_construct(&self->backlight_pwm, backlight_pin, 0, 50000, false);
+ if (result != PWMOUT_OK) {
+ self->backlight_inout.base.type = &digitalio_digitalinout_type;
+ common_hal_digitalio_digitalinout_construct(&self->backlight_inout, backlight_pin);
+ common_hal_never_reset_pin(backlight_pin);
+ } else {
+ self->backlight_pwm.base.type = &pwmio_pwmout_type;
+ common_hal_pwmio_pwmout_never_reset(&self->backlight_pwm);
+ }
+ #else
+ // Otherwise default to digital
+ self->backlight_inout.base.type = &digitalio_digitalinout_type;
+ common_hal_digitalio_digitalinout_construct(&self->backlight_inout, backlight_pin);
+ common_hal_never_reset_pin(backlight_pin);
+ #endif
+ }
+ if (!self->auto_brightness && (self->backlight_inout.base.type != &mp_type_NoneType ||
+ brightness_command != NO_BRIGHTNESS_COMMAND)) {
+ common_hal_displayio_display_set_brightness(self, brightness);
+ } else {
+ self->current_brightness = -1.0;
+ }
+
+ // Set the group after initialization otherwise we may send pixels while we delay in
+ // initialization.
+ common_hal_displayio_display_show(self, &circuitpython_splash);
+ common_hal_displayio_display_set_auto_refresh(self, auto_refresh);
+}
+
+bool common_hal_displayio_display_show(displayio_display_obj_t *self, displayio_group_t *root_group) {
+ return displayio_display_core_show(&self->core, root_group);
+}
+
+uint16_t common_hal_displayio_display_get_width(displayio_display_obj_t *self) {
+ return displayio_display_core_get_width(&self->core);
+}
+
+uint16_t common_hal_displayio_display_get_height(displayio_display_obj_t *self) {
+ return displayio_display_core_get_height(&self->core);
+}
+
+bool common_hal_displayio_display_get_auto_brightness(displayio_display_obj_t *self) {
+ return self->auto_brightness;
+}
+
+void common_hal_displayio_display_set_auto_brightness(displayio_display_obj_t *self, bool auto_brightness) {
+ self->auto_brightness = auto_brightness;
+}
+
+mp_float_t common_hal_displayio_display_get_brightness(displayio_display_obj_t *self) {
+ return self->current_brightness;
+}
+
+bool common_hal_displayio_display_set_brightness(displayio_display_obj_t *self, mp_float_t brightness) {
+ self->updating_backlight = true;
+ if (!self->backlight_on_high) {
+ brightness = 1.0 - brightness;
+ }
+ bool ok = false;
+
+ // Avoid PWM types and functions when the module isn't enabled
+ #if (CIRCUITPY_PWMIO)
+ bool ispwm = (self->backlight_pwm.base.type == &pwmio_pwmout_type) ? true : false;
+ #else
+ bool ispwm = false;
+ #endif
+
+ if (ispwm) {
+ #if (CIRCUITPY_PWMIO)
+ common_hal_pwmio_pwmout_set_duty_cycle(&self->backlight_pwm, (uint16_t)(0xffff * brightness));
+ ok = true;
+ #else
+ ok = false;
+ #endif
+ } else if (self->backlight_inout.base.type == &digitalio_digitalinout_type) {
+ common_hal_digitalio_digitalinout_set_value(&self->backlight_inout, brightness > 0.99);
+ ok = true;
+ } else if (self->brightness_command != NO_BRIGHTNESS_COMMAND) {
+ ok = displayio_display_core_begin_transaction(&self->core);
+ if (ok) {
+ if (self->data_as_commands) {
+ uint8_t set_brightness[2] = {self->brightness_command, (uint8_t)(0xff * brightness)};
+ self->core.send(self->core.bus, DISPLAY_COMMAND, CHIP_SELECT_TOGGLE_EVERY_BYTE, set_brightness, 2);
+ } else {
+ uint8_t command = self->brightness_command;
+ uint8_t hex_brightness = 0xff * brightness;
+ self->core.send(self->core.bus, DISPLAY_COMMAND, CHIP_SELECT_TOGGLE_EVERY_BYTE, &command, 1);
+ self->core.send(self->core.bus, DISPLAY_DATA, CHIP_SELECT_UNTOUCHED, &hex_brightness, 1);
+ }
+ displayio_display_core_end_transaction(&self->core);
+ }
+
+ }
+ self->updating_backlight = false;
+ if (ok) {
+ self->current_brightness = brightness;
+ }
+ return ok;
+}
+
+mp_obj_t common_hal_displayio_display_get_bus(displayio_display_obj_t *self) {
+ return self->core.bus;
+}
+
+mp_obj_t common_hal_displayio_display_get_root_group(displayio_display_obj_t *self) {
+ return self->core.current_group;
+}
+
+STATIC const displayio_area_t *_get_refresh_areas(displayio_display_obj_t *self) {
+ if (self->core.full_refresh) {
+ self->core.area.next = NULL;
+ return &self->core.area;
+ } else if (self->core.current_group != NULL) {
+ return displayio_group_get_refresh_areas(self->core.current_group, NULL);
+ }
+ return NULL;
+}
+
+STATIC void _send_pixels(displayio_display_obj_t *self, uint8_t *pixels, uint32_t length) {
+ if (!self->data_as_commands) {
+ self->core.send(self->core.bus, DISPLAY_COMMAND, CHIP_SELECT_TOGGLE_EVERY_BYTE, &self->write_ram_command, 1);
+ }
+ self->core.send(self->core.bus, DISPLAY_DATA, CHIP_SELECT_UNTOUCHED, pixels, length);
+}
+
+STATIC bool _refresh_area(displayio_display_obj_t *self, const displayio_area_t *area) {
+ uint16_t buffer_size = 128; // In uint32_ts
+
+ displayio_area_t clipped;
+ // Clip the area to the display by overlapping the areas. If there is no overlap then we're done.
+ if (!displayio_display_core_clip_area(&self->core, area, &clipped)) {
+ return true;
+ }
+ uint16_t rows_per_buffer = displayio_area_height(&clipped);
+ uint8_t pixels_per_word = (sizeof(uint32_t) * 8) / self->core.colorspace.depth;
+ uint16_t pixels_per_buffer = displayio_area_size(&clipped);
+
+ uint16_t subrectangles = 1;
+ // for SH1107 and other boundary constrained controllers
+ // write one single row at a time
+ if (self->SH1107_addressing) {
+ subrectangles = rows_per_buffer / 8; // page addressing mode writes 8 rows at a time
+ rows_per_buffer = 8;
+ } else if (displayio_area_size(&clipped) > buffer_size * pixels_per_word) {
+ rows_per_buffer = buffer_size * pixels_per_word / displayio_area_width(&clipped);
+ if (rows_per_buffer == 0) {
+ rows_per_buffer = 1;
+ }
+ // If pixels are packed by column then ensure rows_per_buffer is on a byte boundary.
+ if (self->core.colorspace.depth < 8 && !self->core.colorspace.pixels_in_byte_share_row) {
+ uint8_t pixels_per_byte = 8 / self->core.colorspace.depth;
+ if (rows_per_buffer % pixels_per_byte != 0) {
+ rows_per_buffer -= rows_per_buffer % pixels_per_byte;
+ }
+ }
+ subrectangles = displayio_area_height(&clipped) / rows_per_buffer;
+ if (displayio_area_height(&clipped) % rows_per_buffer != 0) {
+ subrectangles++;
+ }
+ pixels_per_buffer = rows_per_buffer * displayio_area_width(&clipped);
+ buffer_size = pixels_per_buffer / pixels_per_word;
+ if (pixels_per_buffer % pixels_per_word) {
+ buffer_size += 1;
+ }
+ }
+
+ // Allocated and shared as a uint32_t array so the compiler knows the
+ // alignment everywhere.
+ uint32_t buffer[buffer_size];
+ uint32_t mask_length = (pixels_per_buffer / 32) + 1;
+ uint32_t mask[mask_length];
+ uint16_t remaining_rows = displayio_area_height(&clipped);
+
+ for (uint16_t j = 0; j < subrectangles; j++) {
+ displayio_area_t subrectangle = {
+ .x1 = clipped.x1,
+ .y1 = clipped.y1 + rows_per_buffer * j,
+ .x2 = clipped.x2,
+ .y2 = clipped.y1 + rows_per_buffer * (j + 1)
+ };
+ if (remaining_rows < rows_per_buffer) {
+ subrectangle.y2 = subrectangle.y1 + remaining_rows;
+ }
+ remaining_rows -= rows_per_buffer;
+
+ displayio_display_core_set_region_to_update(&self->core, self->set_column_command,
+ self->set_row_command, NO_COMMAND, NO_COMMAND, self->data_as_commands, false,
+ &subrectangle, self->SH1107_addressing);
+
+ uint16_t subrectangle_size_bytes;
+ if (self->core.colorspace.depth >= 8) {
+ subrectangle_size_bytes = displayio_area_size(&subrectangle) * (self->core.colorspace.depth / 8);
+ } else {
+ subrectangle_size_bytes = displayio_area_size(&subrectangle) / (8 / self->core.colorspace.depth);
+ }
+
+ memset(mask, 0, mask_length * sizeof(mask[0]));
+ memset(buffer, 0, buffer_size * sizeof(buffer[0]));
+
+ displayio_display_core_fill_area(&self->core, &subrectangle, mask, buffer);
+
+ // Can't acquire display bus; skip the rest of the data.
+ if (!displayio_display_core_bus_free(&self->core)) {
+ return false;
+ }
+
+ displayio_display_core_begin_transaction(&self->core);
+ _send_pixels(self, (uint8_t *)buffer, subrectangle_size_bytes);
+ displayio_display_core_end_transaction(&self->core);
+
+ // TODO(tannewt): Make refresh displays faster so we don't starve other
+ // background tasks.
+ #if CIRCUITPY_USB
+ usb_background();
+ #endif
+ }
+ return true;
+}
+
+STATIC void _refresh_display(displayio_display_obj_t *self) {
+ if (!displayio_display_core_start_refresh(&self->core)) {
+ // A refresh on this bus is already in progress. Try next display.
+ return;
+ }
+ const displayio_area_t *current_area = _get_refresh_areas(self);
+ while (current_area != NULL) {
+ _refresh_area(self, current_area);
+ current_area = current_area->next;
+ }
+ displayio_display_core_finish_refresh(&self->core);
+}
+
+void common_hal_displayio_display_set_rotation(displayio_display_obj_t *self, int rotation) {
+ bool transposed = (self->core.rotation == 90 || self->core.rotation == 270);
+ bool will_transposed = (rotation == 90 || rotation == 270);
+ if (transposed != will_transposed) {
+ int tmp = self->core.width;
+ self->core.width = self->core.height;
+ self->core.height = tmp;
+ }
+ displayio_display_core_set_rotation(&self->core, rotation);
+ if (self == &displays[0].display) {
+ supervisor_stop_terminal();
+ supervisor_start_terminal(self->core.width, self->core.height);
+ }
+ if (self->core.current_group != NULL) {
+ displayio_group_update_transform(self->core.current_group, &self->core.transform);
+ }
+}
+
+uint16_t common_hal_displayio_display_get_rotation(displayio_display_obj_t *self) {
+ return self->core.rotation;
+}
+
+
+bool common_hal_displayio_display_refresh(displayio_display_obj_t *self, uint32_t target_ms_per_frame, uint32_t maximum_ms_per_real_frame) {
+ if (!self->auto_refresh && !self->first_manual_refresh && (target_ms_per_frame != 0xffffffff)) {
+ uint64_t current_time = supervisor_ticks_ms64();
+ uint32_t current_ms_since_real_refresh = current_time - self->core.last_refresh;
+ // Test to see if the real frame time is below our minimum.
+ if (current_ms_since_real_refresh > maximum_ms_per_real_frame) {
+ mp_raise_RuntimeError(translate("Below minimum frame rate"));
+ }
+ uint32_t current_ms_since_last_call = current_time - self->last_refresh_call;
+ self->last_refresh_call = current_time;
+ // Skip the actual refresh to help catch up.
+ if (current_ms_since_last_call > target_ms_per_frame) {
+ return false;
+ }
+ uint32_t remaining_time = target_ms_per_frame - (current_ms_since_real_refresh % target_ms_per_frame);
+ // We're ahead of the game so wait until we align with the frame rate.
+ while (supervisor_ticks_ms64() - self->last_refresh_call < remaining_time) {
+ RUN_BACKGROUND_TASKS;
+ }
+ }
+ self->first_manual_refresh = false;
+ _refresh_display(self);
+ return true;
+}
+
+bool common_hal_displayio_display_get_auto_refresh(displayio_display_obj_t *self) {
+ return self->auto_refresh;
+}
+
+void common_hal_displayio_display_set_auto_refresh(displayio_display_obj_t *self,
+ bool auto_refresh) {
+ self->first_manual_refresh = !auto_refresh;
+ if (auto_refresh != self->auto_refresh) {
+ if (auto_refresh) {
+ supervisor_enable_tick();
+ } else {
+ supervisor_disable_tick();
+ }
+ }
+ self->auto_refresh = auto_refresh;
+}
+
+STATIC void _update_backlight(displayio_display_obj_t *self) {
+ if (!self->auto_brightness || self->updating_backlight) {
+ return;
+ }
+ if (supervisor_ticks_ms64() - self->last_backlight_refresh < 100) {
+ return;
+ }
+ // TODO(tannewt): Fade the backlight based on its existing value and a target value. The target
+ // should account for ambient light when possible.
+ common_hal_displayio_display_set_brightness(self, 1.0);
+
+ self->last_backlight_refresh = supervisor_ticks_ms64();
+}
+
+void displayio_display_background(displayio_display_obj_t *self) {
+ _update_backlight(self);
+
+ if (self->auto_refresh && (supervisor_ticks_ms64() - self->core.last_refresh) > self->native_ms_per_frame) {
+ _refresh_display(self);
+ }
+}
+
+void release_display(displayio_display_obj_t *self) {
+ common_hal_displayio_display_set_auto_refresh(self, false);
+ release_display_core(&self->core);
+ #if (CIRCUITPY_PWMIO)
+ if (self->backlight_pwm.base.type == &pwmio_pwmout_type) {
+ common_hal_pwmio_pwmout_reset_ok(&self->backlight_pwm);
+ common_hal_pwmio_pwmout_deinit(&self->backlight_pwm);
+ } else if (self->backlight_inout.base.type == &digitalio_digitalinout_type) {
+ common_hal_digitalio_digitalinout_deinit(&self->backlight_inout);
+ }
+ #else
+ common_hal_digitalio_digitalinout_deinit(&self->backlight_inout);
+ #endif
+}
+
+void reset_display(displayio_display_obj_t *self) {
+ common_hal_displayio_display_set_auto_refresh(self, true);
+ self->auto_brightness = true;
+ common_hal_displayio_display_show(self, NULL);
+}
+
+void displayio_display_collect_ptrs(displayio_display_obj_t *self) {
+ displayio_display_core_collect_ptrs(&self->core);
+}
diff --git a/circuitpython/shared-module/displayio/Display.h b/circuitpython/shared-module/displayio/Display.h
new file mode 100644
index 0000000..a0049f0
--- /dev/null
+++ b/circuitpython/shared-module/displayio/Display.h
@@ -0,0 +1,73 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Scott Shawcroft 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.
+ */
+
+#ifndef MICROPY_INCLUDED_SHARED_MODULE_DISPLAYIO_DISPLAY_H
+#define MICROPY_INCLUDED_SHARED_MODULE_DISPLAYIO_DISPLAY_H
+
+#include "shared-bindings/digitalio/DigitalInOut.h"
+#include "shared-bindings/displayio/Group.h"
+#if CIRCUITPY_PWMIO
+#include "shared-bindings/pwmio/PWMOut.h"
+#endif
+
+#include "shared-module/displayio/area.h"
+#include "shared-module/displayio/display_core.h"
+
+typedef struct {
+ mp_obj_base_t base;
+ displayio_display_core_t core;
+ union {
+ digitalio_digitalinout_obj_t backlight_inout;
+ #if CIRCUITPY_PWMIO
+ pwmio_pwmout_obj_t backlight_pwm;
+ #endif
+ };
+ uint64_t last_backlight_refresh;
+ uint64_t last_refresh_call;
+ mp_float_t current_brightness;
+ uint16_t brightness_command;
+ uint16_t native_frames_per_second;
+ uint16_t native_ms_per_frame;
+ uint8_t set_column_command;
+ uint8_t set_row_command;
+ uint8_t write_ram_command;
+ bool auto_refresh;
+ bool first_manual_refresh;
+ bool data_as_commands;
+ bool auto_brightness;
+ bool updating_backlight;
+ bool backlight_on_high;
+ // new quirk for sh1107
+ bool SH1107_addressing;
+} displayio_display_obj_t;
+
+void displayio_display_background(displayio_display_obj_t *self);
+void release_display(displayio_display_obj_t *self);
+void reset_display(displayio_display_obj_t *self);
+
+void displayio_display_collect_ptrs(displayio_display_obj_t *self);
+
+#endif // MICROPY_INCLUDED_SHARED_MODULE_DISPLAYIO_DISPLAY_H
diff --git a/circuitpython/shared-module/displayio/EPaperDisplay.c b/circuitpython/shared-module/displayio/EPaperDisplay.c
new file mode 100644
index 0000000..b1e9980
--- /dev/null
+++ b/circuitpython/shared-module/displayio/EPaperDisplay.c
@@ -0,0 +1,453 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Scott Shawcroft 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.
+ */
+
+#include "shared-bindings/displayio/EPaperDisplay.h"
+
+#include "py/gc.h"
+#include "py/runtime.h"
+#include "shared-bindings/displayio/ColorConverter.h"
+#include "shared-bindings/displayio/FourWire.h"
+#include "shared-bindings/displayio/I2CDisplay.h"
+#if CIRCUITPY_PARALLELDISPLAY
+#include "shared-bindings/paralleldisplay/ParallelBus.h"
+#endif
+#include "shared-bindings/microcontroller/Pin.h"
+#include "shared-bindings/time/__init__.h"
+#include "shared-module/displayio/__init__.h"
+#include "supervisor/shared/display.h"
+#include "supervisor/shared/tick.h"
+#include "supervisor/usb.h"
+
+#include <stdint.h>
+#include <string.h>
+
+#define DELAY 0x80
+
+void common_hal_displayio_epaperdisplay_construct(displayio_epaperdisplay_obj_t *self,
+ mp_obj_t bus, const uint8_t *start_sequence, uint16_t start_sequence_len,
+ const uint8_t *stop_sequence, uint16_t stop_sequence_len,
+ uint16_t width, uint16_t height, uint16_t ram_width, uint16_t ram_height,
+ int16_t colstart, int16_t rowstart, uint16_t rotation,
+ uint16_t set_column_window_command, uint16_t set_row_window_command,
+ uint16_t set_current_column_command, uint16_t set_current_row_command,
+ uint16_t write_black_ram_command, bool black_bits_inverted, uint16_t write_color_ram_command, bool color_bits_inverted, uint32_t highlight_color, uint16_t refresh_display_command, mp_float_t refresh_time,
+ const mcu_pin_obj_t *busy_pin, bool busy_state, mp_float_t seconds_per_frame, bool chip_select, bool grayscale, bool two_byte_sequence_length) {
+ if (highlight_color != 0x000000) {
+ self->core.colorspace.tricolor = true;
+ self->core.colorspace.tricolor_hue = displayio_colorconverter_compute_hue(highlight_color);
+ self->core.colorspace.tricolor_luma = displayio_colorconverter_compute_luma(highlight_color);
+ }
+
+ displayio_display_core_construct(&self->core, bus, width, height, ram_width, ram_height, colstart, rowstart, rotation, 1, true, true, 1, true, true);
+
+ self->set_column_window_command = set_column_window_command;
+ self->set_row_window_command = set_row_window_command;
+ self->set_current_column_command = set_current_column_command;
+ self->set_current_row_command = set_current_row_command;
+ self->write_black_ram_command = write_black_ram_command;
+ self->black_bits_inverted = black_bits_inverted;
+ self->write_color_ram_command = write_color_ram_command;
+ self->color_bits_inverted = color_bits_inverted;
+ self->refresh_display_command = refresh_display_command;
+ self->refresh_time = refresh_time * 1000;
+ self->busy_state = busy_state;
+ self->refreshing = false;
+ self->milliseconds_per_frame = seconds_per_frame * 1000;
+ self->chip_select = chip_select ? CHIP_SELECT_TOGGLE_EVERY_BYTE : CHIP_SELECT_UNTOUCHED;
+ self->grayscale = grayscale;
+
+ self->start_sequence = start_sequence;
+ self->start_sequence_len = start_sequence_len;
+ self->stop_sequence = stop_sequence;
+ self->stop_sequence_len = stop_sequence_len;
+
+ self->busy.base.type = &mp_type_NoneType;
+ self->two_byte_sequence_length = two_byte_sequence_length;
+ if (busy_pin != NULL) {
+ self->busy.base.type = &digitalio_digitalinout_type;
+ common_hal_digitalio_digitalinout_construct(&self->busy, busy_pin);
+ common_hal_never_reset_pin(busy_pin);
+ }
+
+ // Clear the color memory if it isn't in use.
+ if (highlight_color == 0x00 && write_color_ram_command != NO_COMMAND) {
+ // TODO: Clear
+ }
+
+ // Set the group after initialization otherwise we may send pixels while we delay in
+ // initialization.
+ common_hal_displayio_epaperdisplay_show(self, &circuitpython_splash);
+}
+
+bool common_hal_displayio_epaperdisplay_show(displayio_epaperdisplay_obj_t *self, displayio_group_t *root_group) {
+ return displayio_display_core_show(&self->core, root_group);
+}
+
+STATIC const displayio_area_t *displayio_epaperdisplay_get_refresh_areas(displayio_epaperdisplay_obj_t *self) {
+ if (self->core.full_refresh) {
+ self->core.area.next = NULL;
+ return &self->core.area;
+ }
+ const displayio_area_t *first_area = NULL;
+ if (self->core.current_group != NULL) {
+ first_area = displayio_group_get_refresh_areas(self->core.current_group, NULL);
+ }
+ if (first_area != NULL && self->set_row_window_command == NO_COMMAND) {
+ self->core.area.next = NULL;
+ return &self->core.area;
+ }
+ return first_area;
+}
+
+uint16_t common_hal_displayio_epaperdisplay_get_width(displayio_epaperdisplay_obj_t *self) {
+ return displayio_display_core_get_width(&self->core);
+}
+
+uint16_t common_hal_displayio_epaperdisplay_get_height(displayio_epaperdisplay_obj_t *self) {
+ return displayio_display_core_get_height(&self->core);
+}
+
+STATIC void wait_for_busy(displayio_epaperdisplay_obj_t *self) {
+ if (self->busy.base.type == &mp_type_NoneType) {
+ return;
+ }
+ while (common_hal_digitalio_digitalinout_get_value(&self->busy) == self->busy_state) {
+ RUN_BACKGROUND_TASKS;
+ }
+}
+
+STATIC void send_command_sequence(displayio_epaperdisplay_obj_t *self,
+ bool should_wait_for_busy, const uint8_t *sequence, uint32_t sequence_len) {
+ uint32_t i = 0;
+ while (i < sequence_len) {
+ const uint8_t *cmd = sequence + i;
+ uint8_t data_size = *(cmd + 1);
+ bool delay = (data_size & DELAY) != 0;
+ const uint8_t *data = cmd + 2;
+ data_size &= ~DELAY;
+ if (self->two_byte_sequence_length) {
+ data_size = ((data_size & ~DELAY) << 8) + *(cmd + 2);
+ data = cmd + 3;
+ }
+ displayio_display_core_begin_transaction(&self->core);
+ self->core.send(self->core.bus, DISPLAY_COMMAND, self->chip_select, cmd, 1);
+ self->core.send(self->core.bus, DISPLAY_DATA, self->chip_select, data, data_size);
+ displayio_display_core_end_transaction(&self->core);
+ uint16_t delay_length_ms = 0;
+ if (delay) {
+ data_size++;
+ delay_length_ms = *(cmd + 1 + data_size);
+ if (delay_length_ms == 255) {
+ delay_length_ms = 500;
+ }
+ }
+ common_hal_time_delay_ms(delay_length_ms);
+ if (should_wait_for_busy) {
+ wait_for_busy(self);
+ }
+ i += 2 + data_size;
+ if (self->two_byte_sequence_length) {
+ i++;
+ }
+ }
+}
+
+void displayio_epaperdisplay_change_refresh_mode_parameters(displayio_epaperdisplay_obj_t *self,
+ mp_buffer_info_t *start_sequence, float seconds_per_frame) {
+ self->start_sequence = (uint8_t *)start_sequence->buf;
+ self->start_sequence_len = start_sequence->len;
+ self->milliseconds_per_frame = seconds_per_frame * 1000;
+}
+
+STATIC void displayio_epaperdisplay_start_refresh(displayio_epaperdisplay_obj_t *self) {
+ // run start sequence
+ self->core.bus_reset(self->core.bus);
+
+ send_command_sequence(self, true, self->start_sequence, self->start_sequence_len);
+ displayio_display_core_start_refresh(&self->core);
+}
+
+uint32_t common_hal_displayio_epaperdisplay_get_time_to_refresh(displayio_epaperdisplay_obj_t *self) {
+ if (self->core.last_refresh == 0) {
+ return 0;
+ }
+ // Refresh at seconds per frame rate.
+ uint32_t elapsed_time = supervisor_ticks_ms64() - self->core.last_refresh;
+ if (elapsed_time > self->milliseconds_per_frame) {
+ return 0;
+ }
+ return self->milliseconds_per_frame - elapsed_time;
+}
+
+STATIC void displayio_epaperdisplay_finish_refresh(displayio_epaperdisplay_obj_t *self) {
+ // Actually refresh the display now that all pixel RAM has been updated.
+ displayio_display_core_begin_transaction(&self->core);
+ self->core.send(self->core.bus, DISPLAY_COMMAND, self->chip_select, &self->refresh_display_command, 1);
+ displayio_display_core_end_transaction(&self->core);
+ supervisor_enable_tick();
+ self->refreshing = true;
+
+ displayio_display_core_finish_refresh(&self->core);
+}
+
+mp_obj_t common_hal_displayio_epaperdisplay_get_bus(displayio_epaperdisplay_obj_t *self) {
+ return self->core.bus;
+}
+
+void common_hal_displayio_epaperdisplay_set_rotation(displayio_epaperdisplay_obj_t *self, int rotation) {
+ bool transposed = (self->core.rotation == 90 || self->core.rotation == 270);
+ bool will_transposed = (rotation == 90 || rotation == 270);
+ if (transposed != will_transposed) {
+ int tmp = self->core.width;
+ self->core.width = self->core.height;
+ self->core.height = tmp;
+ }
+ displayio_display_core_set_rotation(&self->core, rotation);
+ if (self == &displays[0].epaper_display) {
+ supervisor_stop_terminal();
+ supervisor_start_terminal(self->core.width, self->core.height);
+ }
+ if (self->core.current_group != NULL) {
+ displayio_group_update_transform(self->core.current_group, &self->core.transform);
+ }
+}
+
+uint16_t common_hal_displayio_epaperdisplay_get_rotation(displayio_epaperdisplay_obj_t *self) {
+ return self->core.rotation;
+}
+
+
+STATIC bool displayio_epaperdisplay_refresh_area(displayio_epaperdisplay_obj_t *self, const displayio_area_t *area) {
+ uint16_t buffer_size = 128; // In uint32_ts
+
+ displayio_area_t clipped;
+ // Clip the area to the display by overlapping the areas. If there is no overlap then we're done.
+ if (!displayio_display_core_clip_area(&self->core, area, &clipped)) {
+ return true;
+ }
+ uint16_t subrectangles = 1;
+ uint16_t rows_per_buffer = displayio_area_height(&clipped);
+ uint8_t pixels_per_word = (sizeof(uint32_t) * 8) / self->core.colorspace.depth;
+ uint16_t pixels_per_buffer = displayio_area_size(&clipped);
+ if (displayio_area_size(&clipped) > buffer_size * pixels_per_word) {
+ rows_per_buffer = buffer_size * pixels_per_word / displayio_area_width(&clipped);
+ if (rows_per_buffer == 0) {
+ rows_per_buffer = 1;
+ }
+ subrectangles = displayio_area_height(&clipped) / rows_per_buffer;
+ if (displayio_area_height(&clipped) % rows_per_buffer != 0) {
+ subrectangles++;
+ }
+ pixels_per_buffer = rows_per_buffer * displayio_area_width(&clipped);
+ buffer_size = pixels_per_buffer / pixels_per_word;
+ if (pixels_per_buffer % pixels_per_word) {
+ buffer_size += 1;
+ }
+ }
+
+ // Allocated and shared as a uint32_t array so the compiler knows the
+ // alignment everywhere.
+ uint32_t buffer[buffer_size];
+ volatile uint32_t mask_length = (pixels_per_buffer / 32) + 1;
+ uint32_t mask[mask_length];
+
+ uint8_t passes = 1;
+ if (self->core.colorspace.tricolor || self->grayscale) {
+ passes = 2;
+ }
+ for (uint8_t pass = 0; pass < passes; pass++) {
+ uint16_t remaining_rows = displayio_area_height(&clipped);
+
+ if (self->set_row_window_command != NO_COMMAND) {
+ displayio_display_core_set_region_to_update(&self->core, self->set_column_window_command,
+ self->set_row_window_command, self->set_current_column_command, self->set_current_row_command,
+ false, self->chip_select, &clipped, false /* SH1107_addressing */);
+ }
+
+ uint8_t write_command = self->write_black_ram_command;
+ if (pass == 1) {
+ write_command = self->write_color_ram_command;
+ }
+ displayio_display_core_begin_transaction(&self->core);
+ self->core.send(self->core.bus, DISPLAY_COMMAND, self->chip_select, &write_command, 1);
+ displayio_display_core_end_transaction(&self->core);
+
+ for (uint16_t j = 0; j < subrectangles; j++) {
+ displayio_area_t subrectangle = {
+ .x1 = clipped.x1,
+ .y1 = clipped.y1 + rows_per_buffer * j,
+ .x2 = clipped.x2,
+ .y2 = clipped.y1 + rows_per_buffer * (j + 1)
+ };
+ if (remaining_rows < rows_per_buffer) {
+ subrectangle.y2 = subrectangle.y1 + remaining_rows;
+ }
+ remaining_rows -= rows_per_buffer;
+
+
+ uint16_t subrectangle_size_bytes = displayio_area_size(&subrectangle) / (8 / self->core.colorspace.depth);
+
+ memset(mask, 0, mask_length * sizeof(mask[0]));
+ memset(buffer, 0, buffer_size * sizeof(buffer[0]));
+
+ self->core.colorspace.grayscale = true;
+ self->core.colorspace.grayscale_bit = 7;
+ if (pass == 1) {
+ if (self->grayscale) { // 4-color grayscale
+ self->core.colorspace.grayscale_bit = 6;
+ } else { // Tri-color
+ self->core.colorspace.grayscale = false;
+ }
+ }
+ displayio_display_core_fill_area(&self->core, &subrectangle, mask, buffer);
+
+ // Invert it all.
+ if ((pass == 1 && self->color_bits_inverted) ||
+ (pass == 0 && self->black_bits_inverted)) {
+ for (uint16_t k = 0; k < buffer_size; k++) {
+ buffer[k] = ~buffer[k];
+ }
+ }
+
+ if (!displayio_display_core_begin_transaction(&self->core)) {
+ // Can't acquire display bus; skip the rest of the data. Try next display.
+ return false;
+ }
+ self->core.send(self->core.bus, DISPLAY_DATA, self->chip_select, (uint8_t *)buffer, subrectangle_size_bytes);
+ displayio_display_core_end_transaction(&self->core);
+
+ // TODO(tannewt): Make refresh displays faster so we don't starve other
+ // background tasks.
+ #if CIRCUITPY_USB
+ usb_background();
+ #endif
+ }
+ }
+
+ return true;
+}
+
+bool common_hal_displayio_epaperdisplay_refresh(displayio_epaperdisplay_obj_t *self) {
+
+ if (self->refreshing && self->busy.base.type == &digitalio_digitalinout_type) {
+ if (common_hal_digitalio_digitalinout_get_value(&self->busy) != self->busy_state) {
+ supervisor_disable_tick();
+ self->refreshing = false;
+ // Run stop sequence but don't wait for busy because busy is set when sleeping.
+ send_command_sequence(self, false, self->stop_sequence, self->stop_sequence_len);
+ } else {
+ return false;
+ }
+ }
+ if (self->core.current_group == NULL) {
+ return true;
+ }
+ // Refresh at seconds per frame rate.
+ if (common_hal_displayio_epaperdisplay_get_time_to_refresh(self) > 0) {
+ return false;
+ }
+ if (!displayio_display_core_bus_free(&self->core)) {
+ // Can't acquire display bus; skip updating this display. Try next display.
+ return false;
+ }
+ const displayio_area_t *current_area = displayio_epaperdisplay_get_refresh_areas(self);
+ if (current_area == NULL) {
+ return true;
+ }
+ displayio_epaperdisplay_start_refresh(self);
+ while (current_area != NULL) {
+ displayio_epaperdisplay_refresh_area(self, current_area);
+ current_area = current_area->next;
+ }
+ displayio_epaperdisplay_finish_refresh(self);
+ return true;
+}
+
+void displayio_epaperdisplay_background(displayio_epaperdisplay_obj_t *self) {
+ if (self->refreshing) {
+ bool refresh_done = false;
+ if (self->busy.base.type == &digitalio_digitalinout_type) {
+ bool busy = common_hal_digitalio_digitalinout_get_value(&self->busy);
+ refresh_done = busy != self->busy_state;
+ } else {
+ refresh_done = supervisor_ticks_ms64() - self->core.last_refresh > self->refresh_time;
+ }
+ if (refresh_done) {
+ supervisor_disable_tick();
+ self->refreshing = false;
+ // Run stop sequence but don't wait for busy because busy is set when sleeping.
+ send_command_sequence(self, false, self->stop_sequence, self->stop_sequence_len);
+ }
+ }
+}
+
+bool common_hal_displayio_epaperdisplay_get_busy(displayio_epaperdisplay_obj_t *self) {
+ displayio_epaperdisplay_background(self);
+ return self->refreshing;
+}
+
+void release_epaperdisplay(displayio_epaperdisplay_obj_t *self) {
+ if (self->refreshing) {
+ wait_for_busy(self);
+ supervisor_disable_tick();
+ self->refreshing = false;
+ // Run stop sequence but don't wait for busy because busy is set when sleeping.
+ send_command_sequence(self, false, self->stop_sequence, self->stop_sequence_len);
+ }
+
+ release_display_core(&self->core);
+ if (self->busy.base.type == &digitalio_digitalinout_type) {
+ common_hal_digitalio_digitalinout_deinit(&self->busy);
+ }
+}
+
+void displayio_epaperdisplay_collect_ptrs(displayio_epaperdisplay_obj_t *self) {
+ displayio_display_core_collect_ptrs(&self->core);
+ gc_collect_ptr((void *)self->start_sequence);
+ gc_collect_ptr((void *)self->stop_sequence);
+}
+
+size_t maybe_refresh_epaperdisplay(void) {
+ for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) {
+ if (displays[i].epaper_display.base.type != &displayio_epaperdisplay_type ||
+ displays[i].epaper_display.core.current_group != &circuitpython_splash) {
+ // Skip regular displays and those not showing the splash.
+ continue;
+ }
+ displayio_epaperdisplay_obj_t *display = &displays[i].epaper_display;
+ size_t time_to_refresh = common_hal_displayio_epaperdisplay_get_time_to_refresh(display);
+ if (time_to_refresh > 0) {
+ return time_to_refresh;
+ }
+ if (common_hal_displayio_epaperdisplay_refresh(display)) {
+ return 0;
+ }
+ // If we could refresh but it failed, then we want to retry.
+ return 1;
+ }
+ // Return 0 if no ePaper displays are available to pretend it was updated.
+ return 0;
+}
diff --git a/circuitpython/shared-module/displayio/EPaperDisplay.h b/circuitpython/shared-module/displayio/EPaperDisplay.h
new file mode 100644
index 0000000..55feaf9
--- /dev/null
+++ b/circuitpython/shared-module/displayio/EPaperDisplay.h
@@ -0,0 +1,71 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Scott Shawcroft 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.
+ */
+
+#ifndef MICROPY_INCLUDED_SHARED_MODULE_DISPLAYIO_EPAPERDISPLAY_H
+#define MICROPY_INCLUDED_SHARED_MODULE_DISPLAYIO_EPAPERDISPLAY_H
+
+#include "shared-bindings/digitalio/DigitalInOut.h"
+#include "shared-bindings/displayio/Group.h"
+
+#include "shared-module/displayio/area.h"
+#include "shared-module/displayio/display_core.h"
+
+typedef struct {
+ mp_obj_base_t base;
+ displayio_display_core_t core;
+ digitalio_digitalinout_obj_t busy;
+ uint32_t milliseconds_per_frame;
+ const uint8_t *start_sequence;
+ uint32_t start_sequence_len;
+ const uint8_t *stop_sequence;
+ uint32_t stop_sequence_len;
+ uint16_t refresh_time;
+ uint16_t set_column_window_command;
+ uint16_t set_row_window_command;
+ uint16_t set_current_column_command;
+ uint16_t set_current_row_command;
+ uint16_t write_black_ram_command;
+ uint16_t write_color_ram_command;
+ uint8_t refresh_display_command;
+ uint8_t hue;
+ bool busy_state;
+ bool black_bits_inverted;
+ bool color_bits_inverted;
+ bool refreshing;
+ bool grayscale;
+ display_chip_select_behavior_t chip_select;
+ bool two_byte_sequence_length;
+} displayio_epaperdisplay_obj_t;
+
+void displayio_epaperdisplay_change_refresh_mode_parameters(displayio_epaperdisplay_obj_t *self,
+ mp_buffer_info_t *start_sequence, float seconds_per_frame);
+void displayio_epaperdisplay_background(displayio_epaperdisplay_obj_t *self);
+void release_epaperdisplay(displayio_epaperdisplay_obj_t *self);
+size_t maybe_refresh_epaperdisplay(void);
+
+void displayio_epaperdisplay_collect_ptrs(displayio_epaperdisplay_obj_t *self);
+
+#endif // MICROPY_INCLUDED_SHARED_MODULE_DISPLAYIO_EPAPERDISPLAY_H
diff --git a/circuitpython/shared-module/displayio/FourWire.c b/circuitpython/shared-module/displayio/FourWire.c
new file mode 100644
index 0000000..41d4340
--- /dev/null
+++ b/circuitpython/shared-module/displayio/FourWire.c
@@ -0,0 +1,179 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Scott Shawcroft 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.
+ */
+
+#include "shared-bindings/displayio/FourWire.h"
+
+#include <stdint.h>
+
+#include "py/gc.h"
+#include "shared-bindings/busio/SPI.h"
+#include "shared-bindings/digitalio/DigitalInOut.h"
+#include "shared-bindings/microcontroller/Pin.h"
+#include "shared-bindings/microcontroller/__init__.h"
+#include "shared-bindings/time/__init__.h"
+#include "shared-module/displayio/display_core.h"
+
+void common_hal_displayio_fourwire_construct(displayio_fourwire_obj_t *self,
+ busio_spi_obj_t *spi, const mcu_pin_obj_t *command,
+ const mcu_pin_obj_t *chip_select, const mcu_pin_obj_t *reset, uint32_t baudrate,
+ uint8_t polarity, uint8_t phase) {
+
+ self->bus = spi;
+ common_hal_busio_spi_never_reset(self->bus);
+ // Our object is statically allocated off the heap so make sure the bus object lives to the end
+ // of the heap as well.
+ gc_never_free(self->bus);
+
+ self->frequency = baudrate;
+ self->polarity = polarity;
+ self->phase = phase;
+
+ common_hal_digitalio_digitalinout_construct(&self->chip_select, chip_select);
+ common_hal_digitalio_digitalinout_switch_to_output(&self->chip_select, true, DRIVE_MODE_PUSH_PULL);
+
+ self->command.base.type = &mp_type_NoneType;
+ if (command != NULL) {
+ self->command.base.type = &digitalio_digitalinout_type;
+ common_hal_digitalio_digitalinout_construct(&self->command, command);
+ common_hal_digitalio_digitalinout_switch_to_output(&self->command, true, DRIVE_MODE_PUSH_PULL);
+ common_hal_never_reset_pin(command);
+ }
+ self->reset.base.type = &mp_type_NoneType;
+ if (reset != NULL) {
+ self->reset.base.type = &digitalio_digitalinout_type;
+ common_hal_digitalio_digitalinout_construct(&self->reset, reset);
+ common_hal_digitalio_digitalinout_switch_to_output(&self->reset, true, DRIVE_MODE_PUSH_PULL);
+ common_hal_never_reset_pin(reset);
+ common_hal_displayio_fourwire_reset(self);
+ }
+
+ common_hal_never_reset_pin(chip_select);
+}
+
+void common_hal_displayio_fourwire_deinit(displayio_fourwire_obj_t *self) {
+ if (self->bus == &self->inline_bus) {
+ common_hal_busio_spi_deinit(self->bus);
+ }
+
+ common_hal_reset_pin(self->command.pin);
+ common_hal_reset_pin(self->chip_select.pin);
+ common_hal_reset_pin(self->reset.pin);
+}
+
+bool common_hal_displayio_fourwire_reset(mp_obj_t obj) {
+ displayio_fourwire_obj_t *self = MP_OBJ_TO_PTR(obj);
+ if (self->reset.base.type == &mp_type_NoneType) {
+ return false;
+ }
+ common_hal_digitalio_digitalinout_set_value(&self->reset, false);
+ common_hal_mcu_delay_us(1000);
+ common_hal_digitalio_digitalinout_set_value(&self->reset, true);
+ common_hal_mcu_delay_us(1000);
+ return true;
+}
+
+bool common_hal_displayio_fourwire_bus_free(mp_obj_t obj) {
+ displayio_fourwire_obj_t *self = MP_OBJ_TO_PTR(obj);
+ if (!common_hal_busio_spi_try_lock(self->bus)) {
+ return false;
+ }
+ common_hal_busio_spi_unlock(self->bus);
+ return true;
+}
+
+bool common_hal_displayio_fourwire_begin_transaction(mp_obj_t obj) {
+ displayio_fourwire_obj_t *self = MP_OBJ_TO_PTR(obj);
+ if (!common_hal_busio_spi_try_lock(self->bus)) {
+ return false;
+ }
+ common_hal_busio_spi_configure(self->bus, self->frequency, self->polarity,
+ self->phase, 8);
+ common_hal_digitalio_digitalinout_set_value(&self->chip_select, false);
+ return true;
+}
+
+void common_hal_displayio_fourwire_send(mp_obj_t obj, display_byte_type_t data_type,
+ display_chip_select_behavior_t chip_select, const uint8_t *data, uint32_t data_length) {
+ displayio_fourwire_obj_t *self = MP_OBJ_TO_PTR(obj);
+ if (self->command.base.type == &mp_type_NoneType) {
+ // When the data/command pin is not specified, we simulate a 9-bit SPI mode, by
+ // adding a data/command bit to every byte, and then splitting the resulting data back
+ // into 8-bit chunks for transmission. If the length of the data being transmitted
+ // is not a multiple of 8, there will be additional bits at the end of the
+ // transmission. We toggle the CS pin to make the receiver discard them.
+ uint8_t buffer = 0;
+ uint8_t bits = 0;
+ uint8_t dc = (data_type == DISPLAY_DATA);
+
+ for (size_t i = 0; i < data_length; i++) {
+ bits = (bits + 1) % 8;
+
+ if (bits == 0) {
+ // send the previous byte and the dc bit
+ // we will send the current byte later
+ buffer = (buffer << 1) | dc;
+ common_hal_busio_spi_write(self->bus, &buffer, 1);
+ // send the current byte, because previous byte already filled all bits
+ common_hal_busio_spi_write(self->bus, &data[i], 1);
+ } else {
+ // send remaining bits from previous byte, dc and beginning of current byte
+ buffer = (buffer << (9 - bits)) | (dc << (8 - bits)) | (data[i] >> bits);
+ common_hal_busio_spi_write(self->bus, &buffer, 1);
+ }
+ // save the current byte
+ buffer = data[i];
+ }
+ // send any remaining bits
+ if (bits > 0) {
+ buffer = buffer << (8 - bits);
+ common_hal_busio_spi_write(self->bus, &buffer, 1);
+ // toggle CS to discard superfluous bits
+ common_hal_digitalio_digitalinout_set_value(&self->chip_select, true);
+ common_hal_mcu_delay_us(1);
+ common_hal_digitalio_digitalinout_set_value(&self->chip_select, false);
+ }
+ } else {
+ common_hal_digitalio_digitalinout_set_value(&self->command, data_type == DISPLAY_DATA);
+ if (chip_select == CHIP_SELECT_TOGGLE_EVERY_BYTE) {
+ // Toggle chip select after each command byte in case the display driver
+ // IC latches commands based on it.
+ for (size_t i = 0; i < data_length; i++) {
+ common_hal_busio_spi_write(self->bus, &data[i], 1);
+ common_hal_digitalio_digitalinout_set_value(&self->chip_select, true);
+ common_hal_mcu_delay_us(1);
+ common_hal_digitalio_digitalinout_set_value(&self->chip_select, false);
+ }
+ } else {
+ common_hal_busio_spi_write(self->bus, data, data_length);
+ }
+ }
+}
+
+void common_hal_displayio_fourwire_end_transaction(mp_obj_t obj) {
+ displayio_fourwire_obj_t *self = MP_OBJ_TO_PTR(obj);
+ common_hal_digitalio_digitalinout_set_value(&self->chip_select, true);
+ common_hal_busio_spi_unlock(self->bus);
+}
diff --git a/circuitpython/shared-module/displayio/FourWire.h b/circuitpython/shared-module/displayio/FourWire.h
new file mode 100644
index 0000000..b28c1ef
--- /dev/null
+++ b/circuitpython/shared-module/displayio/FourWire.h
@@ -0,0 +1,46 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Scott Shawcroft 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.
+ */
+
+#ifndef MICROPY_INCLUDED_SHARED_MODULE_DISPLAYIO_FOURWIRE_H
+#define MICROPY_INCLUDED_SHARED_MODULE_DISPLAYIO_FOURWIRE_H
+
+#include "common-hal/busio/SPI.h"
+#include "common-hal/digitalio/DigitalInOut.h"
+#include "shared-module/displayio/Group.h"
+
+typedef struct {
+ mp_obj_base_t base;
+ busio_spi_obj_t *bus;
+ busio_spi_obj_t inline_bus;
+ digitalio_digitalinout_obj_t command;
+ digitalio_digitalinout_obj_t chip_select;
+ digitalio_digitalinout_obj_t reset;
+ uint32_t frequency;
+ uint8_t polarity;
+ uint8_t phase;
+} displayio_fourwire_obj_t;
+
+#endif // MICROPY_INCLUDED_SHARED_MODULE_DISPLAYIO_FOURWIRE_H
diff --git a/circuitpython/shared-module/displayio/Group.c b/circuitpython/shared-module/displayio/Group.c
new file mode 100644
index 0000000..b9179f0
--- /dev/null
+++ b/circuitpython/shared-module/displayio/Group.c
@@ -0,0 +1,451 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Scott Shawcroft 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.
+ */
+
+#include "shared-bindings/displayio/Group.h"
+
+#include "py/runtime.h"
+#include "py/objlist.h"
+#include "shared-bindings/displayio/TileGrid.h"
+
+#if CIRCUITPY_VECTORIO
+#include "shared-bindings/vectorio/VectorShape.h"
+#endif
+
+
+void common_hal_displayio_group_construct(displayio_group_t *self, uint32_t scale, mp_int_t x, mp_int_t y) {
+ mp_obj_list_t *members = mp_obj_new_list(0, NULL);
+ displayio_group_construct(self, members, scale, x, y);
+}
+
+bool common_hal_displayio_group_get_hidden(displayio_group_t *self) {
+ return self->hidden;
+}
+
+void common_hal_displayio_group_set_hidden(displayio_group_t *self, bool hidden) {
+ if (self->hidden == hidden) {
+ return;
+ }
+ self->hidden = hidden;
+ if (self->hidden_by_parent) {
+ return;
+ }
+ for (size_t i = 0; i < self->members->len; i++) {
+ mp_obj_t layer;
+ layer = mp_obj_cast_to_native_base(
+ self->members->items[i], &displayio_tilegrid_type);
+ if (layer != MP_OBJ_NULL) {
+ displayio_tilegrid_set_hidden_by_parent(layer, hidden);
+ continue;
+ }
+ layer = mp_obj_cast_to_native_base(
+ self->members->items[i], &displayio_group_type);
+ if (layer != MP_OBJ_NULL) {
+ displayio_group_set_hidden_by_parent(layer, hidden);
+ continue;
+ }
+ }
+}
+
+void displayio_group_set_hidden_by_parent(displayio_group_t *self, bool hidden) {
+ if (self->hidden_by_parent == hidden) {
+ return;
+ }
+ self->hidden_by_parent = hidden;
+ // If we're already hidden, then we're done.
+ if (self->hidden) {
+ return;
+ }
+ for (size_t i = 0; i < self->members->len; i++) {
+ mp_obj_t layer;
+ layer = mp_obj_cast_to_native_base(
+ self->members->items[i], &displayio_tilegrid_type);
+ if (layer != MP_OBJ_NULL) {
+ displayio_tilegrid_set_hidden_by_parent(layer, hidden);
+ continue;
+ }
+ layer = mp_obj_cast_to_native_base(
+ self->members->items[i], &displayio_group_type);
+ if (layer != MP_OBJ_NULL) {
+ displayio_group_set_hidden_by_parent(layer, hidden);
+ continue;
+ }
+ }
+}
+
+uint32_t common_hal_displayio_group_get_scale(displayio_group_t *self) {
+ return self->scale;
+}
+
+bool displayio_group_get_previous_area(displayio_group_t *self, displayio_area_t *area) {
+ bool first = true;
+ for (size_t i = 0; i < self->members->len; i++) {
+ mp_obj_t layer;
+ displayio_area_t layer_area;
+ layer = mp_obj_cast_to_native_base(
+ self->members->items[i], &displayio_tilegrid_type);
+ if (layer != MP_OBJ_NULL) {
+ if (!displayio_tilegrid_get_previous_area(layer, &layer_area)) {
+ continue;
+ }
+ } else {
+ layer = mp_obj_cast_to_native_base(
+ self->members->items[i], &displayio_group_type);
+ if (layer != MP_OBJ_NULL) {
+ if (!displayio_group_get_previous_area(layer, &layer_area)) {
+ continue;
+ }
+ }
+ }
+ if (first) {
+ displayio_area_copy(&layer_area, area);
+ first = false;
+ } else {
+ displayio_area_union(area, &layer_area, area);
+ }
+ }
+ if (self->item_removed) {
+ if (first) {
+ displayio_area_copy(&self->dirty_area, area);
+ first = false;
+ } else {
+ displayio_area_union(area, &self->dirty_area, area);
+ }
+ }
+ return !first;
+}
+
+static void _update_child_transforms(displayio_group_t *self) {
+ if (!self->in_group) {
+ return;
+ }
+ for (size_t i = 0; i < self->members->len; i++) {
+ mp_obj_t layer;
+ #if CIRCUITPY_VECTORIO
+ const vectorio_draw_protocol_t *draw_protocol = mp_proto_get(MP_QSTR_protocol_draw, self->members->items[i]);
+ if (draw_protocol != NULL) {
+ layer = draw_protocol->draw_get_protocol_self(self->members->items[i]);
+ draw_protocol->draw_protocol_impl->draw_update_transform(layer, &self->absolute_transform);
+ continue;
+ }
+ #endif
+ layer = mp_obj_cast_to_native_base(
+ self->members->items[i], &displayio_tilegrid_type);
+ if (layer != MP_OBJ_NULL) {
+ displayio_tilegrid_update_transform(layer, &self->absolute_transform);
+ continue;
+ }
+ layer = mp_obj_cast_to_native_base(
+ self->members->items[i], &displayio_group_type);
+ if (layer != MP_OBJ_NULL) {
+ displayio_group_update_transform(layer, &self->absolute_transform);
+ continue;
+ }
+ }
+}
+
+void displayio_group_update_transform(displayio_group_t *self,
+ const displayio_buffer_transform_t *parent_transform) {
+ self->in_group = parent_transform != NULL;
+ if (self->in_group) {
+ int16_t x = self->x;
+ int16_t y = self->y;
+ if (parent_transform->transpose_xy) {
+ x = y;
+ y = self->x;
+ }
+ self->absolute_transform.x = parent_transform->x + parent_transform->dx * x;
+ self->absolute_transform.y = parent_transform->y + parent_transform->dy * y;
+ self->absolute_transform.dx = parent_transform->dx * self->scale;
+ self->absolute_transform.dy = parent_transform->dy * self->scale;
+ self->absolute_transform.transpose_xy = parent_transform->transpose_xy;
+ self->absolute_transform.mirror_x = parent_transform->mirror_x;
+ self->absolute_transform.mirror_y = parent_transform->mirror_y;
+
+ self->absolute_transform.scale = parent_transform->scale * self->scale;
+ }
+ _update_child_transforms(self);
+}
+
+void common_hal_displayio_group_set_scale(displayio_group_t *self, uint32_t scale) {
+ if (self->scale == scale) {
+ return;
+ }
+ uint8_t parent_scale = self->absolute_transform.scale / self->scale;
+ self->absolute_transform.dx = self->absolute_transform.dx / self->scale * scale;
+ self->absolute_transform.dy = self->absolute_transform.dy / self->scale * scale;
+ self->absolute_transform.scale = parent_scale * scale;
+ self->scale = scale;
+ _update_child_transforms(self);
+}
+
+mp_int_t common_hal_displayio_group_get_x(displayio_group_t *self) {
+ return self->x;
+}
+
+void common_hal_displayio_group_set_x(displayio_group_t *self, mp_int_t x) {
+ if (self->x == x) {
+ return;
+ }
+ if (self->absolute_transform.transpose_xy) {
+ int16_t dy = self->absolute_transform.dy / self->scale;
+ self->absolute_transform.y += dy * (x - self->x);
+ } else {
+ int16_t dx = self->absolute_transform.dx / self->scale;
+ self->absolute_transform.x += dx * (x - self->x);
+ }
+
+ self->x = x;
+ _update_child_transforms(self);
+}
+
+mp_int_t common_hal_displayio_group_get_y(displayio_group_t *self) {
+ return self->y;
+}
+
+void common_hal_displayio_group_set_y(displayio_group_t *self, mp_int_t y) {
+ if (self->y == y) {
+ return;
+ }
+ if (self->absolute_transform.transpose_xy) {
+ int8_t dx = self->absolute_transform.dx / self->scale;
+ self->absolute_transform.x += dx * (y - self->y);
+ } else {
+ int8_t dy = self->absolute_transform.dy / self->scale;
+ self->absolute_transform.y += dy * (y - self->y);
+ }
+ self->y = y;
+ _update_child_transforms(self);
+}
+
+static void _add_layer(displayio_group_t *self, mp_obj_t layer) {
+ #if CIRCUITPY_VECTORIO
+ const vectorio_draw_protocol_t *draw_protocol = mp_proto_get(MP_QSTR_protocol_draw, layer);
+ if (draw_protocol != NULL) {
+ draw_protocol->draw_protocol_impl->draw_update_transform(draw_protocol->draw_get_protocol_self(layer), &self->absolute_transform);
+ return;
+ }
+ #endif
+ mp_obj_t native_layer = mp_obj_cast_to_native_base(layer, &displayio_tilegrid_type);
+ if (native_layer != MP_OBJ_NULL) {
+ displayio_tilegrid_t *tilegrid = native_layer;
+ if (tilegrid->in_group) {
+ mp_raise_ValueError(translate("Layer already in a group."));
+ } else {
+ tilegrid->in_group = true;
+ }
+ displayio_tilegrid_update_transform(tilegrid, &self->absolute_transform);
+ displayio_tilegrid_set_hidden_by_parent(
+ tilegrid, self->hidden || self->hidden_by_parent);
+ return;
+ }
+ native_layer = mp_obj_cast_to_native_base(layer, &displayio_group_type);
+ if (native_layer != MP_OBJ_NULL) {
+ displayio_group_t *group = native_layer;
+ if (group->in_group) {
+ mp_raise_ValueError(translate("Layer already in a group."));
+ } else {
+ group->in_group = true;
+ }
+ displayio_group_update_transform(group, &self->absolute_transform);
+ displayio_group_set_hidden_by_parent(
+ group, self->hidden || self->hidden_by_parent);
+ return;
+ }
+ mp_raise_ValueError(translate("Layer must be a Group or TileGrid subclass."));
+}
+
+static void _remove_layer(displayio_group_t *self, size_t index) {
+ mp_obj_t layer;
+ displayio_area_t layer_area;
+ bool rendered_last_frame = false;
+ #if CIRCUITPY_VECTORIO
+ const vectorio_draw_protocol_t *draw_protocol = mp_proto_get(MP_QSTR_protocol_draw, self->members->items[index]);
+ if (draw_protocol != NULL) {
+ layer = draw_protocol->draw_get_protocol_self(self->members->items[index]);
+ bool has_dirty_area = draw_protocol->draw_protocol_impl->draw_get_dirty_area(layer, &layer_area);
+ rendered_last_frame = has_dirty_area;
+ draw_protocol->draw_protocol_impl->draw_update_transform(layer, NULL);
+ }
+ #endif
+ layer = mp_obj_cast_to_native_base(
+ self->members->items[index], &displayio_tilegrid_type);
+ if (layer != MP_OBJ_NULL) {
+ displayio_tilegrid_t *tilegrid = layer;
+ rendered_last_frame = displayio_tilegrid_get_previous_area(tilegrid, &layer_area);
+ displayio_tilegrid_update_transform(tilegrid, NULL);
+ }
+ layer = mp_obj_cast_to_native_base(
+ self->members->items[index], &displayio_group_type);
+ if (layer != MP_OBJ_NULL) {
+ displayio_group_t *group = layer;
+ rendered_last_frame = displayio_group_get_previous_area(group, &layer_area);
+ displayio_group_update_transform(group, NULL);
+ }
+ if (!rendered_last_frame) {
+ return;
+ }
+ if (!self->item_removed) {
+ displayio_area_copy(&layer_area, &self->dirty_area);
+ } else {
+ displayio_area_union(&self->dirty_area, &layer_area, &self->dirty_area);
+ }
+ self->item_removed = true;
+}
+
+void common_hal_displayio_group_insert(displayio_group_t *self, size_t index, mp_obj_t layer) {
+ _add_layer(self, layer);
+ mp_obj_list_insert(self->members, index, layer);
+}
+
+mp_obj_t common_hal_displayio_group_pop(displayio_group_t *self, size_t index) {
+ _remove_layer(self, index);
+ return mp_obj_list_pop(self->members, index);
+}
+
+mp_int_t common_hal_displayio_group_index(displayio_group_t *self, mp_obj_t layer) {
+ mp_obj_t args[] = {self->members, layer};
+ mp_obj_t *index = mp_seq_index_obj(
+ self->members->items, self->members->len, 2, args);
+ return MP_OBJ_SMALL_INT_VALUE(index);
+}
+
+size_t common_hal_displayio_group_get_len(displayio_group_t *self) {
+ return self->members->len;
+}
+
+mp_obj_t common_hal_displayio_group_get(displayio_group_t *self, size_t index) {
+ return self->members->items[index];
+}
+
+void common_hal_displayio_group_set(displayio_group_t *self, size_t index, mp_obj_t layer) {
+ _add_layer(self, layer);
+ _remove_layer(self, index);
+ mp_obj_list_store(self->members, MP_OBJ_NEW_SMALL_INT(index), layer);
+}
+
+void displayio_group_construct(displayio_group_t *self, mp_obj_list_t *members, uint32_t scale, mp_int_t x, mp_int_t y) {
+ self->x = x;
+ self->y = y;
+ self->members = members;
+ self->item_removed = false;
+ self->scale = scale;
+ self->in_group = false;
+}
+
+bool displayio_group_fill_area(displayio_group_t *self, const _displayio_colorspace_t *colorspace, const displayio_area_t *area, uint32_t *mask, uint32_t *buffer) {
+ // Track if any of the layers finishes filling in the given area. We can ignore any remaining
+ // layers at that point.
+ for (int32_t i = self->members->len - 1; i >= 0; i--) {
+ mp_obj_t layer;
+ #if CIRCUITPY_VECTORIO
+ const vectorio_draw_protocol_t *draw_protocol = mp_proto_get(MP_QSTR_protocol_draw, self->members->items[i]);
+ if (draw_protocol != NULL) {
+ layer = draw_protocol->draw_get_protocol_self(self->members->items[i]);
+ if (draw_protocol->draw_protocol_impl->draw_fill_area(layer, colorspace, area, mask, buffer)) {
+ return true;
+ }
+ continue;
+ }
+ #endif
+ layer = mp_obj_cast_to_native_base(
+ self->members->items[i], &displayio_tilegrid_type);
+ if (layer != MP_OBJ_NULL) {
+ if (displayio_tilegrid_fill_area(layer, colorspace, area, mask, buffer)) {
+ return true;
+ }
+ continue;
+ }
+ layer = mp_obj_cast_to_native_base(
+ self->members->items[i], &displayio_group_type);
+ if (layer != MP_OBJ_NULL) {
+ if (displayio_group_fill_area(layer, colorspace, area, mask, buffer)) {
+ return true;
+ }
+ continue;
+ }
+ }
+ return false;
+}
+
+void displayio_group_finish_refresh(displayio_group_t *self) {
+ self->item_removed = false;
+ for (int32_t i = self->members->len - 1; i >= 0; i--) {
+ mp_obj_t layer;
+ #if CIRCUITPY_VECTORIO
+ const vectorio_draw_protocol_t *draw_protocol = mp_proto_get(MP_QSTR_protocol_draw, self->members->items[i]);
+ if (draw_protocol != NULL) {
+ layer = draw_protocol->draw_get_protocol_self(self->members->items[i]);
+ draw_protocol->draw_protocol_impl->draw_finish_refresh(layer);
+ continue;
+ }
+ #endif
+ layer = mp_obj_cast_to_native_base(
+ self->members->items[i], &displayio_tilegrid_type);
+ if (layer != MP_OBJ_NULL) {
+ displayio_tilegrid_finish_refresh(layer);
+ continue;
+ }
+ layer = mp_obj_cast_to_native_base(
+ self->members->items[i], &displayio_group_type);
+ if (layer != MP_OBJ_NULL) {
+ displayio_group_finish_refresh(layer);
+ continue;
+ }
+ }
+}
+
+displayio_area_t *displayio_group_get_refresh_areas(displayio_group_t *self, displayio_area_t *tail) {
+ if (self->item_removed) {
+ self->dirty_area.next = tail;
+ tail = &self->dirty_area;
+ }
+
+ for (int32_t i = self->members->len - 1; i >= 0; i--) {
+ mp_obj_t layer;
+ #if CIRCUITPY_VECTORIO
+ const vectorio_draw_protocol_t *draw_protocol = mp_proto_get(MP_QSTR_protocol_draw, self->members->items[i]);
+ if (draw_protocol != NULL) {
+ layer = draw_protocol->draw_get_protocol_self(self->members->items[i]);
+ tail = draw_protocol->draw_protocol_impl->draw_get_refresh_areas(layer, tail);
+ continue;
+ }
+ #endif
+ layer = mp_obj_cast_to_native_base(
+ self->members->items[i], &displayio_tilegrid_type);
+ if (layer != MP_OBJ_NULL) {
+ tail = displayio_tilegrid_get_refresh_areas(layer, tail);
+ continue;
+ }
+ layer = mp_obj_cast_to_native_base(
+ self->members->items[i], &displayio_group_type);
+ if (layer != MP_OBJ_NULL) {
+ tail = displayio_group_get_refresh_areas(layer, tail);
+ continue;
+ }
+ }
+
+ return tail;
+}
diff --git a/circuitpython/shared-module/displayio/Group.h b/circuitpython/shared-module/displayio/Group.h
new file mode 100644
index 0000000..f684223
--- /dev/null
+++ b/circuitpython/shared-module/displayio/Group.h
@@ -0,0 +1,61 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Scott Shawcroft 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.
+ */
+
+#ifndef MICROPY_INCLUDED_SHARED_MODULE_DISPLAYIO_GROUP_H
+#define MICROPY_INCLUDED_SHARED_MODULE_DISPLAYIO_GROUP_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "py/obj.h"
+#include "py/objlist.h"
+#include "shared-module/displayio/area.h"
+#include "shared-module/displayio/Palette.h"
+
+typedef struct {
+ mp_obj_base_t base;
+ mp_obj_list_t *members;
+ displayio_buffer_transform_t absolute_transform;
+ displayio_area_t dirty_area; // Catch all for changed area
+ int16_t x;
+ int16_t y;
+ uint16_t scale;
+ bool item_removed : 1;
+ bool in_group : 1;
+ bool hidden : 1;
+ bool hidden_by_parent : 1;
+ uint8_t padding : 4;
+} displayio_group_t;
+
+void displayio_group_construct(displayio_group_t *self, mp_obj_list_t *members, uint32_t scale, mp_int_t x, mp_int_t y);
+void displayio_group_set_hidden_by_parent(displayio_group_t *self, bool hidden);
+bool displayio_group_get_previous_area(displayio_group_t *group, displayio_area_t *area);
+bool displayio_group_fill_area(displayio_group_t *group, const _displayio_colorspace_t *colorspace, const displayio_area_t *area, uint32_t *mask, uint32_t *buffer);
+void displayio_group_update_transform(displayio_group_t *group, const displayio_buffer_transform_t *parent_transform);
+void displayio_group_finish_refresh(displayio_group_t *self);
+displayio_area_t *displayio_group_get_refresh_areas(displayio_group_t *self, displayio_area_t *tail);
+
+#endif // MICROPY_INCLUDED_SHARED_MODULE_DISPLAYIO_GROUP_H
diff --git a/circuitpython/shared-module/displayio/I2CDisplay.c b/circuitpython/shared-module/displayio/I2CDisplay.c
new file mode 100644
index 0000000..8fae5d3
--- /dev/null
+++ b/circuitpython/shared-module/displayio/I2CDisplay.c
@@ -0,0 +1,127 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Scott Shawcroft 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.
+ */
+
+#include "shared-bindings/displayio/I2CDisplay.h"
+
+#include <stdint.h>
+#include <string.h>
+
+#include "py/gc.h"
+#include "py/runtime.h"
+#include "shared-bindings/busio/I2C.h"
+#include "shared-bindings/digitalio/DigitalInOut.h"
+#include "shared-bindings/microcontroller/Pin.h"
+#include "shared-bindings/microcontroller/__init__.h"
+#include "shared-bindings/time/__init__.h"
+#include "shared-module/displayio/display_core.h"
+
+void common_hal_displayio_i2cdisplay_construct(displayio_i2cdisplay_obj_t *self,
+ busio_i2c_obj_t *i2c, uint16_t device_address, const mcu_pin_obj_t *reset) {
+
+ // Reset the display before probing
+ self->reset.base.type = &mp_type_NoneType;
+ if (reset != NULL) {
+ self->reset.base.type = &digitalio_digitalinout_type;
+ common_hal_digitalio_digitalinout_construct(&self->reset, reset);
+ common_hal_digitalio_digitalinout_switch_to_output(&self->reset, true, DRIVE_MODE_PUSH_PULL);
+ common_hal_never_reset_pin(reset);
+ common_hal_displayio_i2cdisplay_reset(self);
+ }
+
+ // Probe the bus to see if a device acknowledges the given address.
+ if (!common_hal_busio_i2c_probe(i2c, device_address)) {
+ self->base.type = &mp_type_NoneType;
+ mp_raise_ValueError_varg(translate("Unable to find I2C Display at %x"), device_address);
+ }
+
+ // Write to the device and return 0 on success or an appropriate error code from mperrno.h
+ self->bus = i2c;
+ common_hal_busio_i2c_never_reset(self->bus);
+ // Our object is statically allocated off the heap so make sure the bus object lives to the end
+ // of the heap as well.
+ gc_never_free(self->bus);
+
+ self->address = device_address;
+}
+
+void common_hal_displayio_i2cdisplay_deinit(displayio_i2cdisplay_obj_t *self) {
+ if (self->bus == &self->inline_bus) {
+ common_hal_busio_i2c_deinit(self->bus);
+ }
+
+ if (self->reset.base.type == &digitalio_digitalinout_type) {
+ common_hal_digitalio_digitalinout_deinit(&self->reset);
+ }
+}
+
+bool common_hal_displayio_i2cdisplay_reset(mp_obj_t obj) {
+ displayio_i2cdisplay_obj_t *self = MP_OBJ_TO_PTR(obj);
+ if (self->reset.base.type == &mp_type_NoneType) {
+ return false;
+ }
+
+ common_hal_digitalio_digitalinout_set_value(&self->reset, false);
+ common_hal_mcu_delay_us(4);
+ common_hal_digitalio_digitalinout_set_value(&self->reset, true);
+ return true;
+}
+
+bool common_hal_displayio_i2cdisplay_bus_free(mp_obj_t obj) {
+ displayio_i2cdisplay_obj_t *self = MP_OBJ_TO_PTR(obj);
+ if (!common_hal_busio_i2c_try_lock(self->bus)) {
+ return false;
+ }
+ common_hal_busio_i2c_unlock(self->bus);
+ return true;
+}
+
+bool common_hal_displayio_i2cdisplay_begin_transaction(mp_obj_t obj) {
+ displayio_i2cdisplay_obj_t *self = MP_OBJ_TO_PTR(obj);
+ return common_hal_busio_i2c_try_lock(self->bus);
+}
+
+void common_hal_displayio_i2cdisplay_send(mp_obj_t obj, display_byte_type_t data_type,
+ display_chip_select_behavior_t chip_select, const uint8_t *data, uint32_t data_length) {
+ displayio_i2cdisplay_obj_t *self = MP_OBJ_TO_PTR(obj);
+ if (data_type == DISPLAY_COMMAND) {
+ uint8_t command_bytes[2 * data_length];
+ for (uint32_t i = 0; i < data_length; i++) {
+ command_bytes[2 * i] = 0x80;
+ command_bytes[2 * i + 1] = data[i];
+ }
+ common_hal_busio_i2c_write(self->bus, self->address, command_bytes, 2 * data_length);
+ } else {
+ uint8_t data_bytes[data_length + 1];
+ data_bytes[0] = 0x40;
+ memcpy(data_bytes + 1, data, data_length);
+ common_hal_busio_i2c_write(self->bus, self->address, data_bytes, data_length + 1);
+ }
+}
+
+void common_hal_displayio_i2cdisplay_end_transaction(mp_obj_t obj) {
+ displayio_i2cdisplay_obj_t *self = MP_OBJ_TO_PTR(obj);
+ common_hal_busio_i2c_unlock(self->bus);
+}
diff --git a/circuitpython/shared-module/displayio/I2CDisplay.h b/circuitpython/shared-module/displayio/I2CDisplay.h
new file mode 100644
index 0000000..cc5bcf1
--- /dev/null
+++ b/circuitpython/shared-module/displayio/I2CDisplay.h
@@ -0,0 +1,41 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Scott Shawcroft 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.
+ */
+
+#ifndef MICROPY_INCLUDED_SHARED_MODULE_DISPLAYIO_I2CDISPLAY_H
+#define MICROPY_INCLUDED_SHARED_MODULE_DISPLAYIO_I2CDISPLAY_H
+
+#include "common-hal/busio/I2C.h"
+#include "common-hal/digitalio/DigitalInOut.h"
+
+typedef struct {
+ mp_obj_base_t base;
+ busio_i2c_obj_t *bus;
+ busio_i2c_obj_t inline_bus;
+ digitalio_digitalinout_obj_t reset;
+ uint16_t address;
+} displayio_i2cdisplay_obj_t;
+
+#endif // MICROPY_INCLUDED_SHARED_MODULE_DISPLAYIO_I2CDISPLAY_H
diff --git a/circuitpython/shared-module/displayio/OnDiskBitmap.c b/circuitpython/shared-module/displayio/OnDiskBitmap.c
new file mode 100644
index 0000000..dd2731a
--- /dev/null
+++ b/circuitpython/shared-module/displayio/OnDiskBitmap.c
@@ -0,0 +1,208 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Scott Shawcroft 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.
+ */
+
+#include "shared-bindings/displayio/OnDiskBitmap.h"
+#include "shared-bindings/displayio/ColorConverter.h"
+#include "shared-bindings/displayio/Palette.h"
+#include "shared-module/displayio/ColorConverter.h"
+#include "shared-module/displayio/Palette.h"
+
+#include <string.h>
+
+#include "py/mperrno.h"
+#include "py/runtime.h"
+
+static uint32_t read_word(uint16_t *bmp_header, uint16_t index) {
+ return bmp_header[index] | bmp_header[index + 1] << 16;
+}
+
+void common_hal_displayio_ondiskbitmap_construct(displayio_ondiskbitmap_t *self, pyb_file_obj_t *file) {
+ // Load the wave
+ self->file = file;
+ uint16_t bmp_header[69];
+ f_rewind(&self->file->fp);
+ UINT bytes_read;
+ if (f_read(&self->file->fp, bmp_header, 138, &bytes_read) != FR_OK) {
+ mp_raise_OSError(MP_EIO);
+ }
+ if (bytes_read != 138 ||
+ memcmp(bmp_header, "BM", 2) != 0) {
+ mp_raise_ValueError(translate("Invalid BMP file"));
+ }
+
+ // We can't cast because we're not aligned.
+ self->data_offset = read_word(bmp_header, 5);
+
+ uint32_t header_size = read_word(bmp_header, 7);
+ uint16_t bits_per_pixel = bmp_header[14];
+ uint32_t compression = read_word(bmp_header, 15);
+ uint32_t number_of_colors = read_word(bmp_header, 23);
+
+ bool indexed = bits_per_pixel <= 8;
+ self->bitfield_compressed = (compression == 3);
+ self->bits_per_pixel = bits_per_pixel;
+ self->width = read_word(bmp_header, 9);
+ self->height = read_word(bmp_header, 11);
+
+ displayio_colorconverter_t *colorconverter = m_new_obj(displayio_colorconverter_t);
+ colorconverter->base.type = &displayio_colorconverter_type;
+ common_hal_displayio_colorconverter_construct(colorconverter, false, DISPLAYIO_COLORSPACE_RGB888);
+ self->colorconverter = colorconverter;
+
+ if (bits_per_pixel == 16) {
+ if (((header_size >= 56)) || (self->bitfield_compressed)) {
+ self->r_bitmask = read_word(bmp_header, 27);
+ self->g_bitmask = read_word(bmp_header, 29);
+ self->b_bitmask = read_word(bmp_header, 31);
+
+ } else { // no compression or short header means 5:5:5
+ self->r_bitmask = 0x7c00;
+ self->g_bitmask = 0x3e0;
+ self->b_bitmask = 0x1f;
+ }
+ } else if (indexed) {
+ if (number_of_colors == 0) {
+ number_of_colors = 1 << bits_per_pixel;
+ }
+
+ displayio_palette_t *palette = m_new_obj(displayio_palette_t);
+ palette->base.type = &displayio_palette_type;
+ common_hal_displayio_palette_construct(palette, number_of_colors);
+
+ if (number_of_colors > 1) {
+ uint16_t palette_size = number_of_colors * sizeof(uint32_t);
+ uint16_t palette_offset = 0xe + header_size;
+
+ uint32_t *palette_data = m_malloc(palette_size, false);
+
+ f_rewind(&self->file->fp);
+ f_lseek(&self->file->fp, palette_offset);
+
+ UINT palette_bytes_read;
+ if (f_read(&self->file->fp, palette_data, palette_size, &palette_bytes_read) != FR_OK) {
+ mp_raise_OSError(MP_EIO);
+ }
+ if (palette_bytes_read != palette_size) {
+ mp_raise_ValueError(translate("Unable to read color palette data"));
+ }
+ for (uint16_t i = 0; i < number_of_colors; i++) {
+ common_hal_displayio_palette_set_color(palette, i, palette_data[i]);
+ }
+ m_free(palette_data);
+ } else {
+ common_hal_displayio_palette_set_color(palette, 0, 0x0);
+ common_hal_displayio_palette_set_color(palette, 1, 0xffffff);
+ }
+ self->palette = palette;
+
+ } else if (!(header_size == 12 || header_size == 40 || header_size == 108 || header_size == 124)) {
+ mp_raise_ValueError_varg(translate("Only Windows format, uncompressed BMP supported: given header size is %d"), header_size);
+ }
+
+ if (bits_per_pixel == 8 && number_of_colors == 0) {
+ mp_raise_ValueError_varg(translate("Only monochrome, indexed 4bpp or 8bpp, and 16bpp or greater BMPs supported: %d bpp given"), bits_per_pixel);
+ }
+
+ uint8_t bytes_per_pixel = (self->bits_per_pixel / 8) ? (self->bits_per_pixel / 8) : 1;
+ uint8_t pixels_per_byte = 8 / self->bits_per_pixel;
+ if (pixels_per_byte == 0) {
+ self->stride = (self->width * bytes_per_pixel);
+ // Rows are word aligned.
+ if (self->stride % 4 != 0) {
+ self->stride += 4 - self->stride % 4;
+ }
+ } else {
+ uint32_t bit_stride = self->width * self->bits_per_pixel;
+ if (bit_stride % 32 != 0) {
+ bit_stride += 32 - bit_stride % 32;
+ }
+ self->stride = (bit_stride / 8);
+ }
+
+}
+
+
+uint32_t common_hal_displayio_ondiskbitmap_get_pixel(displayio_ondiskbitmap_t *self,
+ int16_t x, int16_t y) {
+ if (x < 0 || x >= self->width || y < 0 || y >= self->height) {
+ return 0;
+ }
+
+ uint32_t location;
+ uint8_t bytes_per_pixel = (self->bits_per_pixel / 8) ? (self->bits_per_pixel / 8) : 1;
+ uint8_t pixels_per_byte = 8 / self->bits_per_pixel;
+ if (pixels_per_byte == 0) {
+ location = self->data_offset + (self->height - y - 1) * self->stride + x * bytes_per_pixel;
+ } else {
+ location = self->data_offset + (self->height - y - 1) * self->stride + x / pixels_per_byte;
+ }
+ // We don't cache here because the underlying FS caches sectors.
+ f_lseek(&self->file->fp, location);
+ UINT bytes_read;
+ uint32_t pixel_data = 0;
+ uint32_t result = f_read(&self->file->fp, &pixel_data, bytes_per_pixel, &bytes_read);
+ if (result == FR_OK) {
+ uint32_t tmp = 0;
+ uint8_t red;
+ uint8_t green;
+ uint8_t blue;
+ if (bytes_per_pixel == 1) {
+ uint8_t offset = (x % pixels_per_byte) * self->bits_per_pixel;
+ uint8_t mask = (1 << self->bits_per_pixel) - 1;
+
+ return (pixel_data >> ((8 - self->bits_per_pixel) - offset)) & mask;
+ } else if (bytes_per_pixel == 2) {
+ if (self->g_bitmask == 0x07e0) { // 565
+ red = ((pixel_data & self->r_bitmask) >> 11);
+ green = ((pixel_data & self->g_bitmask) >> 5);
+ blue = ((pixel_data & self->b_bitmask) >> 0);
+ } else { // 555
+ red = ((pixel_data & self->r_bitmask) >> 10);
+ green = ((pixel_data & self->g_bitmask) >> 4);
+ blue = ((pixel_data & self->b_bitmask) >> 0);
+ }
+ tmp = (red << 19 | green << 10 | blue << 3);
+ return tmp;
+ } else if ((bytes_per_pixel == 4) && (self->bitfield_compressed)) {
+ return pixel_data & 0x00FFFFFF;
+ } else {
+ return pixel_data;
+ }
+ }
+ return 0;
+}
+
+uint16_t common_hal_displayio_ondiskbitmap_get_height(displayio_ondiskbitmap_t *self) {
+ return self->height;
+}
+
+uint16_t common_hal_displayio_ondiskbitmap_get_width(displayio_ondiskbitmap_t *self) {
+ return self->width;
+}
+
+mp_obj_t common_hal_displayio_ondiskbitmap_get_pixel_shader(displayio_ondiskbitmap_t *self) {
+ return MP_OBJ_FROM_PTR(self->pixel_shader_base);
+}
diff --git a/circuitpython/shared-module/displayio/OnDiskBitmap.h b/circuitpython/shared-module/displayio/OnDiskBitmap.h
new file mode 100644
index 0000000..806b13f
--- /dev/null
+++ b/circuitpython/shared-module/displayio/OnDiskBitmap.h
@@ -0,0 +1,56 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Scott Shawcroft 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.
+ */
+
+#ifndef MICROPY_INCLUDED_SHARED_MODULE_DISPLAYIO_ONDISKBITMAP_H
+#define MICROPY_INCLUDED_SHARED_MODULE_DISPLAYIO_ONDISKBITMAP_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "py/obj.h"
+
+#include "extmod/vfs_fat.h"
+
+typedef struct {
+ mp_obj_base_t base;
+ uint16_t width;
+ uint16_t height;
+ uint16_t data_offset;
+ uint16_t stride;
+ uint32_t r_bitmask;
+ uint32_t g_bitmask;
+ uint32_t b_bitmask;
+ pyb_file_obj_t *file;
+ union {
+ mp_obj_base_t *pixel_shader_base;
+ struct displayio_palette *palette;
+ struct displayio_colorconverter *colorconverter;
+ };
+ bool bitfield_compressed;
+ uint8_t bits_per_pixel;
+} displayio_ondiskbitmap_t;
+
+#endif // MICROPY_INCLUDED_SHARED_MODULE_DISPLAYIO_ONDISKBITMAP_H
diff --git a/circuitpython/shared-module/displayio/Palette.c b/circuitpython/shared-module/displayio/Palette.c
new file mode 100644
index 0000000..1bd168b
--- /dev/null
+++ b/circuitpython/shared-module/displayio/Palette.c
@@ -0,0 +1,114 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Scott Shawcroft 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.
+ */
+
+#include "shared-bindings/displayio/Palette.h"
+
+#include "shared-module/displayio/ColorConverter.h"
+
+void common_hal_displayio_palette_construct(displayio_palette_t *self, uint16_t color_count) {
+ self->color_count = color_count;
+ self->colors = (_displayio_color_t *)m_malloc(color_count * sizeof(_displayio_color_t), false);
+}
+
+void common_hal_displayio_palette_make_opaque(displayio_palette_t *self, uint32_t palette_index) {
+ self->colors[palette_index].transparent = false;
+ self->needs_refresh = true;
+}
+
+void common_hal_displayio_palette_make_transparent(displayio_palette_t *self, uint32_t palette_index) {
+ self->colors[palette_index].transparent = true;
+ self->needs_refresh = true;
+}
+
+bool common_hal_displayio_palette_is_transparent(displayio_palette_t *self, uint32_t palette_index) {
+ return self->colors[palette_index].transparent;
+}
+
+uint32_t common_hal_displayio_palette_get_len(displayio_palette_t *self) {
+ return self->color_count;
+}
+
+void common_hal_displayio_palette_set_color(displayio_palette_t *self, uint32_t palette_index, uint32_t color) {
+ if (self->colors[palette_index].rgb888 == color) {
+ return;
+ }
+ self->colors[palette_index].rgb888 = color;
+ self->colors[palette_index].luma = displayio_colorconverter_compute_luma(color);
+ self->colors[palette_index].rgb565 = displayio_colorconverter_compute_rgb565(color);
+
+ uint8_t chroma = displayio_colorconverter_compute_chroma(color);
+ self->colors[palette_index].chroma = chroma;
+ self->colors[palette_index].hue = displayio_colorconverter_compute_hue(color);
+ self->needs_refresh = true;
+}
+
+uint32_t common_hal_displayio_palette_get_color(displayio_palette_t *self, uint32_t palette_index) {
+ return self->colors[palette_index].rgb888;
+}
+
+bool displayio_palette_get_color(displayio_palette_t *self, const _displayio_colorspace_t *colorspace, uint32_t palette_index, uint32_t *color) {
+ if (palette_index > self->color_count || self->colors[palette_index].transparent) {
+ return false; // returns transparent
+ }
+
+ if (colorspace->tricolor) {
+ uint8_t luma = self->colors[palette_index].luma;
+ *color = luma >> (8 - colorspace->depth);
+ // Chroma 0 means the color is a gray and has no hue so never color based on it.
+ if (self->colors[palette_index].chroma <= 16) {
+ if (!colorspace->grayscale) {
+ *color = 0;
+ }
+ return true;
+ }
+ uint8_t pixel_hue = self->colors[palette_index].hue;
+ displayio_colorconverter_compute_tricolor(colorspace, pixel_hue, color);
+ } else if (colorspace->grayscale) {
+ size_t bitmask = (1 << colorspace->depth) - 1;
+ *color = (self->colors[palette_index].luma >> colorspace->grayscale_bit) & bitmask;
+ } else if (colorspace->depth == 16) {
+ uint16_t packed = self->colors[palette_index].rgb565;
+ if (colorspace->reverse_bytes_in_word) {
+ // swap bytes
+ packed = __builtin_bswap16(packed);
+ }
+ *color = packed;
+ } else if (colorspace->depth == 32) {
+ *color = self->colors[palette_index].rgb888;
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+bool displayio_palette_needs_refresh(displayio_palette_t *self) {
+ return self->needs_refresh;
+}
+
+void displayio_palette_finish_refresh(displayio_palette_t *self) {
+ self->needs_refresh = false;
+}
diff --git a/circuitpython/shared-module/displayio/Palette.h b/circuitpython/shared-module/displayio/Palette.h
new file mode 100644
index 0000000..49f03b5
--- /dev/null
+++ b/circuitpython/shared-module/displayio/Palette.h
@@ -0,0 +1,85 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Scott Shawcroft 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.
+ */
+
+#ifndef MICROPY_INCLUDED_SHARED_MODULE_DISPLAYIO_PALETTE_H
+#define MICROPY_INCLUDED_SHARED_MODULE_DISPLAYIO_PALETTE_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "py/obj.h"
+
+typedef struct {
+ uint8_t depth;
+ uint8_t bytes_per_cell;
+ uint8_t tricolor_hue;
+ uint8_t tricolor_luma;
+ uint8_t grayscale_bit; // The lowest grayscale bit. Normally 8 - depth.
+ bool grayscale;
+ bool tricolor;
+ bool pixels_in_byte_share_row;
+ bool reverse_pixels_in_byte;
+ bool reverse_bytes_in_word;
+ bool dither;
+} _displayio_colorspace_t;
+
+typedef struct {
+ uint32_t rgb888;
+ uint16_t rgb565;
+ uint8_t luma;
+ uint8_t hue;
+ uint8_t chroma;
+ bool transparent; // This may have additional bits added later for blending.
+} _displayio_color_t;
+
+typedef struct {
+ uint32_t pixel;
+ uint16_t x;
+ uint16_t y;
+ uint8_t tile;
+ uint16_t tile_x;
+ uint16_t tile_y;
+} displayio_input_pixel_t;
+
+typedef struct {
+ uint32_t pixel;
+ bool opaque;
+} displayio_output_pixel_t;
+
+typedef struct displayio_palette {
+ mp_obj_base_t base;
+ _displayio_color_t *colors;
+ uint32_t color_count;
+ bool needs_refresh;
+} displayio_palette_t;
+
+// Returns false if color fetch did not succeed (out of range or transparent).
+// Returns true if color is opaque, and sets color.
+bool displayio_palette_get_color(displayio_palette_t *palette, const _displayio_colorspace_t *colorspace, uint32_t palette_index, uint32_t *color);
+bool displayio_palette_needs_refresh(displayio_palette_t *self);
+void displayio_palette_finish_refresh(displayio_palette_t *self);
+
+#endif // MICROPY_INCLUDED_SHARED_MODULE_DISPLAYIO_PALLETE_H
diff --git a/circuitpython/shared-module/displayio/Shape.c b/circuitpython/shared-module/displayio/Shape.c
new file mode 100644
index 0000000..4a32c7a
--- /dev/null
+++ b/circuitpython/shared-module/displayio/Shape.c
@@ -0,0 +1,144 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Scott Shawcroft 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.
+ */
+
+#include "shared-bindings/displayio/Shape.h"
+
+#include <string.h>
+
+#include "py/runtime.h"
+#include "py/misc.h"
+
+void common_hal_displayio_shape_construct(displayio_shape_t *self, uint32_t width,
+ uint32_t height, bool mirror_x, bool mirror_y) {
+ self->mirror_x = mirror_x;
+ self->mirror_y = mirror_y;
+ self->width = width;
+ if (self->mirror_x) {
+ width /= 2;
+ width += self->width % 2;
+ }
+ self->half_width = width;
+
+ self->height = height;
+ if (self->mirror_y) {
+ height /= 2;
+ height += self->height % 2;
+ }
+ self->half_height = height;
+
+ self->data = m_malloc(height * sizeof(uint32_t), false);
+
+ for (uint16_t i = 0; i < height; i++) {
+ self->data[2 * i] = 0;
+ self->data[2 * i + 1] = width;
+ }
+
+ self->dirty_area.x1 = 0;
+ self->dirty_area.x2 = width;
+ self->dirty_area.y1 = 0;
+ self->dirty_area.y2 = height;
+}
+
+void common_hal_displayio_shape_set_boundary(displayio_shape_t *self, uint16_t y, uint16_t start_x, uint16_t end_x) {
+ if (y < 0 || y >= self->height || (self->mirror_y && y >= self->half_height)) {
+ mp_raise_ValueError(translate("y value out of bounds"));
+ }
+ if (start_x < 0 || start_x >= self->width || end_x < 0 || end_x >= self->width) {
+ mp_raise_ValueError(translate("x value out of bounds"));
+ }
+ if (self->mirror_x && (start_x >= self->half_width || end_x >= self->half_width)) {
+ mp_raise_ValueError_varg(translate("Maximum x value when mirrored is %d"), self->half_width);
+ }
+
+ uint16_t lower_x, upper_x, lower_y, upper_y;
+
+ // find x-boundaries for updating based on current data and start_x, end_x, and mirror_x
+ lower_x = MIN(start_x, self->data[2 * y]);
+
+ if (self->mirror_x) {
+ upper_x = self->width - lower_x + 1; // dirty rectangles are treated with max value exclusive
+ } else {
+ upper_x = MAX(end_x, self->data[2 * y + 1]) + 1; // dirty rectangles are treated with max value exclusive
+ }
+
+ // find y-boundaries based on y and mirror_y
+ lower_y = y;
+
+ if (self->mirror_y) {
+ upper_y = self->height - lower_y + 1; // dirty rectangles are treated with max value exclusive
+ } else {
+ upper_y = y + 1; // dirty rectangles are treated with max value exclusive
+ }
+
+ self->data[2 * y] = start_x; // update the data array with the new boundaries
+ self->data[2 * y + 1] = end_x;
+
+ if (self->dirty_area.x1 == self->dirty_area.x2) { // Dirty region is empty
+ self->dirty_area.x1 = lower_x;
+ self->dirty_area.x2 = upper_x;
+ self->dirty_area.y1 = lower_y;
+ self->dirty_area.y2 = upper_y;
+
+ } else { // Dirty region is not empty
+ self->dirty_area.x1 = MIN(lower_x, self->dirty_area.x1);
+ self->dirty_area.x2 = MAX(upper_x, self->dirty_area.x2);
+
+ self->dirty_area.y1 = MIN(lower_y, self->dirty_area.y1);
+ self->dirty_area.y2 = MAX(upper_y, self->dirty_area.y2);
+ }
+}
+
+uint32_t common_hal_displayio_shape_get_pixel(void *obj, int16_t x, int16_t y) {
+ displayio_shape_t *self = obj;
+ if (x >= self->width || x < 0 || y >= self->height || y < 0) {
+ return 0;
+ }
+ if (self->mirror_x && x >= self->half_width) {
+ x = self->width - x - 1;
+ }
+ if (self->mirror_y && y >= self->half_height) {
+ y = self->height - y - 1;
+ }
+ uint16_t start_x = self->data[2 * y];
+ uint16_t end_x = self->data[2 * y + 1];
+ if (x < start_x || x > end_x) {
+ return 0;
+ }
+ return 1;
+}
+
+displayio_area_t *displayio_shape_get_refresh_areas(displayio_shape_t *self, displayio_area_t *tail) {
+ if (self->dirty_area.x1 == self->dirty_area.x2) {
+ return tail;
+ }
+ self->dirty_area.next = tail;
+ return &self->dirty_area;
+}
+
+void displayio_shape_finish_refresh(displayio_shape_t *self) {
+ self->dirty_area.x1 = 0;
+ self->dirty_area.x2 = 0;
+}
diff --git a/circuitpython/shared-module/displayio/Shape.h b/circuitpython/shared-module/displayio/Shape.h
new file mode 100644
index 0000000..2cac5d7
--- /dev/null
+++ b/circuitpython/shared-module/displayio/Shape.h
@@ -0,0 +1,51 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Scott Shawcroft 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.
+ */
+
+#ifndef MICROPY_INCLUDED_SHARED_MODULE_DISPLAYIO_SHAPE_H
+#define MICROPY_INCLUDED_SHARED_MODULE_DISPLAYIO_SHAPE_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "py/obj.h"
+#include "shared-module/displayio/area.h"
+
+typedef struct {
+ mp_obj_base_t base;
+ uint16_t width;
+ uint16_t height;
+ uint16_t half_width;
+ uint16_t half_height;
+ uint16_t *data;
+ bool mirror_x;
+ bool mirror_y;
+ displayio_area_t dirty_area;
+} displayio_shape_t;
+
+void displayio_shape_finish_refresh(displayio_shape_t *self);
+displayio_area_t *displayio_shape_get_refresh_areas(displayio_shape_t *self, displayio_area_t *tail);
+
+#endif // MICROPY_INCLUDED_SHARED_MODULE_DISPLAYIO_SHAPE_H
diff --git a/circuitpython/shared-module/displayio/TileGrid.c b/circuitpython/shared-module/displayio/TileGrid.c
new file mode 100644
index 0000000..5c968c3
--- /dev/null
+++ b/circuitpython/shared-module/displayio/TileGrid.c
@@ -0,0 +1,652 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Scott Shawcroft 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.
+ */
+
+#include "shared-bindings/displayio/TileGrid.h"
+
+#include "py/runtime.h"
+#include "shared-bindings/displayio/Bitmap.h"
+#include "shared-bindings/displayio/ColorConverter.h"
+#include "shared-bindings/displayio/OnDiskBitmap.h"
+#include "shared-bindings/displayio/Palette.h"
+#include "shared-bindings/displayio/Shape.h"
+
+void common_hal_displayio_tilegrid_construct(displayio_tilegrid_t *self, mp_obj_t bitmap,
+ uint16_t bitmap_width_in_tiles, uint16_t bitmap_height_in_tiles,
+ mp_obj_t pixel_shader, uint16_t width, uint16_t height,
+ uint16_t tile_width, uint16_t tile_height, uint16_t x, uint16_t y, uint8_t default_tile) {
+ uint32_t total_tiles = width * height;
+ // Sprites will only have one tile so save a little memory by inlining values in the pointer.
+ uint8_t inline_tiles = sizeof(uint8_t *);
+ if (total_tiles <= inline_tiles) {
+ self->tiles = 0;
+ // Pack values into the pointer since there are only a few.
+ for (uint32_t i = 0; i < inline_tiles; i++) {
+ ((uint8_t *)&self->tiles)[i] = default_tile;
+ }
+ self->inline_tiles = true;
+ } else {
+ self->tiles = (uint8_t *)m_malloc(total_tiles, false);
+ for (uint32_t i = 0; i < total_tiles; i++) {
+ self->tiles[i] = default_tile;
+ }
+ self->inline_tiles = false;
+ }
+ self->bitmap_width_in_tiles = bitmap_width_in_tiles;
+ self->tiles_in_bitmap = bitmap_width_in_tiles * bitmap_height_in_tiles;
+ self->width_in_tiles = width;
+ self->height_in_tiles = height;
+ self->x = x;
+ self->y = y;
+ self->pixel_width = width * tile_width;
+ self->pixel_height = height * tile_height;
+ self->tile_width = tile_width;
+ self->tile_height = tile_height;
+ self->bitmap = bitmap;
+ self->pixel_shader = pixel_shader;
+ self->in_group = false;
+ self->hidden = false;
+ self->hidden_by_parent = false;
+ self->previous_area.x1 = 0xffff;
+ self->previous_area.x2 = self->previous_area.x1;
+ self->flip_x = false;
+ self->flip_y = false;
+ self->transpose_xy = false;
+ self->absolute_transform = NULL;
+}
+
+
+bool common_hal_displayio_tilegrid_get_hidden(displayio_tilegrid_t *self) {
+ return self->hidden;
+}
+
+void common_hal_displayio_tilegrid_set_hidden(displayio_tilegrid_t *self, bool hidden) {
+ self->hidden = hidden;
+ if (!hidden) {
+ self->full_change = true;
+ }
+}
+
+void displayio_tilegrid_set_hidden_by_parent(displayio_tilegrid_t *self, bool hidden) {
+ self->hidden_by_parent = hidden;
+ if (!hidden) {
+ self->full_change = true;
+ }
+}
+
+bool displayio_tilegrid_get_previous_area(displayio_tilegrid_t *self, displayio_area_t *area) {
+ if (self->previous_area.x1 == self->previous_area.x2) {
+ return false;
+ }
+ displayio_area_copy(&self->previous_area, area);
+ return true;
+}
+
+STATIC void _update_current_x(displayio_tilegrid_t *self) {
+ uint16_t width;
+ if (self->transpose_xy) {
+ width = self->pixel_height;
+ } else {
+ width = self->pixel_width;
+ }
+
+ // If there's no transform, substitute an identity transform so the calculations will work.
+ const displayio_buffer_transform_t *absolute_transform =
+ self->absolute_transform == NULL
+ ? &null_transform
+ : self->absolute_transform;
+
+ if (absolute_transform->transpose_xy) {
+ self->current_area.y1 = absolute_transform->y + absolute_transform->dy * self->x;
+ self->current_area.y2 = absolute_transform->y + absolute_transform->dy * (self->x + width);
+ if (self->current_area.y2 < self->current_area.y1) {
+ int16_t temp = self->current_area.y2;
+ self->current_area.y2 = self->current_area.y1;
+ self->current_area.y1 = temp;
+ }
+ } else {
+ self->current_area.x1 = absolute_transform->x + absolute_transform->dx * self->x;
+ self->current_area.x2 = absolute_transform->x + absolute_transform->dx * (self->x + width);
+ if (self->current_area.x2 < self->current_area.x1) {
+ int16_t temp = self->current_area.x2;
+ self->current_area.x2 = self->current_area.x1;
+ self->current_area.x1 = temp;
+ }
+ }
+}
+
+STATIC void _update_current_y(displayio_tilegrid_t *self) {
+ uint16_t height;
+ if (self->transpose_xy) {
+ height = self->pixel_width;
+ } else {
+ height = self->pixel_height;
+ }
+
+ // If there's no transform, substitute an identity transform so the calculations will work.
+ const displayio_buffer_transform_t *absolute_transform =
+ self->absolute_transform == NULL
+ ? &null_transform
+ : self->absolute_transform;
+
+ if (absolute_transform->transpose_xy) {
+ self->current_area.x1 = absolute_transform->x + absolute_transform->dx * self->y;
+ self->current_area.x2 = absolute_transform->x + absolute_transform->dx * (self->y + height);
+ if (self->current_area.x2 < self->current_area.x1) {
+ int16_t temp = self->current_area.x2;
+ self->current_area.x2 = self->current_area.x1;
+ self->current_area.x1 = temp;
+ }
+ } else {
+ self->current_area.y1 = absolute_transform->y + absolute_transform->dy * self->y;
+ self->current_area.y2 = absolute_transform->y + absolute_transform->dy * (self->y + height);
+ if (self->current_area.y2 < self->current_area.y1) {
+ int16_t temp = self->current_area.y2;
+ self->current_area.y2 = self->current_area.y1;
+ self->current_area.y1 = temp;
+ }
+ }
+}
+
+void displayio_tilegrid_update_transform(displayio_tilegrid_t *self,
+ const displayio_buffer_transform_t *absolute_transform) {
+ self->in_group = absolute_transform != NULL;
+ self->absolute_transform = absolute_transform;
+ if (absolute_transform != NULL) {
+ self->moved = true;
+
+ _update_current_x(self);
+ _update_current_y(self);
+ }
+}
+
+mp_int_t common_hal_displayio_tilegrid_get_x(displayio_tilegrid_t *self) {
+ return self->x;
+}
+void common_hal_displayio_tilegrid_set_x(displayio_tilegrid_t *self, mp_int_t x) {
+ if (self->x == x) {
+ return;
+ }
+
+ self->moved = true;
+
+ self->x = x;
+ if (self->absolute_transform != NULL) {
+ _update_current_x(self);
+ }
+}
+mp_int_t common_hal_displayio_tilegrid_get_y(displayio_tilegrid_t *self) {
+ return self->y;
+}
+
+void common_hal_displayio_tilegrid_set_y(displayio_tilegrid_t *self, mp_int_t y) {
+ if (self->y == y) {
+ return;
+ }
+ self->moved = true;
+ self->y = y;
+ if (self->absolute_transform != NULL) {
+ _update_current_y(self);
+ }
+}
+
+mp_obj_t common_hal_displayio_tilegrid_get_pixel_shader(displayio_tilegrid_t *self) {
+ return self->pixel_shader;
+}
+
+void common_hal_displayio_tilegrid_set_pixel_shader(displayio_tilegrid_t *self, mp_obj_t pixel_shader) {
+ self->pixel_shader = pixel_shader;
+ self->full_change = true;
+}
+
+mp_obj_t common_hal_displayio_tilegrid_get_bitmap(displayio_tilegrid_t *self) {
+ return self->bitmap;
+}
+
+void common_hal_displayio_tilegrid_set_bitmap(displayio_tilegrid_t *self, mp_obj_t bitmap) {
+ self->bitmap = bitmap;
+ self->full_change = true;
+}
+
+uint16_t common_hal_displayio_tilegrid_get_width(displayio_tilegrid_t *self) {
+ return self->width_in_tiles;
+}
+
+uint16_t common_hal_displayio_tilegrid_get_height(displayio_tilegrid_t *self) {
+ return self->height_in_tiles;
+}
+
+uint16_t common_hal_displayio_tilegrid_get_tile_width(displayio_tilegrid_t *self) {
+ return self->tile_width;
+}
+
+uint16_t common_hal_displayio_tilegrid_get_tile_height(displayio_tilegrid_t *self) {
+ return self->tile_height;
+}
+
+uint8_t common_hal_displayio_tilegrid_get_tile(displayio_tilegrid_t *self, uint16_t x, uint16_t y) {
+ uint8_t *tiles = self->tiles;
+ if (self->inline_tiles) {
+ tiles = (uint8_t *)&self->tiles;
+ }
+ if (tiles == NULL) {
+ return 0;
+ }
+ return tiles[y * self->width_in_tiles + x];
+}
+
+void common_hal_displayio_tilegrid_set_tile(displayio_tilegrid_t *self, uint16_t x, uint16_t y, uint8_t tile_index) {
+ if (tile_index >= self->tiles_in_bitmap) {
+ mp_raise_ValueError(translate("Tile index out of bounds"));
+ }
+ uint8_t *tiles = self->tiles;
+ if (self->inline_tiles) {
+ tiles = (uint8_t *)&self->tiles;
+ }
+ if (tiles == NULL) {
+ return;
+ }
+ tiles[y * self->width_in_tiles + x] = tile_index;
+ displayio_area_t temp_area;
+ displayio_area_t *tile_area;
+ if (!self->partial_change) {
+ tile_area = &self->dirty_area;
+ } else {
+ tile_area = &temp_area;
+ }
+ int16_t tx = (x - self->top_left_x) % self->width_in_tiles;
+ if (tx < 0) {
+ tx += self->width_in_tiles;
+ }
+ tile_area->x1 = tx * self->tile_width;
+ tile_area->x2 = tile_area->x1 + self->tile_width;
+ int16_t ty = (y - self->top_left_y) % self->height_in_tiles;
+ if (ty < 0) {
+ ty += self->height_in_tiles;
+ }
+ tile_area->y1 = ty * self->tile_height;
+ tile_area->y2 = tile_area->y1 + self->tile_height;
+
+ if (self->partial_change) {
+ displayio_area_union(&self->dirty_area, &temp_area, &self->dirty_area);
+ }
+
+ self->partial_change = true;
+}
+
+bool common_hal_displayio_tilegrid_get_flip_x(displayio_tilegrid_t *self) {
+ return self->flip_x;
+}
+
+void common_hal_displayio_tilegrid_set_flip_x(displayio_tilegrid_t *self, bool flip_x) {
+ if (self->flip_x == flip_x) {
+ return;
+ }
+ self->flip_x = flip_x;
+ self->full_change = true;
+}
+
+bool common_hal_displayio_tilegrid_get_flip_y(displayio_tilegrid_t *self) {
+ return self->flip_y;
+}
+
+void common_hal_displayio_tilegrid_set_flip_y(displayio_tilegrid_t *self, bool flip_y) {
+ if (self->flip_y == flip_y) {
+ return;
+ }
+ self->flip_y = flip_y;
+ self->full_change = true;
+}
+
+bool common_hal_displayio_tilegrid_get_transpose_xy(displayio_tilegrid_t *self) {
+ return self->transpose_xy;
+}
+
+void common_hal_displayio_tilegrid_set_transpose_xy(displayio_tilegrid_t *self, bool transpose_xy) {
+ if (self->transpose_xy == transpose_xy) {
+ return;
+ }
+ self->transpose_xy = transpose_xy;
+
+ // Square TileGrids do not change dimensions when transposed.
+ if (self->pixel_width == self->pixel_height) {
+ self->full_change = true;
+ return;
+ }
+
+ _update_current_x(self);
+ _update_current_y(self);
+
+ self->moved = true;
+}
+
+void common_hal_displayio_tilegrid_set_top_left(displayio_tilegrid_t *self, uint16_t x, uint16_t y) {
+ self->top_left_x = x;
+ self->top_left_y = y;
+ self->full_change = true;
+}
+
+bool displayio_tilegrid_fill_area(displayio_tilegrid_t *self,
+ const _displayio_colorspace_t *colorspace, const displayio_area_t *area,
+ uint32_t *mask, uint32_t *buffer) {
+ // If no tiles are present we have no impact.
+ uint8_t *tiles = self->tiles;
+ if (self->inline_tiles) {
+ tiles = (uint8_t *)&self->tiles;
+ }
+ if (tiles == NULL) {
+ return false;
+ }
+
+ bool hidden = self->hidden || self->hidden_by_parent;
+ if (hidden) {
+ return false;
+ }
+
+ displayio_area_t overlap;
+ if (!displayio_area_compute_overlap(area, &self->current_area, &overlap)) {
+ return false;
+ }
+
+ int16_t x_stride = 1;
+ int16_t y_stride = displayio_area_width(area);
+
+ bool flip_x = self->flip_x;
+ bool flip_y = self->flip_y;
+ if (self->transpose_xy != self->absolute_transform->transpose_xy) {
+ bool temp_flip = flip_x;
+ flip_x = flip_y;
+ flip_y = temp_flip;
+ }
+
+ // How many pixels are outside of our area between us and the start of the row.
+ uint16_t start = 0;
+ if ((self->absolute_transform->dx < 0) != flip_x) {
+ start += (area->x2 - area->x1 - 1) * x_stride;
+ x_stride *= -1;
+ }
+ if ((self->absolute_transform->dy < 0) != flip_y) {
+ start += (area->y2 - area->y1 - 1) * y_stride;
+ y_stride *= -1;
+ }
+
+ // Track if this layer finishes filling in the given area. We can ignore any remaining
+ // layers at that point.
+ bool full_coverage = displayio_area_equal(area, &overlap);
+
+ // TODO(tannewt): Skip coverage tracking if all pixels outside the overlap have already been
+ // set and our palette is all opaque.
+
+ // TODO(tannewt): Check to see if the pixel_shader has any transparency. If it doesn't then we
+ // can either return full coverage or bulk update the mask.
+ displayio_area_t transformed;
+ displayio_area_transform_within(flip_x != (self->absolute_transform->dx < 0), flip_y != (self->absolute_transform->dy < 0), self->transpose_xy != self->absolute_transform->transpose_xy,
+ &overlap,
+ &self->current_area,
+ &transformed);
+
+ int16_t start_x = (transformed.x1 - self->current_area.x1);
+ int16_t end_x = (transformed.x2 - self->current_area.x1);
+ int16_t start_y = (transformed.y1 - self->current_area.y1);
+ int16_t end_y = (transformed.y2 - self->current_area.y1);
+
+ int16_t y_shift = 0;
+ int16_t x_shift = 0;
+ if ((self->absolute_transform->dx < 0) != flip_x) {
+ x_shift = area->x2 - overlap.x2;
+ } else {
+ x_shift = overlap.x1 - area->x1;
+ }
+ if ((self->absolute_transform->dy < 0) != flip_y) {
+ y_shift = area->y2 - overlap.y2;
+ } else {
+ y_shift = overlap.y1 - area->y1;
+ }
+
+ // This untransposes x and y so it aligns with bitmap rows.
+ if (self->transpose_xy != self->absolute_transform->transpose_xy) {
+ int16_t temp_stride = x_stride;
+ x_stride = y_stride;
+ y_stride = temp_stride;
+ int16_t temp_shift = x_shift;
+ x_shift = y_shift;
+ y_shift = temp_shift;
+ }
+
+ uint8_t pixels_per_byte = 8 / colorspace->depth;
+
+ displayio_input_pixel_t input_pixel;
+ displayio_output_pixel_t output_pixel;
+
+ for (input_pixel.y = start_y; input_pixel.y < end_y; ++input_pixel.y) {
+ int16_t row_start = start + (input_pixel.y - start_y + y_shift) * y_stride; // in pixels
+ int16_t local_y = input_pixel.y / self->absolute_transform->scale;
+ for (input_pixel.x = start_x; input_pixel.x < end_x; ++input_pixel.x) {
+ // Compute the destination pixel in the buffer and mask based on the transformations.
+ int16_t offset = row_start + (input_pixel.x - start_x + x_shift) * x_stride; // in pixels
+
+ // This is super useful for debugging out of range accesses. Uncomment to use.
+ // if (offset < 0 || offset >= (int32_t) displayio_area_size(area)) {
+ // asm("bkpt");
+ // }
+
+ // Check the mask first to see if the pixel has already been set.
+ if ((mask[offset / 32] & (1 << (offset % 32))) != 0) {
+ continue;
+ }
+ int16_t local_x = input_pixel.x / self->absolute_transform->scale;
+ uint16_t tile_location = ((local_y / self->tile_height + self->top_left_y) % self->height_in_tiles) * self->width_in_tiles + (local_x / self->tile_width + self->top_left_x) % self->width_in_tiles;
+ input_pixel.tile = tiles[tile_location];
+ input_pixel.tile_x = (input_pixel.tile % self->bitmap_width_in_tiles) * self->tile_width + local_x % self->tile_width;
+ input_pixel.tile_y = (input_pixel.tile / self->bitmap_width_in_tiles) * self->tile_height + local_y % self->tile_height;
+
+ output_pixel.pixel = 0;
+ input_pixel.pixel = 0;
+
+ // We always want to read bitmap pixels by row first and then transpose into the destination
+ // buffer because most bitmaps are row associated.
+ if (mp_obj_is_type(self->bitmap, &displayio_bitmap_type)) {
+ input_pixel.pixel = common_hal_displayio_bitmap_get_pixel(self->bitmap, input_pixel.tile_x, input_pixel.tile_y);
+ } else if (mp_obj_is_type(self->bitmap, &displayio_shape_type)) {
+ input_pixel.pixel = common_hal_displayio_shape_get_pixel(self->bitmap, input_pixel.tile_x, input_pixel.tile_y);
+ } else if (mp_obj_is_type(self->bitmap, &displayio_ondiskbitmap_type)) {
+ input_pixel.pixel = common_hal_displayio_ondiskbitmap_get_pixel(self->bitmap, input_pixel.tile_x, input_pixel.tile_y);
+ }
+
+ output_pixel.opaque = true;
+ if (self->pixel_shader == mp_const_none) {
+ output_pixel.pixel = input_pixel.pixel;
+ } else if (mp_obj_is_type(self->pixel_shader, &displayio_palette_type)) {
+ output_pixel.opaque = displayio_palette_get_color(self->pixel_shader, colorspace, input_pixel.pixel, &output_pixel.pixel);
+ } else if (mp_obj_is_type(self->pixel_shader, &displayio_colorconverter_type)) {
+ displayio_colorconverter_convert(self->pixel_shader, colorspace, &input_pixel, &output_pixel);
+ }
+ if (!output_pixel.opaque) {
+ // A pixel is transparent so we haven't fully covered the area ourselves.
+ full_coverage = false;
+ } else {
+ mask[offset / 32] |= 1 << (offset % 32);
+ if (colorspace->depth == 16) {
+ *(((uint16_t *)buffer) + offset) = output_pixel.pixel;
+ } else if (colorspace->depth == 32) {
+ *(((uint32_t *)buffer) + offset) = output_pixel.pixel;
+ } else if (colorspace->depth == 8) {
+ *(((uint8_t *)buffer) + offset) = output_pixel.pixel;
+ } else if (colorspace->depth < 8) {
+ // Reorder the offsets to pack multiple rows into a byte (meaning they share a column).
+ if (!colorspace->pixels_in_byte_share_row) {
+ uint16_t width = displayio_area_width(area);
+ uint16_t row = offset / width;
+ uint16_t col = offset % width;
+ // Dividing by pixels_per_byte does truncated division even if we multiply it back out.
+ offset = col * pixels_per_byte + (row / pixels_per_byte) * pixels_per_byte * width + row % pixels_per_byte;
+ // Also useful for validating that the bitpacking worked correctly.
+ // if (offset > displayio_area_size(area)) {
+ // asm("bkpt");
+ // }
+ }
+ uint8_t shift = (offset % pixels_per_byte) * colorspace->depth;
+ if (colorspace->reverse_pixels_in_byte) {
+ // Reverse the shift by subtracting it from the leftmost shift.
+ shift = (pixels_per_byte - 1) * colorspace->depth - shift;
+ }
+ ((uint8_t *)buffer)[offset / pixels_per_byte] |= output_pixel.pixel << shift;
+ }
+ }
+ (void)input_pixel;
+ }
+ }
+ return full_coverage;
+}
+
+void displayio_tilegrid_finish_refresh(displayio_tilegrid_t *self) {
+ bool first_draw = self->previous_area.x1 == self->previous_area.x2;
+ bool hidden = self->hidden || self->hidden_by_parent;
+ if (!first_draw && hidden) {
+ self->previous_area.x2 = self->previous_area.x1;
+ } else if (self->moved || first_draw) {
+ displayio_area_copy(&self->current_area, &self->previous_area);
+ }
+
+ self->moved = false;
+ self->full_change = false;
+ self->partial_change = false;
+ if (mp_obj_is_type(self->pixel_shader, &displayio_palette_type)) {
+ displayio_palette_finish_refresh(self->pixel_shader);
+ } else if (mp_obj_is_type(self->pixel_shader, &displayio_colorconverter_type)) {
+ displayio_colorconverter_finish_refresh(self->pixel_shader);
+ }
+ if (mp_obj_is_type(self->bitmap, &displayio_bitmap_type)) {
+ displayio_bitmap_finish_refresh(self->bitmap);
+ } else if (mp_obj_is_type(self->bitmap, &displayio_shape_type)) {
+ displayio_shape_finish_refresh(self->bitmap);
+ } else if (mp_obj_is_type(self->bitmap, &displayio_ondiskbitmap_type)) {
+ // OnDiskBitmap changes will trigger a complete reload so no need to
+ // track changes.
+ }
+ // TODO(tannewt): We could double buffer changes to position and move them over here.
+ // That way they won't change during a refresh and tear.
+}
+
+displayio_area_t *displayio_tilegrid_get_refresh_areas(displayio_tilegrid_t *self, displayio_area_t *tail) {
+ bool first_draw = self->previous_area.x1 == self->previous_area.x2;
+ bool hidden = self->hidden || self->hidden_by_parent;
+ // Check hidden first because it trumps all other changes.
+ if (hidden) {
+ if (!first_draw) {
+ self->previous_area.next = tail;
+ return &self->previous_area;
+ } else {
+ return tail;
+ }
+ } else if (self->moved && !first_draw) {
+ displayio_area_union(&self->previous_area, &self->current_area, &self->dirty_area);
+ if (displayio_area_size(&self->dirty_area) <= 2U * self->pixel_width * self->pixel_height) {
+ self->dirty_area.next = tail;
+ return &self->dirty_area;
+ }
+ self->previous_area.next = tail;
+ self->current_area.next = &self->previous_area;
+ return &self->current_area;
+ }
+
+ // If we have an in-memory bitmap, then check it for modifications.
+ if (mp_obj_is_type(self->bitmap, &displayio_bitmap_type)) {
+ displayio_area_t *refresh_area = displayio_bitmap_get_refresh_areas(self->bitmap, tail);
+ if (refresh_area != tail) {
+ // Special case a TileGrid that shows a full bitmap and use its
+ // dirty area. Copy it to ours so we can transform it.
+ if (self->tiles_in_bitmap == 1) {
+ displayio_area_copy(refresh_area, &self->dirty_area);
+ self->partial_change = true;
+ } else {
+ self->full_change = true;
+ }
+ }
+ } else if (mp_obj_is_type(self->bitmap, &displayio_shape_type)) {
+ displayio_area_t *refresh_area = displayio_shape_get_refresh_areas(self->bitmap, tail);
+ if (refresh_area != tail) {
+ displayio_area_copy(refresh_area, &self->dirty_area);
+ self->partial_change = true;
+ }
+ }
+
+ self->full_change = self->full_change ||
+ (mp_obj_is_type(self->pixel_shader, &displayio_palette_type) &&
+ displayio_palette_needs_refresh(self->pixel_shader)) ||
+ (mp_obj_is_type(self->pixel_shader, &displayio_colorconverter_type) &&
+ displayio_colorconverter_needs_refresh(self->pixel_shader));
+ if (self->full_change || first_draw) {
+ self->current_area.next = tail;
+ return &self->current_area;
+ }
+
+ if (self->partial_change) {
+ int16_t x = self->x;
+ int16_t y = self->y;
+ if (self->absolute_transform->transpose_xy) {
+ int16_t temp = y;
+ y = x;
+ x = temp;
+ }
+ int16_t x1 = self->dirty_area.x1;
+ int16_t x2 = self->dirty_area.x2;
+ if (self->flip_x) {
+ x1 = self->pixel_width - x1;
+ x2 = self->pixel_width - x2;
+ }
+ int16_t y1 = self->dirty_area.y1;
+ int16_t y2 = self->dirty_area.y2;
+ if (self->flip_y) {
+ y1 = self->pixel_height - y1;
+ y2 = self->pixel_height - y2;
+ }
+ if (self->transpose_xy != self->absolute_transform->transpose_xy) {
+ int16_t temp1 = y1, temp2 = y2;
+ y1 = x1;
+ x1 = temp1;
+ y2 = x2;
+ x2 = temp2;
+ }
+ self->dirty_area.x1 = self->absolute_transform->x + self->absolute_transform->dx * (x + x1);
+ self->dirty_area.y1 = self->absolute_transform->y + self->absolute_transform->dy * (y + y1);
+ self->dirty_area.x2 = self->absolute_transform->x + self->absolute_transform->dx * (x + x2);
+ self->dirty_area.y2 = self->absolute_transform->y + self->absolute_transform->dy * (y + y2);
+ if (self->dirty_area.y2 < self->dirty_area.y1) {
+ int16_t temp = self->dirty_area.y2;
+ self->dirty_area.y2 = self->dirty_area.y1;
+ self->dirty_area.y1 = temp;
+ }
+ if (self->dirty_area.x2 < self->dirty_area.x1) {
+ int16_t temp = self->dirty_area.x2;
+ self->dirty_area.x2 = self->dirty_area.x1;
+ self->dirty_area.x1 = temp;
+ }
+
+ self->dirty_area.next = tail;
+ return &self->dirty_area;
+ }
+ return tail;
+}
diff --git a/circuitpython/shared-module/displayio/TileGrid.h b/circuitpython/shared-module/displayio/TileGrid.h
new file mode 100644
index 0000000..b5aab51
--- /dev/null
+++ b/circuitpython/shared-module/displayio/TileGrid.h
@@ -0,0 +1,89 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Scott Shawcroft 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.
+ */
+
+#ifndef MICROPY_INCLUDED_SHARED_MODULE_DISPLAYIO_TILEGRID_H
+#define MICROPY_INCLUDED_SHARED_MODULE_DISPLAYIO_TILEGRID_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "py/obj.h"
+#include "shared-module/displayio/area.h"
+#include "shared-module/displayio/Palette.h"
+
+typedef struct {
+ mp_obj_base_t base;
+ mp_obj_t bitmap;
+ mp_obj_t pixel_shader;
+ int16_t x;
+ int16_t y;
+ uint16_t pixel_width;
+ uint16_t pixel_height;
+ uint16_t bitmap_width_in_tiles;
+ ;
+ uint16_t tiles_in_bitmap;
+ uint16_t width_in_tiles;
+ uint16_t height_in_tiles;
+ uint16_t tile_width;
+ uint16_t tile_height;
+ uint16_t top_left_x;
+ uint16_t top_left_y;
+ uint8_t *tiles;
+ const displayio_buffer_transform_t *absolute_transform;
+ displayio_area_t dirty_area; // Stored as a relative area until the refresh area is fetched.
+ displayio_area_t previous_area; // Stored as an absolute area.
+ displayio_area_t current_area; // Stored as an absolute area so it applies across frames.
+ bool partial_change : 1;
+ bool full_change : 1;
+ bool moved : 1;
+ bool inline_tiles : 1;
+ bool in_group : 1;
+ bool flip_x : 1;
+ bool flip_y : 1;
+ bool transpose_xy : 1;
+ bool hidden : 1;
+ bool hidden_by_parent : 1;
+ uint8_t padding : 6;
+} displayio_tilegrid_t;
+
+void displayio_tilegrid_set_hidden_by_parent(displayio_tilegrid_t *self, bool hidden);
+
+// Updating the screen is a three stage process.
+
+// The first stage is used to determine i
+displayio_area_t *displayio_tilegrid_get_refresh_areas(displayio_tilegrid_t *self, displayio_area_t *tail);
+
+// Area is always in absolute screen coordinates. Update transform is used to inform TileGrids how
+// they relate to it.
+bool displayio_tilegrid_fill_area(displayio_tilegrid_t *self, const _displayio_colorspace_t *colorspace, const displayio_area_t *area, uint32_t *mask, uint32_t *buffer);
+void displayio_tilegrid_update_transform(displayio_tilegrid_t *group, const displayio_buffer_transform_t *parent_transform);
+
+// Fills in area with the maximum bounds of all related pixels in the last rendered frame. Returns
+// false if the tilegrid wasn't rendered in the last frame.
+bool displayio_tilegrid_get_previous_area(displayio_tilegrid_t *self, displayio_area_t *area);
+void displayio_tilegrid_finish_refresh(displayio_tilegrid_t *self);
+
+#endif // MICROPY_INCLUDED_SHARED_MODULE_DISPLAYIO_TILEGRID_H
diff --git a/circuitpython/shared-module/displayio/__init__.c b/circuitpython/shared-module/displayio/__init__.c
new file mode 100644
index 0000000..87962df
--- /dev/null
+++ b/circuitpython/shared-module/displayio/__init__.c
@@ -0,0 +1,360 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Scott Shawcroft 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.
+ */
+
+#include <string.h>
+
+#include "shared-module/displayio/__init__.h"
+
+#include "shared/runtime/interrupt_char.h"
+#include "py/runtime.h"
+#include "shared-bindings/board/__init__.h"
+#include "shared-bindings/displayio/Bitmap.h"
+#include "shared-bindings/displayio/Display.h"
+#include "shared-bindings/displayio/Group.h"
+#include "shared-bindings/displayio/Palette.h"
+#include "shared-module/displayio/area.h"
+#include "supervisor/shared/display.h"
+#include "supervisor/shared/reload.h"
+#include "supervisor/memory.h"
+
+#include "supervisor/spi_flash_api.h"
+#include "py/mpconfig.h"
+
+#if CIRCUITPY_SHARPDISPLAY
+#include "shared-bindings/sharpdisplay/SharpMemoryFramebuffer.h"
+#include "shared-module/sharpdisplay/SharpMemoryFramebuffer.h"
+#endif
+
+primary_display_t displays[CIRCUITPY_DISPLAY_LIMIT];
+
+displayio_buffer_transform_t null_transform = {
+ .x = 0,
+ .y = 0,
+ .dx = 1,
+ .dy = 1,
+ .scale = 1,
+ .width = 0,
+ .height = 0,
+ .mirror_x = false,
+ .mirror_y = false,
+ .transpose_xy = false
+};
+
+#if CIRCUITPY_RGBMATRIX || CIRCUITPY_IS31FL3741 || CIRCUITPY_VIDEOCORE
+STATIC bool any_display_uses_this_framebuffer(mp_obj_base_t *obj) {
+ for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) {
+ if (displays[i].display_base.type == &framebufferio_framebufferdisplay_type) {
+ framebufferio_framebufferdisplay_obj_t *display = &displays[i].framebuffer_display;
+ if (display->framebuffer == obj) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+#endif
+
+
+void displayio_background(void) {
+ if (mp_hal_is_interrupted()) {
+ return;
+ }
+ if (autoreload_ready()) {
+ // Reload is about to happen, so don't redisplay.
+ return;
+ }
+
+
+ for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) {
+ if (displays[i].display.base.type == NULL || displays[i].display.base.type == &mp_type_NoneType) {
+ // Skip null display.
+ continue;
+ }
+ if (displays[i].display.base.type == &displayio_display_type) {
+ displayio_display_background(&displays[i].display);
+ #if CIRCUITPY_FRAMEBUFFERIO
+ } else if (displays[i].framebuffer_display.base.type == &framebufferio_framebufferdisplay_type) {
+ framebufferio_framebufferdisplay_background(&displays[i].framebuffer_display);
+ #endif
+ } else if (displays[i].epaper_display.base.type == &displayio_epaperdisplay_type) {
+ displayio_epaperdisplay_background(&displays[i].epaper_display);
+ }
+ }
+
+}
+
+void common_hal_displayio_release_displays(void) {
+ // Release displays before busses so that they can send any final commands to turn the display
+ // off properly.
+ for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) {
+ mp_const_obj_t display_type = displays[i].display.base.type;
+ if (display_type == NULL || display_type == &mp_type_NoneType) {
+ continue;
+ } else if (display_type == &displayio_display_type) {
+ release_display(&displays[i].display);
+ } else if (display_type == &displayio_epaperdisplay_type) {
+ release_epaperdisplay(&displays[i].epaper_display);
+ #if CIRCUITPY_FRAMEBUFFERIO
+ } else if (display_type == &framebufferio_framebufferdisplay_type) {
+ release_framebufferdisplay(&displays[i].framebuffer_display);
+ #endif
+ }
+ displays[i].display.base.type = &mp_type_NoneType;
+ }
+ for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) {
+ mp_const_obj_t bus_type = displays[i].fourwire_bus.base.type;
+ if (bus_type == NULL || bus_type == &mp_type_NoneType) {
+ continue;
+ } else if (bus_type == &displayio_fourwire_type) {
+ common_hal_displayio_fourwire_deinit(&displays[i].fourwire_bus);
+ } else if (bus_type == &displayio_i2cdisplay_type) {
+ common_hal_displayio_i2cdisplay_deinit(&displays[i].i2cdisplay_bus);
+ #if CIRCUITPY_PARALLELDISPLAY
+ } else if (bus_type == &paralleldisplay_parallelbus_type) {
+ common_hal_paralleldisplay_parallelbus_deinit(&displays[i].parallel_bus);
+ #endif
+ #if CIRCUITPY_RGBMATRIX
+ } else if (bus_type == &rgbmatrix_RGBMatrix_type) {
+ common_hal_rgbmatrix_rgbmatrix_deinit(&displays[i].rgbmatrix);
+ #endif
+ #if CIRCUITPY_IS31FL3741
+ } else if (bus_type == &is31fl3741_FrameBuffer_type) {
+ common_hal_is31fl3741_FrameBuffer_deinit(&displays[i].is31fl3741);
+ #endif
+ #if CIRCUITPY_SHARPDISPLAY
+ } else if (displays[i].bus_base.type == &sharpdisplay_framebuffer_type) {
+ common_hal_sharpdisplay_framebuffer_deinit(&displays[i].sharpdisplay);
+ #endif
+ #if CIRCUITPY_VIDEOCORE
+ } else if (displays[i].bus_base.type == &videocore_framebuffer_type) {
+ common_hal_videocore_framebuffer_deinit(&displays[i].videocore);
+ #endif
+ }
+ displays[i].fourwire_bus.base.type = &mp_type_NoneType;
+ }
+
+ supervisor_stop_terminal();
+}
+
+void reset_displays(void) {
+ // The SPI buses used by FourWires may be allocated on the heap so we need to move them inline.
+ for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) {
+ if (displays[i].fourwire_bus.base.type == &displayio_fourwire_type) {
+ displayio_fourwire_obj_t *fourwire = &displays[i].fourwire_bus;
+ if (((size_t)fourwire->bus) < ((size_t)&displays) ||
+ ((size_t)fourwire->bus) > ((size_t)&displays + CIRCUITPY_DISPLAY_LIMIT)) {
+ busio_spi_obj_t *original_spi = fourwire->bus;
+ #if CIRCUITPY_BOARD_SPI
+ // We don't need to move original_spi if it is a board.SPI object because it is
+ // statically allocated already. (Doing so would also make it impossible to reference in
+ // a subsequent VM run.)
+ if (common_hal_board_is_spi(original_spi)) {
+ continue;
+ }
+ #endif
+ #ifdef BOARD_USE_INTERNAL_SPI
+ if (original_spi == (mp_obj_t)(&supervisor_flash_spi_bus)) {
+ continue;
+ }
+ #endif
+ memcpy(&fourwire->inline_bus, original_spi, sizeof(busio_spi_obj_t));
+ fourwire->bus = &fourwire->inline_bus;
+ // Check for other displays that use the same spi bus and swap them too.
+ for (uint8_t j = i + 1; j < CIRCUITPY_DISPLAY_LIMIT; j++) {
+ if (displays[i].fourwire_bus.base.type == &displayio_fourwire_type &&
+ displays[i].fourwire_bus.bus == original_spi) {
+ displays[i].fourwire_bus.bus = &fourwire->inline_bus;
+ }
+ }
+ }
+ } else if (displays[i].i2cdisplay_bus.base.type == &displayio_i2cdisplay_type) {
+ displayio_i2cdisplay_obj_t *i2c = &displays[i].i2cdisplay_bus;
+ if (((size_t)i2c->bus) < ((size_t)&displays) ||
+ ((size_t)i2c->bus) > ((size_t)&displays + CIRCUITPY_DISPLAY_LIMIT)) {
+ busio_i2c_obj_t *original_i2c = i2c->bus;
+ #if CIRCUITPY_BOARD_I2C
+ // We don't need to move original_i2c if it is a board.I2C object because it is
+ // statically allocated already. (Doing so would also make it impossible to reference in
+ // a subsequent VM run.)
+ if (common_hal_board_is_i2c(original_i2c)) {
+ continue;
+ }
+ #endif
+ memcpy(&i2c->inline_bus, original_i2c, sizeof(busio_i2c_obj_t));
+ i2c->bus = &i2c->inline_bus;
+ // Check for other displays that use the same i2c bus and swap them too.
+ for (uint8_t j = i + 1; j < CIRCUITPY_DISPLAY_LIMIT; j++) {
+ if (displays[i].i2cdisplay_bus.base.type == &displayio_i2cdisplay_type &&
+ displays[i].i2cdisplay_bus.bus == original_i2c) {
+ displays[i].i2cdisplay_bus.bus = &i2c->inline_bus;
+ }
+ }
+ }
+ #if CIRCUITPY_RGBMATRIX
+ } else if (displays[i].rgbmatrix.base.type == &rgbmatrix_RGBMatrix_type) {
+ rgbmatrix_rgbmatrix_obj_t *pm = &displays[i].rgbmatrix;
+ if (!any_display_uses_this_framebuffer(&pm->base)) {
+ common_hal_rgbmatrix_rgbmatrix_deinit(pm);
+ } else {
+ common_hal_rgbmatrix_rgbmatrix_set_paused(pm, true);
+ }
+ #endif
+ #if CIRCUITPY_IS31FL3741
+ } else if (displays[i].is31fl3741.base.type == &is31fl3741_FrameBuffer_type) {
+ is31fl3741_FrameBuffer_obj_t *is31fb = &displays[i].is31fl3741;
+
+ if (((uint32_t)is31fb->is31fl3741->i2c) < ((uint32_t)&displays) ||
+ ((uint32_t)is31fb->is31fl3741->i2c) > ((uint32_t)&displays + CIRCUITPY_DISPLAY_LIMIT)) {
+ #if CIRCUITPY_BOARD_I2C
+ // We don't need to move original_i2c if it is the board.I2C object because it is
+ // statically allocated already. (Doing so would also make it impossible to reference in
+ // a subsequent VM run.)
+ if (common_hal_board_is_i2c(is31fb->is31fl3741->i2c)) {
+ continue;
+ }
+ #endif
+
+ is31fl3741_IS31FL3741_obj_t *original_is31 = is31fb->is31fl3741;
+ memcpy(&is31fb->inline_is31fl3741, original_is31, sizeof(is31fl3741_IS31FL3741_obj_t));
+ is31fb->is31fl3741 = &is31fb->inline_is31fl3741;
+
+ busio_i2c_obj_t *original_i2c = is31fb->is31fl3741->i2c;
+ memcpy(&is31fb->is31fl3741->inline_i2c, original_i2c, sizeof(busio_i2c_obj_t));
+ is31fb->is31fl3741->i2c = &is31fb->is31fl3741->inline_i2c;
+ }
+
+ if (!any_display_uses_this_framebuffer(&is31fb->base)) {
+ common_hal_is31fl3741_FrameBuffer_deinit(is31fb);
+ } else {
+ common_hal_is31fl3741_FrameBuffer_set_paused(is31fb, true);
+ }
+ #endif
+ #if CIRCUITPY_SHARPDISPLAY
+ } else if (displays[i].bus_base.type == &sharpdisplay_framebuffer_type) {
+ sharpdisplay_framebuffer_obj_t *sharp = &displays[i].sharpdisplay;
+ common_hal_sharpdisplay_framebuffer_reset(sharp);
+ #endif
+ #if CIRCUITPY_VIDEOCORE
+ } else if (displays[i].bus_base.type == &videocore_framebuffer_type) {
+ videocore_framebuffer_obj_t *vc = &displays[i].videocore;
+ if (!any_display_uses_this_framebuffer(&vc->base)) {
+ common_hal_videocore_framebuffer_deinit(vc);
+ }
+ // The framebuffer is allocated outside of the heap so it doesn't
+ // need to be moved.
+ #endif
+ } else {
+ // Not an active display bus.
+ continue;
+ }
+ }
+
+ for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) {
+ // Reset the displayed group. Only the first will get the terminal but
+ // that's ok.
+ if (displays[i].display.base.type == &displayio_display_type) {
+ reset_display(&displays[i].display);
+ } else if (displays[i].epaper_display.base.type == &displayio_epaperdisplay_type) {
+ displayio_epaperdisplay_obj_t *display = &displays[i].epaper_display;
+ common_hal_displayio_epaperdisplay_show(display, NULL);
+ #if CIRCUITPY_FRAMEBUFFERIO
+ } else if (displays[i].framebuffer_display.base.type == &framebufferio_framebufferdisplay_type) {
+ framebufferio_framebufferdisplay_reset(&displays[i].framebuffer_display);
+ #endif
+ }
+ }
+}
+
+void displayio_gc_collect(void) {
+ for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) {
+ #if CIRCUITPY_RGBMATRIX
+ if (displays[i].rgbmatrix.base.type == &rgbmatrix_RGBMatrix_type) {
+ rgbmatrix_rgbmatrix_collect_ptrs(&displays[i].rgbmatrix);
+ }
+ #endif
+ #if CIRCUITPY_IS31FL3741
+ if (displays[i].is31fl3741.base.type == &is31fl3741_FrameBuffer_type) {
+ is31fl3741_FrameBuffer_collect_ptrs(&displays[i].is31fl3741);
+ }
+ #endif
+ #if CIRCUITPY_SHARPDISPLAY
+ if (displays[i].bus_base.type == &sharpdisplay_framebuffer_type) {
+ common_hal_sharpdisplay_framebuffer_collect_ptrs(&displays[i].sharpdisplay);
+ }
+ #endif
+
+ if (displays[i].display.base.type == NULL) {
+ continue;
+ }
+
+ // Alternatively, we could use gc_collect_root over the whole object,
+ // but this is more precise, and is the only field that needs marking.
+ if (displays[i].display.base.type == &displayio_display_type) {
+ displayio_display_collect_ptrs(&displays[i].display);
+ #if CIRCUITPY_FRAMEBUFFERIO
+ } else if (displays[i].framebuffer_display.base.type == &framebufferio_framebufferdisplay_type) {
+ framebufferio_framebufferdisplay_collect_ptrs(&displays[i].framebuffer_display);
+ #endif
+ } else if (displays[i].epaper_display.base.type == &displayio_epaperdisplay_type) {
+ displayio_epaperdisplay_collect_ptrs(&displays[i].epaper_display);
+ }
+ }
+}
+
+primary_display_t *allocate_display(void) {
+ for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) {
+ mp_const_obj_t display_type = displays[i].display.base.type;
+ if (display_type == NULL || display_type == &mp_type_NoneType) {
+ return &displays[i];
+ }
+ }
+ return NULL;
+}
+
+primary_display_t *allocate_display_or_raise(void) {
+ primary_display_t *result = allocate_display();
+ if (result) {
+ return result;
+ }
+ mp_raise_RuntimeError(translate("Too many displays"));
+}
+primary_display_t *allocate_display_bus(void) {
+ for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) {
+ mp_const_obj_t display_bus_type = displays[i].bus_base.type;
+ if (display_bus_type == NULL || display_bus_type == &mp_type_NoneType) {
+ return &displays[i];
+ }
+ }
+ return NULL;
+}
+
+primary_display_t *allocate_display_bus_or_raise(void) {
+ primary_display_t *result = allocate_display_bus();
+ if (result) {
+ return result;
+ }
+ mp_raise_RuntimeError(translate("Too many display busses"));
+}
diff --git a/circuitpython/shared-module/displayio/__init__.h b/circuitpython/shared-module/displayio/__init__.h
new file mode 100644
index 0000000..c1954b1
--- /dev/null
+++ b/circuitpython/shared-module/displayio/__init__.h
@@ -0,0 +1,98 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Scott Shawcroft 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.
+ */
+
+#ifndef MICROPY_INCLUDED_SHARED_MODULE_DISPLAYIO___INIT___H
+#define MICROPY_INCLUDED_SHARED_MODULE_DISPLAYIO___INIT___H
+
+#include "shared-bindings/displayio/Display.h"
+#include "shared-bindings/displayio/EPaperDisplay.h"
+#if CIRCUITPY_FRAMEBUFFERIO
+#include "shared-bindings/framebufferio/FramebufferDisplay.h"
+#endif
+#include "shared-bindings/displayio/FourWire.h"
+#include "shared-bindings/displayio/Group.h"
+#include "shared-bindings/displayio/I2CDisplay.h"
+#if CIRCUITPY_PARALLELDISPLAY
+#include "shared-bindings/paralleldisplay/ParallelBus.h"
+#endif
+#if CIRCUITPY_RGBMATRIX
+#include "shared-bindings/rgbmatrix/RGBMatrix.h"
+#endif
+#if CIRCUITPY_IS31FL3741
+#include "shared-bindings/is31fl3741/FrameBuffer.h"
+#endif
+#if CIRCUITPY_SHARPDISPLAY
+#include "shared-module/sharpdisplay/SharpMemoryFramebuffer.h"
+#endif
+#if CIRCUITPY_VIDEOCORE
+#include "bindings/videocore/Framebuffer.h"
+#endif
+
+typedef struct {
+ union {
+ mp_obj_base_t bus_base;
+ displayio_fourwire_obj_t fourwire_bus;
+ displayio_i2cdisplay_obj_t i2cdisplay_bus;
+ #if CIRCUITPY_PARALLELDISPLAY
+ paralleldisplay_parallelbus_obj_t parallel_bus;
+ #endif
+ #if CIRCUITPY_RGBMATRIX
+ rgbmatrix_rgbmatrix_obj_t rgbmatrix;
+ #endif
+ #if CIRCUITPY_IS31FL3741
+ is31fl3741_FrameBuffer_obj_t is31fl3741;
+ #endif
+ #if CIRCUITPY_SHARPDISPLAY
+ sharpdisplay_framebuffer_obj_t sharpdisplay;
+ #endif
+ #if CIRCUITPY_VIDEOCORE
+ videocore_framebuffer_obj_t videocore;
+ #endif
+ };
+ union {
+ mp_obj_base_t display_base;
+ displayio_display_obj_t display;
+ displayio_epaperdisplay_obj_t epaper_display;
+ #if CIRCUITPY_FRAMEBUFFERIO
+ framebufferio_framebufferdisplay_obj_t framebuffer_display;
+ #endif
+ };
+} primary_display_t;
+
+extern primary_display_t displays[CIRCUITPY_DISPLAY_LIMIT];
+
+extern displayio_group_t circuitpython_splash;
+
+void displayio_background(void);
+void reset_displays(void);
+void displayio_gc_collect(void);
+
+primary_display_t *allocate_display(void);
+primary_display_t *allocate_display_or_raise(void);
+primary_display_t *allocate_display_bus(void);
+primary_display_t *allocate_display_bus_or_raise(void);
+
+#endif // MICROPY_INCLUDED_SHARED_MODULE_DISPLAYIO___INIT___H
diff --git a/circuitpython/shared-module/displayio/area.c b/circuitpython/shared-module/displayio/area.c
new file mode 100644
index 0000000..6d0f94d
--- /dev/null
+++ b/circuitpython/shared-module/displayio/area.c
@@ -0,0 +1,161 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Scott Shawcroft 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.
+ */
+
+#include "shared-module/displayio/area.h"
+
+#include "py/misc.h"
+
+void displayio_area_copy(const displayio_area_t *src, displayio_area_t *dst) {
+ dst->x1 = src->x1;
+ dst->y1 = src->y1;
+ dst->x2 = src->x2;
+ dst->y2 = src->y2;
+}
+
+void displayio_area_scale(displayio_area_t *area, uint16_t scale) {
+ area->x1 *= scale;
+ area->y1 *= scale;
+ area->x2 *= scale;
+ area->y2 *= scale;
+}
+
+void displayio_area_shift(displayio_area_t *area, int16_t dx, int16_t dy) {
+ area->x1 += dx;
+ area->y1 += dy;
+ area->x2 += dx;
+ area->y2 += dy;
+}
+
+bool displayio_area_compute_overlap(const displayio_area_t *a,
+ const displayio_area_t *b,
+ displayio_area_t *overlap) {
+ overlap->x1 = a->x1;
+ if (b->x1 > overlap->x1) {
+ overlap->x1 = b->x1;
+ }
+ overlap->x2 = a->x2;
+ if (b->x2 < overlap->x2) {
+ overlap->x2 = b->x2;
+ }
+ if (overlap->x1 >= overlap->x2) {
+ return false;
+ }
+ overlap->y1 = a->y1;
+ if (b->y1 > overlap->y1) {
+ overlap->y1 = b->y1;
+ }
+ overlap->y2 = a->y2;
+ if (b->y2 < overlap->y2) {
+ overlap->y2 = b->y2;
+ }
+ if (overlap->y1 >= overlap->y2) {
+ return false;
+ }
+ return true;
+}
+
+bool displayio_area_empty(const displayio_area_t *a) {
+ return (a->x1 == a->x2) || (a->y1 == a->y2);
+}
+
+void displayio_area_canon(displayio_area_t *a) {
+ if (a->x1 > a->x2) {
+ int16_t t = a->x1;
+ a->x1 = a->x2;
+ a->x2 = t;
+ }
+ if (a->y1 > a->y2) {
+ int16_t t = a->y1;
+ a->y1 = a->y2;
+ a->y2 = t;
+ }
+}
+
+void displayio_area_union(const displayio_area_t *a,
+ const displayio_area_t *b,
+ displayio_area_t *u) {
+
+ if (displayio_area_empty(a)) {
+ displayio_area_copy(b, u);
+ return;
+ }
+ if (displayio_area_empty(b)) {
+ displayio_area_copy(a, u);
+ return;
+ }
+ u->x1 = MIN(a->x1, b->x1);
+ u->y1 = MIN(a->y1, b->y1);
+ u->x2 = MAX(a->x2, b->x2);
+ u->y2 = MAX(a->y2, b->y2);
+}
+
+uint16_t displayio_area_width(const displayio_area_t *area) {
+ return area->x2 - area->x1;
+}
+
+uint16_t displayio_area_height(const displayio_area_t *area) {
+ return area->y2 - area->y1;
+}
+
+uint32_t displayio_area_size(const displayio_area_t *area) {
+ return displayio_area_width(area) * displayio_area_height(area);
+}
+
+bool displayio_area_equal(const displayio_area_t *a, const displayio_area_t *b) {
+ return a->x1 == b->x1 &&
+ a->y1 == b->y1 &&
+ a->x2 == b->x2 &&
+ a->y2 == b->y2;
+}
+
+// Original and whole must be in the same coordinate space.
+void displayio_area_transform_within(bool mirror_x, bool mirror_y, bool transpose_xy,
+ const displayio_area_t *original,
+ const displayio_area_t *whole,
+ displayio_area_t *transformed) {
+ if (mirror_x) {
+ transformed->x1 = whole->x1 + (whole->x2 - original->x2);
+ transformed->x2 = whole->x2 - (original->x1 - whole->x1);
+ } else {
+ transformed->x1 = original->x1;
+ transformed->x2 = original->x2;
+ }
+ if (mirror_y) {
+ transformed->y1 = whole->y1 + (whole->y2 - original->y2);
+ transformed->y2 = whole->y2 - (original->y1 - whole->y1);
+ } else {
+ transformed->y1 = original->y1;
+ transformed->y2 = original->y2;
+ }
+ if (transpose_xy) {
+ int16_t y1 = transformed->y1;
+ int16_t y2 = transformed->y2;
+ transformed->y1 = whole->y1 + (transformed->x1 - whole->x1);
+ transformed->y2 = whole->y1 + (transformed->x2 - whole->x1);
+ transformed->x2 = whole->x1 + (y2 - whole->y1);
+ transformed->x1 = whole->x1 + (y1 - whole->y1);
+ }
+}
diff --git a/circuitpython/shared-module/displayio/area.h b/circuitpython/shared-module/displayio/area.h
new file mode 100644
index 0000000..47cb48b
--- /dev/null
+++ b/circuitpython/shared-module/displayio/area.h
@@ -0,0 +1,80 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Scott Shawcroft 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.
+ */
+
+#ifndef MICROPY_INCLUDED_SHARED_MODULE_DISPLAYIO_AREA_H
+#define MICROPY_INCLUDED_SHARED_MODULE_DISPLAYIO_AREA_H
+
+#include <stdint.h>
+#include <stdbool.h>
+
+// Implementations are in area.c
+typedef struct _displayio_area_t displayio_area_t;
+
+struct _displayio_area_t {
+ int16_t x1;
+ int16_t y1;
+ int16_t x2; // Second point is exclusive.
+ int16_t y2;
+ const displayio_area_t *next; // Next area in the linked list.
+};
+
+typedef struct {
+ uint16_t x;
+ uint16_t y;
+ int8_t dx;
+ int8_t dy;
+ uint8_t scale;
+ uint16_t width;
+ uint16_t height;
+ bool mirror_x;
+ bool mirror_y;
+ bool transpose_xy;
+} displayio_buffer_transform_t;
+
+extern displayio_buffer_transform_t null_transform;
+
+bool displayio_area_empty(const displayio_area_t *a);
+void displayio_area_copy_coords(const displayio_area_t *src, displayio_area_t *dest);
+void displayio_area_canon(displayio_area_t *a);
+void displayio_area_union(const displayio_area_t *a,
+ const displayio_area_t *b,
+ displayio_area_t *u);
+void displayio_area_copy(const displayio_area_t *src, displayio_area_t *dst);
+void displayio_area_scale(displayio_area_t *area, uint16_t scale);
+void displayio_area_shift(displayio_area_t *area, int16_t dx, int16_t dy);
+bool displayio_area_compute_overlap(const displayio_area_t *a,
+ const displayio_area_t *b,
+ displayio_area_t *overlap);
+uint16_t displayio_area_width(const displayio_area_t *area);
+uint16_t displayio_area_height(const displayio_area_t *area);
+uint32_t displayio_area_size(const displayio_area_t *area);
+bool displayio_area_equal(const displayio_area_t *a, const displayio_area_t *b);
+void displayio_area_transform_within(bool mirror_x, bool mirror_y, bool transpose_xy,
+ const displayio_area_t *original,
+ const displayio_area_t *whole,
+ displayio_area_t *transformed);
+
+#endif // MICROPY_INCLUDED_SHARED_MODULE_DISPLAYIO_AREA_H
diff --git a/circuitpython/shared-module/displayio/display_core.c b/circuitpython/shared-module/displayio/display_core.c
new file mode 100644
index 0000000..8b2f0bf
--- /dev/null
+++ b/circuitpython/shared-module/displayio/display_core.c
@@ -0,0 +1,398 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Scott Shawcroft 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.
+ */
+
+#include "shared-bindings/displayio/Display.h"
+
+#include "py/gc.h"
+#include "py/runtime.h"
+#include "shared-bindings/displayio/FourWire.h"
+#include "shared-bindings/displayio/I2CDisplay.h"
+#if CIRCUITPY_PARALLELDISPLAY
+#include "shared-bindings/paralleldisplay/ParallelBus.h"
+#endif
+#include "shared-bindings/microcontroller/Pin.h"
+#include "shared-bindings/time/__init__.h"
+#include "shared-module/displayio/__init__.h"
+#include "supervisor/shared/display.h"
+#include "supervisor/shared/tick.h"
+
+#include <stdint.h>
+#include <string.h>
+
+#define DISPLAYIO_CORE_DEBUG(...) (void)0
+// #define DISPLAYIO_CORE_DEBUG(...) mp_printf(&mp_plat_print __VA_OPT__(,) __VA_ARGS__)
+
+void displayio_display_core_construct(displayio_display_core_t *self,
+ mp_obj_t bus, uint16_t width, uint16_t height, uint16_t ram_width, uint16_t ram_height, int16_t colstart, int16_t rowstart, uint16_t rotation,
+ uint16_t color_depth, bool grayscale, bool pixels_in_byte_share_row, uint8_t bytes_per_cell, bool reverse_pixels_in_byte, bool reverse_bytes_in_word) {
+ self->colorspace.depth = color_depth;
+ self->colorspace.grayscale = grayscale;
+ self->colorspace.grayscale_bit = 8 - color_depth;
+ self->colorspace.pixels_in_byte_share_row = pixels_in_byte_share_row;
+ self->colorspace.bytes_per_cell = bytes_per_cell;
+ self->colorspace.reverse_pixels_in_byte = reverse_pixels_in_byte;
+ self->colorspace.reverse_bytes_in_word = reverse_bytes_in_word;
+ self->colorspace.dither = false;
+ self->current_group = NULL;
+ self->colstart = colstart;
+ self->rowstart = rowstart;
+ self->last_refresh = 0;
+
+ // (framebufferdisplay already validated its 'bus' is a buffer-protocol object)
+ if (bus) {
+ #if CIRCUITPY_PARALLELDISPLAY
+ if (mp_obj_is_type(bus, &paralleldisplay_parallelbus_type)) {
+ self->bus_reset = common_hal_paralleldisplay_parallelbus_reset;
+ self->bus_free = common_hal_paralleldisplay_parallelbus_bus_free;
+ self->begin_transaction = common_hal_paralleldisplay_parallelbus_begin_transaction;
+ self->send = common_hal_paralleldisplay_parallelbus_send;
+ self->end_transaction = common_hal_paralleldisplay_parallelbus_end_transaction;
+ } else
+ #endif
+ if (mp_obj_is_type(bus, &displayio_fourwire_type)) {
+ self->bus_reset = common_hal_displayio_fourwire_reset;
+ self->bus_free = common_hal_displayio_fourwire_bus_free;
+ self->begin_transaction = common_hal_displayio_fourwire_begin_transaction;
+ self->send = common_hal_displayio_fourwire_send;
+ self->end_transaction = common_hal_displayio_fourwire_end_transaction;
+ } else if (mp_obj_is_type(bus, &displayio_i2cdisplay_type)) {
+ self->bus_reset = common_hal_displayio_i2cdisplay_reset;
+ self->bus_free = common_hal_displayio_i2cdisplay_bus_free;
+ self->begin_transaction = common_hal_displayio_i2cdisplay_begin_transaction;
+ self->send = common_hal_displayio_i2cdisplay_send;
+ self->end_transaction = common_hal_displayio_i2cdisplay_end_transaction;
+ } else {
+ mp_raise_ValueError(translate("Unsupported display bus type"));
+ }
+ }
+ self->bus = bus;
+
+
+ // (offsetof core is equal in all display types)
+ if (self == &displays[0].display.core) {
+ supervisor_start_terminal(width, height);
+ }
+
+ self->width = width;
+ self->height = height;
+ self->ram_width = ram_width;
+ self->ram_height = ram_height;
+
+ displayio_display_core_set_rotation(self, rotation);
+}
+
+void displayio_display_core_set_rotation(displayio_display_core_t *self,
+ int rotation) {
+ int height = self->height;
+ int width = self->width;
+
+ rotation = rotation % 360;
+ self->rotation = rotation;
+ self->transform.x = 0;
+ self->transform.y = 0;
+ self->transform.scale = 1;
+ self->transform.mirror_x = false;
+ self->transform.mirror_y = false;
+ self->transform.transpose_xy = false;
+ if (rotation == 0 || rotation == 180) {
+ if (rotation == 180) {
+ self->transform.mirror_x = true;
+ self->transform.mirror_y = true;
+ }
+ } else {
+ self->transform.transpose_xy = true;
+ if (rotation == 270) {
+ self->transform.mirror_y = true;
+ } else {
+ self->transform.mirror_x = true;
+ }
+ }
+
+ self->area.x1 = 0;
+ self->area.y1 = 0;
+ self->area.next = NULL;
+
+ self->transform.dx = 1;
+ self->transform.dy = 1;
+ if (self->transform.transpose_xy) {
+ self->area.x2 = height;
+ self->area.y2 = width;
+ if (self->transform.mirror_x) {
+ self->transform.x = height;
+ self->transform.dx = -1;
+ }
+ if (self->transform.mirror_y) {
+ self->transform.y = width;
+ self->transform.dy = -1;
+ }
+ } else {
+ self->area.x2 = width;
+ self->area.y2 = height;
+ if (self->transform.mirror_x) {
+ self->transform.x = width;
+ self->transform.dx = -1;
+ }
+ if (self->transform.mirror_y) {
+ self->transform.y = height;
+ self->transform.dy = -1;
+ }
+ }
+}
+
+bool displayio_display_core_show(displayio_display_core_t *self, displayio_group_t *root_group) {
+
+ if (root_group == NULL) { // set the display to the REPL, reset REPL position and size
+ circuitpython_splash.in_group = false;
+ // force the circuit_python_splash out of any group (Note: could cause problems with the parent group)
+ circuitpython_splash.x = 0; // reset position in case someone moved it.
+ circuitpython_splash.y = 0;
+
+ supervisor_start_terminal(self->width, self->height);
+
+ root_group = &circuitpython_splash;
+ }
+ if (root_group == self->current_group) {
+ return true;
+ }
+ if (root_group != NULL && root_group->in_group) {
+ return false;
+ }
+ if (self->current_group != NULL) {
+ self->current_group->in_group = false;
+ }
+
+ if (root_group != NULL) {
+ displayio_group_update_transform(root_group, &self->transform);
+ root_group->in_group = true;
+ }
+ self->current_group = root_group;
+ self->full_refresh = true;
+ return true;
+}
+
+uint16_t displayio_display_core_get_width(displayio_display_core_t *self) {
+ return self->width;
+}
+
+uint16_t displayio_display_core_get_height(displayio_display_core_t *self) {
+ return self->height;
+}
+
+void displayio_display_core_set_dither(displayio_display_core_t *self, bool dither) {
+ self->colorspace.dither = dither;
+}
+
+bool displayio_display_core_get_dither(displayio_display_core_t *self) {
+ return self->colorspace.dither;
+}
+
+bool displayio_display_core_bus_free(displayio_display_core_t *self) {
+ return !self->bus || self->bus_free(self->bus);
+}
+
+bool displayio_display_core_begin_transaction(displayio_display_core_t *self) {
+ return self->begin_transaction(self->bus);
+}
+
+void displayio_display_core_end_transaction(displayio_display_core_t *self) {
+ self->end_transaction(self->bus);
+}
+
+void displayio_display_core_set_region_to_update(displayio_display_core_t *self, uint8_t column_command,
+ uint8_t row_command, uint16_t set_current_column_command, uint16_t set_current_row_command,
+ bool data_as_commands, bool always_toggle_chip_select,
+ displayio_area_t *area, bool SH1107_addressing) {
+ uint16_t x1 = area->x1 + self->colstart;
+ uint16_t x2 = area->x2 + self->colstart;
+ uint16_t y1 = area->y1 + self->rowstart;
+ uint16_t y2 = area->y2 + self->rowstart;
+
+ // Collapse down the dimension where multiple pixels are in a byte.
+ if (self->colorspace.depth < 8) {
+ uint8_t pixels_per_byte = 8 / self->colorspace.depth;
+ if (self->colorspace.pixels_in_byte_share_row) {
+ x1 /= pixels_per_byte * self->colorspace.bytes_per_cell;
+ x2 /= pixels_per_byte * self->colorspace.bytes_per_cell;
+ } else {
+ y1 /= pixels_per_byte * self->colorspace.bytes_per_cell;
+ y2 /= pixels_per_byte * self->colorspace.bytes_per_cell;
+ }
+ }
+
+ x2 -= 1;
+ y2 -= 1;
+
+ display_chip_select_behavior_t chip_select = CHIP_SELECT_UNTOUCHED;
+ if (always_toggle_chip_select || data_as_commands) {
+ chip_select = CHIP_SELECT_TOGGLE_EVERY_BYTE;
+ }
+
+ // Set column.
+ displayio_display_core_begin_transaction(self);
+ uint8_t data[5];
+ data[0] = column_command;
+ uint8_t data_length = 1;
+ display_byte_type_t data_type = DISPLAY_DATA;
+ if (!data_as_commands) {
+ self->send(self->bus, DISPLAY_COMMAND, CHIP_SELECT_UNTOUCHED, data, 1);
+ data_length = 0;
+ } else {
+ data_type = DISPLAY_COMMAND;
+ }
+
+ if (self->ram_width < 0x100) {
+ data[data_length++] = x1;
+ data[data_length++] = x2;
+ } else {
+ data[data_length++] = x1 >> 8;
+ data[data_length++] = x1 & 0xff;
+ data[data_length++] = x2 >> 8;
+ data[data_length++] = x2 & 0xff;
+ }
+
+ // Quirk for SH1107 "SH1107_addressing"
+ // Column lower command = 0x00, Column upper command = 0x10
+ if (SH1107_addressing) {
+ data[0] = ((x1 >> 4) & 0x0F) | 0x10; // 0x10 to 0x17
+ data[1] = x1 & 0x0F; // 0x00 to 0x0F
+ data_length = 2;
+ }
+
+ self->send(self->bus, data_type, chip_select, data, data_length);
+ displayio_display_core_end_transaction(self);
+
+ if (set_current_column_command != NO_COMMAND) {
+ uint8_t command = set_current_column_command;
+ displayio_display_core_begin_transaction(self);
+ self->send(self->bus, DISPLAY_COMMAND, chip_select, &command, 1);
+ self->send(self->bus, DISPLAY_DATA, chip_select, data, data_length / 2);
+ displayio_display_core_end_transaction(self);
+ }
+
+
+ // Set row.
+ displayio_display_core_begin_transaction(self);
+ data[0] = row_command;
+ data_length = 1;
+ if (!data_as_commands) {
+ self->send(self->bus, DISPLAY_COMMAND, CHIP_SELECT_UNTOUCHED, data, 1);
+ data_length = 0;
+ }
+
+ if (self->ram_height < 0x100) {
+ data[data_length++] = y1;
+ data[data_length++] = y2;
+ } else {
+ data[data_length++] = y1 >> 8;
+ data[data_length++] = y1 & 0xff;
+ data[data_length++] = y2 >> 8;
+ data[data_length++] = y2 & 0xff;
+ }
+
+ // Quirk for SH1107 "SH1107_addressing"
+ // Page address command = 0xB0
+ if (SH1107_addressing) {
+ // set the page to our y value
+ data[0] = 0xB0 | y1;
+ data_length = 1;
+ }
+
+ self->send(self->bus, data_type, chip_select, data, data_length);
+ displayio_display_core_end_transaction(self);
+
+ if (set_current_row_command != NO_COMMAND) {
+ uint8_t command = set_current_row_command;
+ displayio_display_core_begin_transaction(self);
+ self->send(self->bus, DISPLAY_COMMAND, chip_select, &command, 1);
+ self->send(self->bus, DISPLAY_DATA, chip_select, data, data_length / 2);
+ displayio_display_core_end_transaction(self);
+ }
+}
+
+bool displayio_display_core_start_refresh(displayio_display_core_t *self) {
+ if (!displayio_display_core_bus_free(self)) {
+ // Can't acquire display bus; skip updating this display. Try next display.
+ return false;
+ }
+ if (self->refresh_in_progress) {
+ return false;
+ }
+ self->refresh_in_progress = true;
+ self->last_refresh = supervisor_ticks_ms64();
+ return true;
+}
+
+void displayio_display_core_finish_refresh(displayio_display_core_t *self) {
+ if (self->current_group != NULL) {
+ DISPLAYIO_CORE_DEBUG("displayiocore group_finish_refresh\n");
+ displayio_group_finish_refresh(self->current_group);
+ }
+ self->full_refresh = false;
+ self->refresh_in_progress = false;
+ self->last_refresh = supervisor_ticks_ms64();
+}
+
+void release_display_core(displayio_display_core_t *self) {
+ if (self->current_group != NULL) {
+ self->current_group->in_group = false;
+ }
+}
+
+void displayio_display_core_collect_ptrs(displayio_display_core_t *self) {
+ gc_collect_ptr(self->current_group);
+}
+
+bool displayio_display_core_fill_area(displayio_display_core_t *self, displayio_area_t *area, uint32_t *mask, uint32_t *buffer) {
+ return displayio_group_fill_area(self->current_group, &self->colorspace, area, mask, buffer);
+}
+
+bool displayio_display_core_clip_area(displayio_display_core_t *self, const displayio_area_t *area, displayio_area_t *clipped) {
+ bool overlaps = displayio_area_compute_overlap(&self->area, area, clipped);
+ if (!overlaps) {
+ return false;
+ }
+ // Expand the area if we have multiple pixels per byte and we need to byte
+ // align the bounds.
+ if (self->colorspace.depth < 8) {
+ uint8_t pixels_per_byte = 8 / self->colorspace.depth * self->colorspace.bytes_per_cell;
+ if (self->colorspace.pixels_in_byte_share_row) {
+ if (clipped->x1 % pixels_per_byte != 0) {
+ clipped->x1 -= clipped->x1 % pixels_per_byte;
+ }
+ if (clipped->x2 % pixels_per_byte != 0) {
+ clipped->x2 += pixels_per_byte - clipped->x2 % pixels_per_byte;
+ }
+ } else {
+ if (clipped->y1 % pixels_per_byte != 0) {
+ clipped->y1 -= clipped->y1 % pixels_per_byte;
+ }
+ if (clipped->y2 % pixels_per_byte != 0) {
+ clipped->y2 += pixels_per_byte - clipped->y2 % pixels_per_byte;
+ }
+ }
+ }
+ return true;
+}
diff --git a/circuitpython/shared-module/displayio/display_core.h b/circuitpython/shared-module/displayio/display_core.h
new file mode 100644
index 0000000..8c2ba21
--- /dev/null
+++ b/circuitpython/shared-module/displayio/display_core.h
@@ -0,0 +1,94 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Scott Shawcroft 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.
+ */
+
+#ifndef MICROPY_INCLUDED_SHARED_MODULE_DISPLAYIO_DISPLAY_CORE_H
+#define MICROPY_INCLUDED_SHARED_MODULE_DISPLAYIO_DISPLAY_CORE_H
+
+#include "shared-bindings/displayio/__init__.h"
+#include "shared-bindings/displayio/Group.h"
+
+#include "shared-module/displayio/area.h"
+
+#define NO_COMMAND 0x100
+
+typedef struct {
+ mp_obj_t bus;
+ displayio_group_t *current_group;
+ uint64_t last_refresh;
+ display_bus_bus_reset bus_reset;
+ display_bus_bus_free bus_free;
+ display_bus_begin_transaction begin_transaction;
+ display_bus_send send;
+ display_bus_end_transaction end_transaction;
+ displayio_buffer_transform_t transform;
+ displayio_area_t area;
+ uint16_t width;
+ uint16_t height;
+ uint16_t rotation;
+ uint16_t ram_width;
+ uint16_t ram_height;
+ _displayio_colorspace_t colorspace;
+ int16_t colstart;
+ int16_t rowstart;
+ bool full_refresh; // New group means we need to refresh the whole display.
+ bool refresh_in_progress;
+} displayio_display_core_t;
+
+void displayio_display_core_construct(displayio_display_core_t *self,
+ mp_obj_t bus, uint16_t width, uint16_t height, uint16_t ram_width, uint16_t ram_height, int16_t colstart, int16_t rowstart, uint16_t rotation,
+ uint16_t color_depth, bool grayscale, bool pixels_in_byte_share_row, uint8_t bytes_per_cell, bool reverse_pixels_in_byte, bool reverse_bytes_in_word);
+
+bool displayio_display_core_show(displayio_display_core_t *self, displayio_group_t *root_group);
+
+uint16_t displayio_display_core_get_width(displayio_display_core_t *self);
+uint16_t displayio_display_core_get_height(displayio_display_core_t *self);
+
+void displayio_display_core_set_dither(displayio_display_core_t *self, bool dither);
+bool displayio_display_core_get_dither(displayio_display_core_t *self);
+
+void displayio_display_core_set_rotation(displayio_display_core_t *self, int rotation);
+
+bool displayio_display_core_bus_free(displayio_display_core_t *self);
+bool displayio_display_core_begin_transaction(displayio_display_core_t *self);
+void displayio_display_core_end_transaction(displayio_display_core_t *self);
+
+void displayio_display_core_set_region_to_update(displayio_display_core_t *self, uint8_t column_command,
+ uint8_t row_command, uint16_t set_current_column_command, uint16_t set_current_row_command,
+ bool data_as_commands, bool always_toggle_chip_select,
+ displayio_area_t *area, bool SH1107_addressing);
+
+void release_display_core(displayio_display_core_t *self);
+
+bool displayio_display_core_start_refresh(displayio_display_core_t *self);
+void displayio_display_core_finish_refresh(displayio_display_core_t *self);
+
+void displayio_display_core_collect_ptrs(displayio_display_core_t *self);
+
+bool displayio_display_core_fill_area(displayio_display_core_t *self, displayio_area_t *area, uint32_t *mask, uint32_t *buffer);
+
+bool displayio_display_core_clip_area(displayio_display_core_t *self, const displayio_area_t *area, displayio_area_t *clipped);
+
+#endif // MICROPY_INCLUDED_SHARED_MODULE_DISPLAYIO_DISPLAY_CORE_H
diff --git a/circuitpython/shared-module/displayio/mipi_constants.h b/circuitpython/shared-module/displayio/mipi_constants.h
new file mode 100644
index 0000000..3cb7e42
--- /dev/null
+++ b/circuitpython/shared-module/displayio/mipi_constants.h
@@ -0,0 +1,37 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Scott Shawcroft 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.
+ */
+
+#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYIO_MIPI_CONSTANTS_H
+#define MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYIO_MIPI_CONSTANTS_H
+
+// More info here: https://www.tonylabs.com/wp-content/uploads/MIPI_DCS_specification_v1.02.00.pdf
+enum mipi_command {
+ MIPI_COMMAND_SET_COLUMN_ADDRESS = 0x2a,
+ MIPI_COMMAND_SET_PAGE_ADDRESS = 0x2b,
+ MIPI_COMMAND_WRITE_MEMORY_START = 0x2c,
+};
+
+#endif // MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYIO_MIPI_CONSTANTS_H
diff --git a/circuitpython/shared-module/floppyio/__init__.c b/circuitpython/shared-module/floppyio/__init__.c
new file mode 100644
index 0000000..6700c48
--- /dev/null
+++ b/circuitpython/shared-module/floppyio/__init__.c
@@ -0,0 +1,110 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2022 Jeff Epler 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.
+ */
+
+#include "py/runtime.h"
+
+#include "shared-bindings/time/__init__.h"
+#include "shared-bindings/floppyio/__init__.h"
+#include "common-hal/floppyio/__init__.h"
+#include "shared-bindings/digitalio/DigitalInOut.h"
+
+#ifndef T2_5
+#define T2_5 (FLOPPYIO_SAMPLERATE * 5 / 2 / 1000000)
+#endif
+#ifndef T3_5
+#define T3_5 (FLOPPYIO_SAMPLERATE * 7 / 2 / 1000000)
+#endif
+
+#define MFM_IO_MMIO (1)
+#include "lib/adafruit_floppy/src/mfm_impl.h"
+
+__attribute__((optimize("O3")))
+int common_hal_floppyio_flux_readinto(void *buf, size_t len, digitalio_digitalinout_obj_t *data, digitalio_digitalinout_obj_t *index) {
+ uint32_t index_mask;
+ volatile uint32_t *index_port = common_hal_digitalio_digitalinout_get_reg(index, DIGITALINOUT_REG_READ, &index_mask);
+
+ uint32_t data_mask;
+ volatile uint32_t *data_port = common_hal_digitalio_digitalinout_get_reg(data, DIGITALINOUT_REG_READ, &data_mask);
+
+#undef READ_INDEX
+#undef READ_DATA
+#define READ_INDEX() (!!(*index_port & index_mask))
+#define READ_DATA() (!!(*data_port & data_mask))
+
+ memset(buf, 0, len);
+
+ uint8_t *pulses = buf, *pulses_ptr = pulses, *pulses_end = pulses + len;
+
+ common_hal_mcu_disable_interrupts();
+
+ // wait for index pulse low
+ while (READ_INDEX()) { /* NOTHING */
+ }
+
+
+ // if data line is low, wait till it rises
+ while (!READ_DATA()) { /* NOTHING */
+ }
+
+ // and then till it drops down
+ while (READ_DATA()) { /* NOTHING */
+ }
+
+ uint32_t index_state = 0;
+ while (pulses_ptr < pulses_end) {
+ index_state = (index_state << 1) | READ_INDEX();
+ if ((index_state & 3) == 2) { // a zero-to-one transition
+ break;
+ }
+
+ unsigned pulse_count = 3;
+ while (!READ_DATA()) {
+ pulse_count++;
+ }
+
+ while (READ_DATA()) {
+ pulse_count++;
+ }
+
+ *pulses_ptr++ = MIN(255, pulse_count);
+ }
+ common_hal_mcu_enable_interrupts();
+
+ return pulses_ptr - pulses;
+}
+
+int common_hal_floppyio_mfm_readinto(void *buf, size_t n_sectors, digitalio_digitalinout_obj_t *data, digitalio_digitalinout_obj_t *index) {
+ mfm_io_t io;
+ io.index_port = common_hal_digitalio_digitalinout_get_reg(index, DIGITALINOUT_REG_READ, &io.index_mask);
+ io.data_port = common_hal_digitalio_digitalinout_get_reg(data, DIGITALINOUT_REG_READ, &io.data_mask);
+
+ common_hal_mcu_disable_interrupts();
+ uint8_t validity[n_sectors];
+ int result = read_track(io, n_sectors, buf, validity);
+ common_hal_mcu_enable_interrupts();
+
+ return result;
+}
diff --git a/circuitpython/shared-module/fontio/BuiltinFont.c b/circuitpython/shared-module/fontio/BuiltinFont.c
new file mode 100644
index 0000000..cb5b7f9
--- /dev/null
+++ b/circuitpython/shared-module/fontio/BuiltinFont.c
@@ -0,0 +1,79 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Scott Shawcroft 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.
+ */
+
+#include "shared-bindings/fontio/BuiltinFont.h"
+
+
+#include "shared-bindings/fontio/Glyph.h"
+
+#include "py/objnamedtuple.h"
+
+mp_obj_t common_hal_fontio_builtinfont_get_bitmap(const fontio_builtinfont_t *self) {
+ return MP_OBJ_FROM_PTR(self->bitmap);
+}
+
+mp_obj_t common_hal_fontio_builtinfont_get_bounding_box(const fontio_builtinfont_t *self) {
+ mp_obj_t *items = m_new(mp_obj_t, 2);
+ items[0] = MP_OBJ_NEW_SMALL_INT(self->width);
+ items[1] = MP_OBJ_NEW_SMALL_INT(self->height);
+ return mp_obj_new_tuple(2, items);
+}
+
+uint8_t fontio_builtinfont_get_glyph_index(const fontio_builtinfont_t *self, mp_uint_t codepoint) {
+ if (codepoint >= 0x20 && codepoint <= 0x7e) {
+ return codepoint - 0x20;
+ }
+ // Do a linear search of the mapping for unicode.
+ const byte *j = self->unicode_characters;
+ uint8_t k = 0;
+ while (j < self->unicode_characters + self->unicode_characters_len) {
+ unichar potential_c = utf8_get_char(j);
+ j = utf8_next_char(j);
+ if (codepoint == potential_c) {
+ return 0x7f - 0x20 + k;
+ }
+ k++;
+ }
+ return 0xff;
+}
+
+mp_obj_t common_hal_fontio_builtinfont_get_glyph(const fontio_builtinfont_t *self, mp_uint_t codepoint) {
+ uint8_t glyph_index = fontio_builtinfont_get_glyph_index(self, codepoint);
+ if (glyph_index == 0xff) {
+ return mp_const_none;
+ }
+ mp_obj_t field_values[8] = {
+ MP_OBJ_FROM_PTR(self->bitmap),
+ MP_OBJ_NEW_SMALL_INT(glyph_index),
+ MP_OBJ_NEW_SMALL_INT(self->width),
+ MP_OBJ_NEW_SMALL_INT(self->height),
+ MP_OBJ_NEW_SMALL_INT(0),
+ MP_OBJ_NEW_SMALL_INT(0),
+ MP_OBJ_NEW_SMALL_INT(self->width),
+ MP_OBJ_NEW_SMALL_INT(0)
+ };
+ return namedtuple_make_new((const mp_obj_type_t *)&fontio_glyph_type, 8, 0, field_values);
+}
diff --git a/circuitpython/shared-module/fontio/BuiltinFont.h b/circuitpython/shared-module/fontio/BuiltinFont.h
new file mode 100644
index 0000000..79c8614
--- /dev/null
+++ b/circuitpython/shared-module/fontio/BuiltinFont.h
@@ -0,0 +1,47 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Scott Shawcroft 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.
+ */
+
+#ifndef MICROPY_INCLUDED_SHARED_MODULE_FONTIO_BUILTINFONT_H
+#define MICROPY_INCLUDED_SHARED_MODULE_FONTIO_BUILTINFONT_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "py/obj.h"
+#include "shared-bindings/displayio/Bitmap.h"
+
+typedef struct {
+ mp_obj_base_t base;
+ const displayio_bitmap_t *bitmap;
+ uint8_t width;
+ uint8_t height;
+ const byte *unicode_characters;
+ uint16_t unicode_characters_len;
+} fontio_builtinfont_t;
+
+uint8_t fontio_builtinfont_get_glyph_index(const fontio_builtinfont_t *self, mp_uint_t codepoint);
+
+#endif // MICROPY_INCLUDED_SHARED_MODULE_FONTIO_BUILTINFONT_H
diff --git a/circuitpython/shared-module/fontio/__init__.c b/circuitpython/shared-module/fontio/__init__.c
new file mode 100644
index 0000000..674343c
--- /dev/null
+++ b/circuitpython/shared-module/fontio/__init__.c
@@ -0,0 +1,27 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Scott Shawcroft
+ *
+ * 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.
+ */
+
+// Nothing now.
diff --git a/circuitpython/shared-module/framebufferio/FramebufferDisplay.c b/circuitpython/shared-module/framebufferio/FramebufferDisplay.c
new file mode 100644
index 0000000..48353b7
--- /dev/null
+++ b/circuitpython/shared-module/framebufferio/FramebufferDisplay.c
@@ -0,0 +1,375 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Scott Shawcroft for Adafruit Industries
+ * Copyright (c) 2020 Jeff Epler 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.
+ */
+
+#include "shared-bindings/framebufferio/FramebufferDisplay.h"
+
+#include "py/gc.h"
+#include "py/runtime.h"
+#include "shared-bindings/microcontroller/Pin.h"
+#include "shared-bindings/time/__init__.h"
+#include "shared-module/displayio/__init__.h"
+#include "shared-module/displayio/display_core.h"
+#include "supervisor/shared/display.h"
+#include "supervisor/shared/tick.h"
+#include "supervisor/usb.h"
+
+#include <stdint.h>
+#include <string.h>
+
+#define fb_getter_default(method, default_value) \
+ (self->framebuffer_protocol->method \
+ ? self->framebuffer_protocol->method(self->framebuffer) \
+ : (default_value))
+
+void common_hal_framebufferio_framebufferdisplay_construct(framebufferio_framebufferdisplay_obj_t *self,
+ mp_obj_t framebuffer,
+ uint16_t rotation,
+ bool auto_refresh) {
+ // Turn off auto-refresh as we init.
+ self->auto_refresh = false;
+ self->framebuffer = framebuffer;
+ self->framebuffer_protocol = mp_proto_get_or_throw(MP_QSTR_protocol_framebuffer, framebuffer);
+
+ uint16_t ram_width = 0x100;
+ uint16_t ram_height = 0x100;
+ uint16_t depth = fb_getter_default(get_color_depth, 16);
+ displayio_display_core_construct(
+ &self->core,
+ NULL,
+ self->framebuffer_protocol->get_width(self->framebuffer),
+ self->framebuffer_protocol->get_height(self->framebuffer),
+ ram_width,
+ ram_height,
+ 0,
+ 0,
+ 0, // rotation
+ depth,
+ fb_getter_default(get_grayscale, (depth < 8)),
+ fb_getter_default(get_pixels_in_byte_share_row, false),
+ fb_getter_default(get_bytes_per_cell, 2),
+ fb_getter_default(get_reverse_pixels_in_byte, false),
+ fb_getter_default(get_reverse_pixels_in_word, false)
+ );
+
+ self->first_pixel_offset = fb_getter_default(get_first_pixel_offset, 0);
+ self->row_stride = fb_getter_default(get_row_stride, 0);
+ if (self->row_stride == 0) {
+ self->row_stride = self->core.width * self->core.colorspace.depth / 8;
+ }
+
+ self->framebuffer_protocol->get_bufinfo(self->framebuffer, &self->bufinfo);
+ size_t framebuffer_size = self->first_pixel_offset + self->row_stride * self->core.height;
+ if (self->bufinfo.len < framebuffer_size) {
+ mp_raise_IndexError_varg(translate("Framebuffer requires %d bytes"), framebuffer_size);
+ }
+
+ self->first_manual_refresh = !auto_refresh;
+
+ self->native_frames_per_second = fb_getter_default(get_native_frames_per_second, 60);
+ self->native_ms_per_frame = 1000 / self->native_frames_per_second;
+
+ if (rotation != 0) {
+ common_hal_framebufferio_framebufferdisplay_set_rotation(self, rotation);
+ }
+
+ // Set the group after initialization otherwise we may send pixels while we delay in
+ // initialization.
+ common_hal_framebufferio_framebufferdisplay_show(self, &circuitpython_splash);
+ common_hal_framebufferio_framebufferdisplay_set_auto_refresh(self, auto_refresh);
+}
+
+bool common_hal_framebufferio_framebufferdisplay_show(framebufferio_framebufferdisplay_obj_t *self, displayio_group_t *root_group) {
+ return displayio_display_core_show(&self->core, root_group);
+}
+
+uint16_t common_hal_framebufferio_framebufferdisplay_get_width(framebufferio_framebufferdisplay_obj_t *self) {
+ return displayio_display_core_get_width(&self->core);
+}
+
+uint16_t common_hal_framebufferio_framebufferdisplay_get_height(framebufferio_framebufferdisplay_obj_t *self) {
+ return displayio_display_core_get_height(&self->core);
+}
+
+bool common_hal_framebufferio_framebufferdisplay_get_auto_brightness(framebufferio_framebufferdisplay_obj_t *self) {
+ if (self->framebuffer_protocol->get_auto_brightness) {
+ return self->framebuffer_protocol->get_auto_brightness(self->framebuffer);
+ }
+ return true;
+}
+
+bool common_hal_framebufferio_framebufferdisplay_set_auto_brightness(framebufferio_framebufferdisplay_obj_t *self, bool auto_brightness) {
+ if (self->framebuffer_protocol->set_auto_brightness) {
+ return self->framebuffer_protocol->set_auto_brightness(self->framebuffer, auto_brightness);
+ }
+ return false;
+}
+
+mp_float_t common_hal_framebufferio_framebufferdisplay_get_brightness(framebufferio_framebufferdisplay_obj_t *self) {
+ if (self->framebuffer_protocol->get_brightness) {
+ return self->framebuffer_protocol->get_brightness(self->framebuffer);
+ }
+ return -1;
+}
+
+bool common_hal_framebufferio_framebufferdisplay_set_brightness(framebufferio_framebufferdisplay_obj_t *self, mp_float_t brightness) {
+ bool ok = false;
+ if (self->framebuffer_protocol->set_brightness) {
+ self->framebuffer_protocol->set_brightness(self->framebuffer, brightness);
+ ok = true;
+ }
+ return ok;
+}
+
+mp_obj_t common_hal_framebufferio_framebufferdisplay_get_framebuffer(framebufferio_framebufferdisplay_obj_t *self) {
+ return self->framebuffer;
+}
+
+STATIC const displayio_area_t *_get_refresh_areas(framebufferio_framebufferdisplay_obj_t *self) {
+ if (self->core.full_refresh) {
+ self->core.area.next = NULL;
+ return &self->core.area;
+ } else if (self->core.current_group != NULL) {
+ return displayio_group_get_refresh_areas(self->core.current_group, NULL);
+ }
+ return NULL;
+}
+
+#define MARK_ROW_DIRTY(r) (dirty_row_bitmask[r / 8] |= (1 << (r & 7)))
+STATIC bool _refresh_area(framebufferio_framebufferdisplay_obj_t *self, const displayio_area_t *area, uint8_t *dirty_row_bitmask) {
+ uint16_t buffer_size = CIRCUITPY_DISPLAY_AREA_BUFFER_SIZE / sizeof(uint32_t); // In uint32_ts
+
+ displayio_area_t clipped;
+ // Clip the area to the display by overlapping the areas. If there is no overlap then we're done.
+ if (!displayio_display_core_clip_area(&self->core, area, &clipped)) {
+ return true;
+ }
+ uint16_t subrectangles = 1;
+
+ // If pixels are packed by row then rows are on byte boundaries
+ if (self->core.colorspace.depth < 8 && self->core.colorspace.pixels_in_byte_share_row) {
+ int div = 8 / self->core.colorspace.depth;
+ clipped.x1 = (clipped.x1 / div) * div;
+ clipped.x2 = ((clipped.x2 + div - 1) / div) * div;
+ }
+
+ uint16_t rows_per_buffer = displayio_area_height(&clipped);
+ uint8_t pixels_per_word = (sizeof(uint32_t) * 8) / self->core.colorspace.depth;
+ uint16_t pixels_per_buffer = displayio_area_size(&clipped);
+ if (displayio_area_size(&clipped) > buffer_size * pixels_per_word) {
+ rows_per_buffer = buffer_size * pixels_per_word / displayio_area_width(&clipped);
+ if (rows_per_buffer == 0) {
+ rows_per_buffer = 1;
+ }
+ // If pixels are packed by column then ensure rows_per_buffer is on a byte boundary.
+ if (self->core.colorspace.depth < 8 && !self->core.colorspace.pixels_in_byte_share_row) {
+ uint8_t pixels_per_byte = 8 / self->core.colorspace.depth;
+ if (rows_per_buffer % pixels_per_byte != 0) {
+ rows_per_buffer -= rows_per_buffer % pixels_per_byte;
+ }
+ }
+ subrectangles = displayio_area_height(&clipped) / rows_per_buffer;
+ if (displayio_area_height(&clipped) % rows_per_buffer != 0) {
+ subrectangles++;
+ }
+ pixels_per_buffer = rows_per_buffer * displayio_area_width(&clipped);
+ buffer_size = pixels_per_buffer / pixels_per_word;
+ if (pixels_per_buffer % pixels_per_word) {
+ buffer_size += 1;
+ }
+ }
+
+ // Allocated and shared as a uint32_t array so the compiler knows the
+ // alignment everywhere.
+ uint32_t buffer[buffer_size];
+ uint32_t mask_length = (pixels_per_buffer / 32) + 1;
+ uint32_t mask[mask_length];
+ uint16_t remaining_rows = displayio_area_height(&clipped);
+
+ for (uint16_t j = 0; j < subrectangles; j++) {
+ displayio_area_t subrectangle = {
+ .x1 = clipped.x1,
+ .y1 = clipped.y1 + rows_per_buffer * j,
+ .x2 = clipped.x2,
+ .y2 = clipped.y1 + rows_per_buffer * (j + 1)
+ };
+
+ if (remaining_rows < rows_per_buffer) {
+ subrectangle.y2 = subrectangle.y1 + remaining_rows;
+ }
+ remaining_rows -= rows_per_buffer;
+
+ memset(mask, 0, mask_length * sizeof(mask[0]));
+ memset(buffer, 0, buffer_size * sizeof(buffer[0]));
+
+ displayio_display_core_fill_area(&self->core, &subrectangle, mask, buffer);
+
+ uint8_t *buf = (uint8_t *)self->bufinfo.buf, *endbuf = buf + self->bufinfo.len;
+ (void)endbuf; // Hint to compiler that endbuf is "used" even if NDEBUG
+ buf += self->first_pixel_offset;
+
+ size_t rowstride = self->row_stride;
+ uint8_t *dest = buf + subrectangle.y1 * rowstride + subrectangle.x1 * self->core.colorspace.depth / 8;
+ uint8_t *src = (uint8_t *)buffer;
+ size_t rowsize = (subrectangle.x2 - subrectangle.x1) * self->core.colorspace.depth / 8;
+
+ for (uint16_t i = subrectangle.y1; i < subrectangle.y2; i++) {
+ assert(dest >= buf && dest < endbuf && dest + rowsize <= endbuf);
+ MARK_ROW_DIRTY(i);
+ memcpy(dest, src, rowsize);
+ dest += rowstride;
+ src += rowsize;
+ }
+
+ // TODO(tannewt): Make refresh displays faster so we don't starve other
+ // background tasks.
+ #if CIRCUITPY_USB
+ usb_background();
+ #endif
+ }
+ return true;
+}
+
+STATIC void _refresh_display(framebufferio_framebufferdisplay_obj_t *self) {
+ self->framebuffer_protocol->get_bufinfo(self->framebuffer, &self->bufinfo);
+ if (!self->bufinfo.buf) {
+ return;
+ }
+ displayio_display_core_start_refresh(&self->core);
+ const displayio_area_t *current_area = _get_refresh_areas(self);
+ if (current_area) {
+ uint8_t dirty_row_bitmask[(self->core.height + 7) / 8];
+ memset(dirty_row_bitmask, 0, sizeof(dirty_row_bitmask));
+ self->framebuffer_protocol->get_bufinfo(self->framebuffer, &self->bufinfo);
+ while (current_area != NULL) {
+ _refresh_area(self, current_area, dirty_row_bitmask);
+ current_area = current_area->next;
+ }
+ self->framebuffer_protocol->swapbuffers(self->framebuffer, dirty_row_bitmask);
+ }
+ displayio_display_core_finish_refresh(&self->core);
+}
+
+void common_hal_framebufferio_framebufferdisplay_set_rotation(framebufferio_framebufferdisplay_obj_t *self, int rotation) {
+ bool transposed = (self->core.rotation == 90 || self->core.rotation == 270);
+ bool will_transposed = (rotation == 90 || rotation == 270);
+ if (transposed != will_transposed) {
+ int tmp = self->core.width;
+ self->core.width = self->core.height;
+ self->core.height = tmp;
+ }
+ displayio_display_core_set_rotation(&self->core, rotation);
+ if (self == &displays[0].framebuffer_display) {
+ supervisor_stop_terminal();
+ supervisor_start_terminal(self->core.width, self->core.height);
+ }
+ if (self->core.current_group != NULL) {
+ displayio_group_update_transform(self->core.current_group, &self->core.transform);
+ }
+}
+
+uint16_t common_hal_framebufferio_framebufferdisplay_get_rotation(framebufferio_framebufferdisplay_obj_t *self) {
+ return self->core.rotation;
+}
+
+
+bool common_hal_framebufferio_framebufferdisplay_refresh(framebufferio_framebufferdisplay_obj_t *self, uint32_t target_ms_per_frame, uint32_t maximum_ms_per_real_frame) {
+ if (!self->auto_refresh && !self->first_manual_refresh) {
+ uint64_t current_time = supervisor_ticks_ms64();
+ uint32_t current_ms_since_real_refresh = current_time - self->core.last_refresh;
+ // Test to see if the real frame time is below our minimum.
+ if (current_ms_since_real_refresh > maximum_ms_per_real_frame) {
+ mp_raise_RuntimeError(translate("Below minimum frame rate"));
+ }
+ uint32_t current_ms_since_last_call = current_time - self->last_refresh_call;
+ self->last_refresh_call = current_time;
+ // Skip the actual refresh to help catch up.
+ if (current_ms_since_last_call > target_ms_per_frame) {
+ return false;
+ }
+ uint32_t remaining_time = target_ms_per_frame - (current_ms_since_real_refresh % target_ms_per_frame);
+ // We're ahead of the game so wait until we align with the frame rate.
+ while (supervisor_ticks_ms64() - self->last_refresh_call < remaining_time) {
+ RUN_BACKGROUND_TASKS;
+ }
+ }
+ self->first_manual_refresh = false;
+ _refresh_display(self);
+ return true;
+}
+
+bool common_hal_framebufferio_framebufferdisplay_get_auto_refresh(framebufferio_framebufferdisplay_obj_t *self) {
+ return self->auto_refresh;
+}
+
+void common_hal_framebufferio_framebufferdisplay_set_auto_refresh(framebufferio_framebufferdisplay_obj_t *self,
+ bool auto_refresh) {
+ self->first_manual_refresh = !auto_refresh;
+ if (auto_refresh != self->auto_refresh) {
+ if (auto_refresh) {
+ supervisor_enable_tick();
+ } else {
+ supervisor_disable_tick();
+ }
+ }
+ self->auto_refresh = auto_refresh;
+}
+
+STATIC void _update_backlight(framebufferio_framebufferdisplay_obj_t *self) {
+ // TODO(tannewt): Fade the backlight based on it's existing value and a target value. The target
+ // should account for ambient light when possible.
+}
+
+void framebufferio_framebufferdisplay_background(framebufferio_framebufferdisplay_obj_t *self) {
+ _update_backlight(self);
+
+ if (self->auto_refresh && (supervisor_ticks_ms64() - self->core.last_refresh) > self->native_ms_per_frame) {
+ _refresh_display(self);
+ }
+}
+
+void release_framebufferdisplay(framebufferio_framebufferdisplay_obj_t *self) {
+ common_hal_framebufferio_framebufferdisplay_set_auto_refresh(self, false);
+ release_display_core(&self->core);
+ self->framebuffer_protocol->deinit(self->framebuffer);
+ self->base.type = &mp_type_NoneType;
+}
+
+void framebufferio_framebufferdisplay_collect_ptrs(framebufferio_framebufferdisplay_obj_t *self) {
+ gc_collect_ptr(self->framebuffer);
+ displayio_display_core_collect_ptrs(&self->core);
+}
+
+void framebufferio_framebufferdisplay_reset(framebufferio_framebufferdisplay_obj_t *self) {
+ const mp_obj_type_t *fb_type = mp_obj_get_type(self->framebuffer);
+ if (fb_type != NULL && fb_type != &mp_type_NoneType) {
+ common_hal_framebufferio_framebufferdisplay_set_auto_refresh(self, true);
+ common_hal_framebufferio_framebufferdisplay_show(self, NULL);
+ self->core.full_refresh = true;
+ } else {
+ release_framebufferdisplay(self);
+ }
+}
diff --git a/circuitpython/shared-module/framebufferio/FramebufferDisplay.h b/circuitpython/shared-module/framebufferio/FramebufferDisplay.h
new file mode 100644
index 0000000..b6138e2
--- /dev/null
+++ b/circuitpython/shared-module/framebufferio/FramebufferDisplay.h
@@ -0,0 +1,113 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Scott Shawcroft for Adafruit Industries
+ * Copyright (c) 2020 Jeff Epler 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.
+ */
+
+#ifndef MICROPY_INCLUDED_SHARED_MODULE_DISPLAYIO_FRAMEBUFFERDISPLAY_H
+#define MICROPY_INCLUDED_SHARED_MODULE_DISPLAYIO_FRAMEBUFFERDISPLAY_H
+
+#include "py/obj.h"
+#include "py/proto.h"
+
+#include "shared-bindings/digitalio/DigitalInOut.h"
+#include "shared-bindings/displayio/Group.h"
+
+#include "shared-module/displayio/area.h"
+#include "shared-module/displayio/display_core.h"
+
+typedef struct {
+ mp_obj_base_t base;
+ displayio_display_core_t core;
+ mp_obj_t framebuffer;
+ const struct _framebuffer_p_t *framebuffer_protocol;
+ mp_buffer_info_t bufinfo;
+ uint64_t last_backlight_refresh;
+ uint64_t last_refresh_call;
+ uint16_t native_frames_per_second;
+ uint16_t native_ms_per_frame;
+ uint16_t first_pixel_offset;
+ uint16_t row_stride;
+ bool auto_refresh;
+ bool first_manual_refresh;
+} framebufferio_framebufferdisplay_obj_t;
+
+void framebufferio_framebufferdisplay_background(framebufferio_framebufferdisplay_obj_t *self);
+void release_framebufferdisplay(framebufferio_framebufferdisplay_obj_t *self);
+void framebufferio_framebufferdisplay_reset(framebufferio_framebufferdisplay_obj_t *self);
+
+void framebufferio_framebufferdisplay_collect_ptrs(framebufferio_framebufferdisplay_obj_t *self);
+
+mp_obj_t common_hal_framebufferio_framebufferdisplay_get_framebuffer(framebufferio_framebufferdisplay_obj_t *self);
+
+typedef bool (*framebuffer_get_auto_brightness_fun)(mp_obj_t);
+typedef bool (*framebuffer_get_reverse_pixels_in_byte_fun)(mp_obj_t);
+typedef bool (*framebuffer_get_reverse_pixels_in_word_fun)(mp_obj_t);
+typedef bool (*framebuffer_set_auto_brightness_fun)(mp_obj_t, bool);
+typedef bool (*framebuffer_set_brightness_fun)(mp_obj_t, mp_float_t);
+typedef int (*framebuffer_get_bytes_per_cell_fun)(mp_obj_t);
+typedef int (*framebuffer_get_color_depth_fun)(mp_obj_t);
+typedef int (*framebuffer_get_first_pixel_offset_fun)(mp_obj_t);
+typedef int (*framebuffer_get_grayscale_fun)(mp_obj_t);
+typedef int (*framebuffer_get_height_fun)(mp_obj_t);
+typedef int (*framebuffer_get_native_frames_per_second_fun)(mp_obj_t);
+typedef bool (*framebuffer_get_pixels_in_byte_share_row_fun)(mp_obj_t);
+typedef int (*framebuffer_get_row_stride_fun)(mp_obj_t);
+typedef int (*framebuffer_get_width_fun)(mp_obj_t);
+typedef mp_float_t (*framebuffer_get_brightness_fun)(mp_obj_t);
+typedef void (*framebuffer_deinit_fun)(mp_obj_t);
+typedef void (*framebuffer_get_bufinfo_fun)(mp_obj_t, mp_buffer_info_t *bufinfo);
+typedef void (*framebuffer_swapbuffers_fun)(mp_obj_t, uint8_t *dirty_row_bitmask);
+
+typedef struct _framebuffer_p_t {
+ MP_PROTOCOL_HEAD // MP_QSTR_protocol_framebuffer
+
+ // Mandatory
+ framebuffer_get_bufinfo_fun get_bufinfo;
+ framebuffer_swapbuffers_fun swapbuffers;
+ framebuffer_deinit_fun deinit;
+ framebuffer_get_width_fun get_width;
+ framebuffer_get_height_fun get_height;
+
+ // Optional getters
+ framebuffer_get_bytes_per_cell_fun get_bytes_per_cell; // default: 2
+ framebuffer_get_color_depth_fun get_color_depth; // default: 16
+ framebuffer_get_first_pixel_offset_fun get_first_pixel_offset; // default: 0
+ framebuffer_get_grayscale_fun get_grayscale; // default: grayscale if depth < 8
+ framebuffer_get_native_frames_per_second_fun get_native_frames_per_second; // default: 60
+ framebuffer_get_pixels_in_byte_share_row_fun get_pixels_in_byte_share_row; // default: false
+ framebuffer_get_reverse_pixels_in_byte_fun get_reverse_pixels_in_byte; // default: false
+ framebuffer_get_reverse_pixels_in_word_fun get_reverse_pixels_in_word; // default: false
+ framebuffer_get_row_stride_fun get_row_stride; // default: 0 (no extra row padding)
+
+ // Optional -- default is no brightness control
+ framebuffer_get_brightness_fun get_brightness;
+ framebuffer_set_brightness_fun set_brightness;
+
+ // Optional -- default is no automatic brightness control
+ framebuffer_get_auto_brightness_fun get_auto_brightness;
+ framebuffer_set_auto_brightness_fun set_auto_brightness;
+} framebuffer_p_t;
+
+#endif // MICROPY_INCLUDED_SHARED_MODULE_DISPLAYIO_FRAMEBUFFERDISPLAY_H
diff --git a/circuitpython/shared-module/framebufferio/__init__.c b/circuitpython/shared-module/framebufferio/__init__.c
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/circuitpython/shared-module/framebufferio/__init__.c
diff --git a/circuitpython/shared-module/framebufferio/__init__.h b/circuitpython/shared-module/framebufferio/__init__.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/circuitpython/shared-module/framebufferio/__init__.h
diff --git a/circuitpython/shared-module/gamepadshift/GamePadShift.c b/circuitpython/shared-module/gamepadshift/GamePadShift.c
new file mode 100644
index 0000000..0fb7d1e
--- /dev/null
+++ b/circuitpython/shared-module/gamepadshift/GamePadShift.c
@@ -0,0 +1,52 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Radomir Dopieralski 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.
+ */
+
+#include "py/mpstate.h"
+#include "shared-bindings/digitalio/DigitalInOut.h"
+#include "shared-bindings/gamepadshift/GamePadShift.h"
+#include "shared-module/gamepadshift/GamePadShift.h"
+#include "supervisor/shared/tick.h"
+
+void common_hal_gamepadshift_gamepadshift_init(gamepadshift_obj_t *gamepadshift,
+ digitalio_digitalinout_obj_t *clock_pin,
+ digitalio_digitalinout_obj_t *data_pin,
+ digitalio_digitalinout_obj_t *latch_pin) {
+ common_hal_digitalio_digitalinout_switch_to_input(data_pin, PULL_NONE);
+ gamepadshift->data_pin = data_pin;
+ common_hal_digitalio_digitalinout_switch_to_output(clock_pin, 0,
+ DRIVE_MODE_PUSH_PULL);
+ gamepadshift->clock_pin = clock_pin;
+ common_hal_digitalio_digitalinout_switch_to_output(latch_pin, 1,
+ DRIVE_MODE_PUSH_PULL);
+ gamepadshift->latch_pin = latch_pin;
+
+ gamepadshift->last = 0;
+}
+
+void common_hal_gamepadshift_gamepadshift_deinit(gamepadshift_obj_t *gamepadshift) {
+ MP_STATE_VM(gamepad_singleton) = NULL;
+ supervisor_disable_tick();
+}
diff --git a/circuitpython/shared-module/gamepadshift/GamePadShift.h b/circuitpython/shared-module/gamepadshift/GamePadShift.h
new file mode 100644
index 0000000..53aef50
--- /dev/null
+++ b/circuitpython/shared-module/gamepadshift/GamePadShift.h
@@ -0,0 +1,43 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Radomir Dopieralski 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.
+ */
+
+#ifndef MICROPY_INCLUDED_GAMEPADSHIFT_GAMEPADSHIFT_H
+#define MICROPY_INCLUDED_GAMEPADSHIFT_GAMEPADSHIFT_H
+
+#include <stdint.h>
+
+#include "shared-bindings/digitalio/DigitalInOut.h"
+
+typedef struct {
+ mp_obj_base_t base;
+ digitalio_digitalinout_obj_t *data_pin;
+ digitalio_digitalinout_obj_t *clock_pin;
+ digitalio_digitalinout_obj_t *latch_pin;
+ volatile uint8_t pressed;
+ volatile uint8_t last;
+} gamepadshift_obj_t;
+
+#endif // MICROPY_INCLUDED_GAMEPADSHIFT_GAMEPADSHIFT_H
diff --git a/circuitpython/shared-module/gamepadshift/__init__.c b/circuitpython/shared-module/gamepadshift/__init__.c
new file mode 100644
index 0000000..eadd303
--- /dev/null
+++ b/circuitpython/shared-module/gamepadshift/__init__.c
@@ -0,0 +1,57 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Scott Shawcroft
+ *
+ * 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.
+ */
+
+#include "shared-module/gamepadshift/__init__.h"
+
+#include "py/mpstate.h"
+#include "shared-bindings/gamepadshift/GamePadShift.h"
+
+void gamepadshift_tick(void) {
+ void *singleton = MP_STATE_VM(gamepad_singleton);
+ if (singleton == NULL || !mp_obj_is_type(MP_OBJ_FROM_PTR(singleton), &gamepadshift_type)) {
+ return;
+ }
+
+ gamepadshift_obj_t *self = MP_OBJ_TO_PTR(singleton);
+ uint8_t current = 0;
+ uint8_t bit = 1;
+ common_hal_digitalio_digitalinout_set_value(self->latch_pin, 1);
+ for (int i = 0; i < 8; ++i) {
+ common_hal_digitalio_digitalinout_set_value(self->clock_pin, 0);
+ if (common_hal_digitalio_digitalinout_get_value(self->data_pin)) {
+ current |= bit;
+ }
+ common_hal_digitalio_digitalinout_set_value(self->clock_pin, 1);
+ bit <<= 1;
+ }
+ common_hal_digitalio_digitalinout_set_value(self->latch_pin, 0);
+ self->pressed |= self->last & current;
+ self->last = current;
+}
+
+void gamepadshift_reset(void) {
+ MP_STATE_VM(gamepad_singleton) = NULL;
+}
diff --git a/circuitpython/shared-module/gamepadshift/__init__.h b/circuitpython/shared-module/gamepadshift/__init__.h
new file mode 100644
index 0000000..225db73
--- /dev/null
+++ b/circuitpython/shared-module/gamepadshift/__init__.h
@@ -0,0 +1,33 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Scott Shawcroft 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.
+ */
+
+#ifndef MICROPY_INCLUDED_GAMEPADSHIFT___INIT___H
+#define MICROPY_INCLUDED_GAMEPADSHIFT___INIT___H
+
+void gamepadshift_tick(void);
+void gamepadshift_reset(void);
+
+#endif // MICROPY_INCLUDED_GAMEPADSHIFT___INIT___H
diff --git a/circuitpython/shared-module/getpass/__init__.c b/circuitpython/shared-module/getpass/__init__.c
new file mode 100644
index 0000000..8f16d75
--- /dev/null
+++ b/circuitpython/shared-module/getpass/__init__.c
@@ -0,0 +1,61 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 microDev
+ *
+ * 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.
+ */
+
+#include "py/mphal.h"
+#include "shared/readline/readline.h"
+#include "shared-module/getpass/__init__.h"
+
+mp_obj_t shared_module_getpass_getpass(const char *prompt, mp_print_t *print) {
+ vstr_t vstr;
+ vstr_init(&vstr, 16);
+
+ if (print == NULL) {
+ mp_hal_stdout_tx_str(prompt);
+ } else {
+ mp_printf(print, prompt);
+ }
+
+ for (;;) {
+ int c = mp_hal_stdin_rx_chr();
+ if (c == CHAR_CTRL_C) {
+ mp_raise_type(&mp_type_KeyboardInterrupt);
+ } else if (c == CHAR_CTRL_D && vstr.len == 0) {
+ mp_raise_type(&mp_type_EOFError);
+ } else if (c == 8 || c == 127) {
+ // backspace
+ vstr_cut_tail_bytes(&vstr, 1);
+ } else if (c >= 32) {
+ // printable character
+ vstr_ins_char(&vstr, vstr.len, c);
+ } else if (c == '\r') {
+ // newline
+ mp_hal_stdout_tx_str("\r\n");
+ break;
+ }
+ }
+
+ return mp_obj_new_str_from_vstr(&mp_type_str, &vstr);
+}
diff --git a/circuitpython/shared-module/getpass/__init__.h b/circuitpython/shared-module/getpass/__init__.h
new file mode 100644
index 0000000..80595d4
--- /dev/null
+++ b/circuitpython/shared-module/getpass/__init__.h
@@ -0,0 +1,34 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 microDev
+ *
+ * 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.
+ */
+
+#ifndef MICROPY_INCLUDED_SHARED_MODULE_GETPASS___INIT___H
+#define MICROPY_INCLUDED_SHARED_MODULE_GETPASS___INIT___H
+
+#include "py/runtime.h"
+
+extern mp_obj_t shared_module_getpass_getpass(const char *prompt, mp_print_t *print);
+
+#endif // MICROPY_INCLUDED_SHARED_MODULE_GETPASS___INIT___H
diff --git a/circuitpython/shared-module/gifio/GifWriter.c b/circuitpython/shared-module/gifio/GifWriter.c
new file mode 100644
index 0000000..c993a87
--- /dev/null
+++ b/circuitpython/shared-module/gifio/GifWriter.c
@@ -0,0 +1,283 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 Jeff Epler for Adafruit Industries
+ * Copyright (c) 2013-2021 Ibrahim Abdelkader <iabdalkader@openmv.io>
+ * Copyright (c) 2013-2021 Kwabena W. Agyeman <kwagyeman@openmv.io>
+ *
+ * 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.
+ */
+
+#include <string.h>
+
+#include "py/gc.h"
+#include "py/runtime.h"
+
+#include "shared-module/gifio/GifWriter.h"
+#include "shared-bindings/gifio/GifWriter.h"
+#include "shared-bindings/displayio/ColorConverter.h"
+#include "shared-bindings/util.h"
+
+#define BLOCK_SIZE (126) // (2^7) - 2 // (DO NOT CHANGE!)
+
+static void handle_error(gifio_gifwriter_t *self) {
+ if (self->error != 0) {
+ mp_raise_OSError(self->error);
+ }
+}
+
+static void flush_data(gifio_gifwriter_t *self) {
+ if (self->cur == 0) {
+ return;
+ }
+ int error = 0;
+ self->file_proto->write(self->file, self->data, self->cur, &error);
+ self->cur = 0;
+ if (error != 0) {
+ self->error = error;
+ }
+}
+
+// These "write" calls _MUST_ have enough buffer space available! This is
+// ensured by allocating the proper buffer size in construct.
+static void write_data(gifio_gifwriter_t *self, const void *data, size_t size) {
+ assert(self->cur + size <= self->size);
+ memcpy(self->data + self->cur, data, size);
+ self->cur += size;
+}
+
+static void write_byte(gifio_gifwriter_t *self, uint8_t value) {
+ write_data(self, &value, sizeof(value));
+}
+
+static void write_long(gifio_gifwriter_t *self, uint32_t value) {
+ write_data(self, &value, sizeof(value));
+}
+
+static void write_word(gifio_gifwriter_t *self, uint16_t value) {
+ write_data(self, &value, sizeof(value));
+}
+
+void shared_module_gifio_gifwriter_construct(gifio_gifwriter_t *self, mp_obj_t *file, int width, int height, displayio_colorspace_t colorspace, bool loop, bool dither, bool own_file) {
+ self->file = file;
+ self->file_proto = mp_get_stream_raise(file, MP_STREAM_OP_WRITE | MP_STREAM_OP_IOCTL);
+ if (self->file_proto->is_text) {
+ mp_raise_TypeError(translate("file must be a file opened in byte mode"));
+ }
+ self->width = width;
+ self->height = height;
+ self->colorspace = colorspace;
+ self->dither = dither;
+ self->own_file = own_file;
+
+ size_t nblocks = (width * height + 125) / 126;
+ self->size = nblocks * 128 + 4;
+ self->data = gc_alloc(self->size, 0, false);
+ self->cur = 0;
+ self->error = 0;
+
+ write_data(self, "GIF89a", 6);
+ write_word(self, width);
+ write_word(self, height);
+ write_data(self, (uint8_t []) {0xF6, 0x00, 0x00}, 3);
+
+ switch (colorspace) {
+ case DISPLAYIO_COLORSPACE_RGB565:
+ case DISPLAYIO_COLORSPACE_RGB565_SWAPPED:
+ case DISPLAYIO_COLORSPACE_BGR565:
+ case DISPLAYIO_COLORSPACE_BGR565_SWAPPED:
+ case DISPLAYIO_COLORSPACE_L8:
+ break;
+
+ default:
+ mp_raise_TypeError(translate("unsupported colorspace for GifWriter"));
+ }
+
+ bool color = (colorspace != DISPLAYIO_COLORSPACE_L8);
+
+ bool bgr = (colorspace == DISPLAYIO_COLORSPACE_BGR565 || colorspace == DISPLAYIO_COLORSPACE_BGR565_SWAPPED);
+ self->byteswap = (colorspace == DISPLAYIO_COLORSPACE_RGB565_SWAPPED || colorspace == DISPLAYIO_COLORSPACE_BGR565_SWAPPED);
+
+ if (color) {
+ for (int i = 0; i < 128; i++) {
+ int red = (int)(((((i & 0x60) >> 5) * 255) + 1.5) / 3);
+ int green = (int)(((((i & 0x1C) >> 2) * 255) + 3.5) / 7);
+ int blue = (int)((((i & 0x3) * 255) + 1.5) / 3);
+ if (bgr) {
+ write_data(self, (uint8_t []) {blue, red, green}, 3);
+ } else {
+ write_data(self, (uint8_t []) {red, green, blue}, 3);
+ }
+ }
+ } else {
+ for (int i = 0; i < 128; i++) {
+ int gray = (int)(((i * 255) + 63.5) / 127);
+ write_data(self, (uint8_t []) {gray, gray, gray}, 3);
+ }
+ }
+
+ if (loop) {
+ write_data(self, (uint8_t []) {'!', 0xFF, 0x0B}, 3);
+ write_data(self, "NETSCAPE2.0", 11);
+ write_data(self, (uint8_t []) {0x03, 0x01, 0x00, 0x00, 0x00}, 5);
+ }
+
+ flush_data(self);
+ handle_error(self);
+}
+
+bool shared_module_gifio_gifwriter_deinited(gifio_gifwriter_t *self) {
+ return !self->file;
+}
+
+void shared_module_gifio_gifwriter_check_for_deinit(gifio_gifwriter_t *self) {
+ if (shared_module_gifio_gifwriter_deinited(self)) {
+ raise_deinited_error();
+ }
+}
+
+void shared_module_gifio_gifwriter_deinit(gifio_gifwriter_t *self) {
+ if (!shared_module_gifio_gifwriter_deinited(self)) {
+ shared_module_gifio_gifwriter_close(self);
+ }
+}
+
+static const uint8_t rb_bayer[4][4] = {
+ { 0, 33, 8, 42},
+ {50, 16, 58, 25},
+ {12, 46, 4, 37},
+ {63, 29, 54, 21}
+};
+
+static const uint8_t g_bayer[4][4] = {
+ { 0, 16, 4, 20},
+ {24, 8, 28, 12},
+ { 6, 22, 2, 18},
+ {31, 14, 26, 10}
+};
+
+void shared_module_gifio_gifwriter_add_frame(gifio_gifwriter_t *self, const mp_buffer_info_t *bufinfo, int16_t delay) {
+ if (delay) {
+ write_data(self, (uint8_t []) {'!', 0xF9, 0x04, 0x04}, 4);
+ write_word(self, delay);
+ write_word(self, 0); // end
+ }
+
+ write_byte(self, 0x2C);
+ write_long(self, 0);
+ write_word(self, self->width);
+ write_word(self, self->height);
+ write_data(self, (uint8_t []) {0x00, 0x07}, 2); // 7-bits
+
+ int pixel_count = self->width * self->height;
+ int blocks = (pixel_count + BLOCK_SIZE - 1) / BLOCK_SIZE;
+
+ uint8_t *data = self->data + self->cur;
+
+ if (self->colorspace == DISPLAYIO_COLORSPACE_L8) {
+ mp_get_index(&mp_type_memoryview, bufinfo->len, MP_OBJ_NEW_SMALL_INT(pixel_count - 1), false);
+
+ uint8_t *pixels = bufinfo->buf;
+ for (int i = 0; i < blocks; i++) {
+ assert(pixel_count >= 0);
+ int block_size = MIN(BLOCK_SIZE, pixel_count);
+ pixel_count -= block_size;
+ *data++ = 1 + block_size;
+ *data++ = 0x80;
+ for (int j = 0; j < block_size; j++) {
+ *data++ = (*pixels++) >> 1;
+ }
+ }
+ } else if (!self->dither) {
+ mp_get_index(&mp_type_memoryview, bufinfo->len, MP_OBJ_NEW_SMALL_INT(2 * pixel_count - 1), false);
+
+ uint16_t *pixels = bufinfo->buf;
+ for (int i = 0; i < blocks; i++) {
+ int block_size = MIN(BLOCK_SIZE, pixel_count);
+ pixel_count -= block_size;
+
+ *data++ = 1 + block_size;
+ *data++ = 0x80;
+ for (int j = 0; j < block_size; j++) {
+ int pixel = *pixels++;
+ if (self->byteswap) {
+ pixel = __builtin_bswap16(pixel);
+ }
+ int red = (pixel >> (11 + (5 - 2))) & 0x3;
+ int green = (pixel >> (5 + (6 - 3))) & 0x7;
+ int blue = (pixel >> (0 + (5 - 2))) & 0x3;
+ *data++ = (red << 5) | (green << 2) | blue;
+ }
+ }
+ } else {
+ mp_get_index(&mp_type_memoryview, bufinfo->len, MP_OBJ_NEW_SMALL_INT(2 * pixel_count - 1), false);
+
+ uint16_t *pixels = bufinfo->buf;
+ int x = 0, y = 0;
+ for (int i = 0; i < blocks; i++) {
+ int block_size = MIN(BLOCK_SIZE, pixel_count);
+ pixel_count -= block_size;
+
+ *data++ = 1 + block_size;
+ *data++ = 0x80;
+ for (int j = 0; j < block_size; j++) {
+ int pixel = *pixels++;
+ if (self->byteswap) {
+ pixel = __builtin_bswap16(pixel);
+ }
+ int red = (pixel >> 8) & 0xf8;
+ int green = (pixel >> 3) & 0xfc;
+ int blue = (pixel << 3) & 0xf8;
+
+ red = MAX(0, red - rb_bayer[x % 4][y % 4]);
+ green = MAX(0, green - g_bayer[x % 4][(y + 2) % 4]);
+ blue = MAX(0, blue - rb_bayer[(x + 2) % 4][y % 4]);
+ x++;
+ if (x == self->width) {
+ x = 0;
+ y++;
+ }
+
+ *data++ = ((red >> 1) & 0x60) | ((green >> 3) & 0x1c) | (blue >> 6);
+ }
+ }
+ }
+
+ self->cur = data - self->data;
+
+ write_data(self, (uint8_t []) {0x01, 0x81, 0x00}, 3); // end code
+ flush_data(self);
+ handle_error(self);
+}
+
+void shared_module_gifio_gifwriter_close(gifio_gifwriter_t *self) {
+ write_byte(self, ';');
+ flush_data(self);
+
+ int error = 0;
+ self->file_proto->ioctl(self->file, self->own_file ? MP_STREAM_CLOSE : MP_STREAM_FLUSH, 0, &error);
+ self->file = NULL;
+
+ if (error != 0) {
+ self->error = error;
+ }
+ handle_error(self);
+}
diff --git a/circuitpython/shared-module/gifio/GifWriter.h b/circuitpython/shared-module/gifio/GifWriter.h
new file mode 100644
index 0000000..5ed57bb
--- /dev/null
+++ b/circuitpython/shared-module/gifio/GifWriter.h
@@ -0,0 +1,45 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 Jeff Epler 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.
+ */
+
+#pragma once
+
+#include "py/obj.h"
+#include "py/stream.h"
+#include "shared-bindings/displayio/__init__.h"
+
+typedef struct gifio_gifwriter {
+ mp_obj_base_t base;
+ mp_obj_t *file;
+ const mp_stream_p_t *file_proto;
+ displayio_colorspace_t colorspace;
+ int width, height;
+ int error;
+ uint8_t *data;
+ size_t cur, size;
+ bool own_file;
+ bool byteswap;
+ bool dither;
+} gifio_gifwriter_t;
diff --git a/circuitpython/shared-module/gifio/__init__.c b/circuitpython/shared-module/gifio/__init__.c
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/circuitpython/shared-module/gifio/__init__.c
diff --git a/circuitpython/shared-module/gifio/__init__.h b/circuitpython/shared-module/gifio/__init__.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/circuitpython/shared-module/gifio/__init__.h
diff --git a/circuitpython/shared-module/imagecapture/ParallelImageCapture.c b/circuitpython/shared-module/imagecapture/ParallelImageCapture.c
new file mode 100644
index 0000000..346bc76
--- /dev/null
+++ b/circuitpython/shared-module/imagecapture/ParallelImageCapture.c
@@ -0,0 +1,44 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Scott Shawcroft 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.
+ */
+
+#include "shared-bindings/imagecapture/ParallelImageCapture.h"
+#include "py/runtime.h"
+
+// If the continuous-capture mode isn't supported, then this default (weak) implementation will raise exceptions for you
+__attribute__((weak))
+void common_hal_imagecapture_parallelimagecapture_continuous_capture_start(imagecapture_parallelimagecapture_obj_t *self, mp_obj_t buffer1, mp_obj_t buffer2) {
+ mp_raise_NotImplementedError(translate("This microcontroller does not support continuous capture."));
+}
+
+__attribute__((weak))
+void common_hal_imagecapture_parallelimagecapture_continuous_capture_stop(imagecapture_parallelimagecapture_obj_t *self) {
+ mp_raise_NotImplementedError(translate("This microcontroller does not support continuous capture."));
+}
+
+__attribute__((weak))
+mp_obj_t common_hal_imagecapture_parallelimagecapture_continuous_capture_get_frame(imagecapture_parallelimagecapture_obj_t *self) {
+ mp_raise_NotImplementedError(translate("This microcontroller does not support continuous capture."));
+}
diff --git a/circuitpython/shared-module/ipaddress/IPv4Address.c b/circuitpython/shared-module/ipaddress/IPv4Address.c
new file mode 100644
index 0000000..6850fca
--- /dev/null
+++ b/circuitpython/shared-module/ipaddress/IPv4Address.c
@@ -0,0 +1,39 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Scott Shawcroft 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.
+ */
+
+#include "py/obj.h"
+
+#include "shared-bindings/ipaddress/__init__.h"
+#include "shared-bindings/ipaddress/IPv4Address.h"
+
+
+void common_hal_ipaddress_ipv4address_construct(ipaddress_ipv4address_obj_t *self, uint8_t *buf, size_t len) {
+ self->ip_bytes = mp_obj_new_bytes(buf, len);
+}
+
+mp_obj_t common_hal_ipaddress_ipv4address_get_packed(ipaddress_ipv4address_obj_t *self) {
+ return self->ip_bytes;
+}
diff --git a/circuitpython/shared-module/ipaddress/IPv4Address.h b/circuitpython/shared-module/ipaddress/IPv4Address.h
new file mode 100644
index 0000000..3f2bde0
--- /dev/null
+++ b/circuitpython/shared-module/ipaddress/IPv4Address.h
@@ -0,0 +1,37 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Scott Shawcroft 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.
+ */
+
+#ifndef MICROPY_INCLUDED_SHARED_MODULE_IPADDRESS_IPV4ADDRESS_H
+#define MICROPY_INCLUDED_SHARED_MODULE_IPADDRESS_IPV4ADDRESS_H
+
+#include "py/obj.h"
+
+typedef struct {
+ mp_obj_base_t base;
+ mp_obj_t ip_bytes;
+} ipaddress_ipv4address_obj_t;
+
+#endif // MICROPY_INCLUDED_SHARED_MODULE_IPADDRESS_IPV4ADDRESS_H
diff --git a/circuitpython/shared-module/ipaddress/__init__.c b/circuitpython/shared-module/ipaddress/__init__.c
new file mode 100644
index 0000000..9d98c0e
--- /dev/null
+++ b/circuitpython/shared-module/ipaddress/__init__.c
@@ -0,0 +1,35 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Scott Shawcroft 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.
+ */
+
+#include "shared-bindings/ipaddress/__init__.h"
+#include "shared-bindings/ipaddress/IPv4Address.h"
+
+mp_obj_t common_hal_ipaddress_new_ipv4address(uint32_t value) {
+ ipaddress_ipv4address_obj_t *self = m_new_obj(ipaddress_ipv4address_obj_t);
+ self->base.type = &ipaddress_ipv4address_type;
+ common_hal_ipaddress_ipv4address_construct(self, (uint8_t *)&value, 4);
+ return MP_OBJ_FROM_PTR(self);
+}
diff --git a/circuitpython/shared-module/ipaddress/__init__.h b/circuitpython/shared-module/ipaddress/__init__.h
new file mode 100644
index 0000000..7d5f2c2
--- /dev/null
+++ b/circuitpython/shared-module/ipaddress/__init__.h
@@ -0,0 +1,36 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Scott Shawcroft 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.
+ */
+
+#ifndef MICROPY_INCLUDED_SHARED_MODULE_IPADDRESS___INIT___H
+#define MICROPY_INCLUDED_SHARED_MODULE_IPADDRESS___INIT___H
+
+#include <stdint.h>
+
+#include "py/obj.h"
+
+mp_obj_t common_hal_ipaddress_new_ipv4(uint32_t value);
+
+#endif // MICROPY_INCLUDED_SHARED_MODULE_IPADDRESS___INIT___H
diff --git a/circuitpython/shared-module/is31fl3741/FrameBuffer.c b/circuitpython/shared-module/is31fl3741/FrameBuffer.c
new file mode 100644
index 0000000..400f6f8
--- /dev/null
+++ b/circuitpython/shared-module/is31fl3741/FrameBuffer.c
@@ -0,0 +1,227 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 Mark Komus
+ *
+ * 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.
+ */
+
+#include <string.h>
+
+#include "py/gc.h"
+#include "py/obj.h"
+#include "py/objarray.h"
+#include "py/objproperty.h"
+#include "py/runtime.h"
+
+#include "shared-module/is31fl3741/allocator.h"
+#include "shared-bindings/is31fl3741/IS31FL3741.h"
+#include "shared-bindings/is31fl3741/FrameBuffer.h"
+#include "shared-bindings/util.h"
+#include "shared-module/framebufferio/FramebufferDisplay.h"
+#include "shared-bindings/busio/I2C.h"
+
+void common_hal_is31fl3741_FrameBuffer_construct(is31fl3741_FrameBuffer_obj_t *self, int width, int height, mp_obj_t framebuffer, is31fl3741_IS31FL3741_obj_t *is31, mp_obj_t mapping) {
+ self->width = width;
+ self->height = height;
+
+ self->bufsize = sizeof(uint32_t) * width * height;
+
+ self->is31fl3741 = is31;
+
+ common_hal_busio_i2c_never_reset(self->is31fl3741->i2c);
+ // Our object is statically allocated off the heap so make sure the bus object lives to the end
+ // of the heap as well.
+ gc_never_free(self->is31fl3741->i2c);
+ gc_never_free(self->is31fl3741);
+
+ mp_obj_t *items;
+ size_t len;
+ mp_obj_tuple_get(mapping, &len, &items);
+
+ if (len != (size_t)(self->scale_width * self->scale_height * 3)) {
+ mp_raise_ValueError(translate("LED mappings must match display size"));
+ }
+
+ self->mapping = common_hal_is31fl3741_allocator_impl(sizeof(uint16_t) * len);
+ for (size_t i = 0; i < len; i++) {
+ mp_int_t value = mp_obj_get_int(items[i]);
+ // We only store up to 16 bits
+ if (value > 0xFFFF) {
+ value = 0xFFFF;
+ }
+ self->mapping[i] = (uint16_t)value;
+ }
+
+ common_hal_is31fl3741_FrameBuffer_reconstruct(self, framebuffer);
+}
+
+void common_hal_is31fl3741_FrameBuffer_reconstruct(is31fl3741_FrameBuffer_obj_t *self, mp_obj_t framebuffer) {
+ self->paused = 1;
+
+ if (framebuffer) {
+ self->framebuffer = framebuffer;
+ mp_get_buffer_raise(self->framebuffer, &self->bufinfo, MP_BUFFER_READ);
+ if (mp_get_buffer(self->framebuffer, &self->bufinfo, MP_BUFFER_RW)) {
+ self->bufinfo.typecode = 'H' | MP_OBJ_ARRAY_TYPECODE_FLAG_RW;
+ } else {
+ self->bufinfo.typecode = 'H';
+ }
+
+ // verify that the matrix is big enough
+ mp_get_index(mp_obj_get_type(self->framebuffer), self->bufinfo.len, MP_OBJ_NEW_SMALL_INT(self->bufsize - 1), false);
+ } else {
+ common_hal_is31fl3741_free_impl(self->bufinfo.buf);
+
+ self->framebuffer = NULL;
+ self->bufinfo.buf = common_hal_is31fl3741_allocator_impl(self->bufsize);
+ self->bufinfo.len = self->bufsize;
+ self->bufinfo.typecode = 'H' | MP_OBJ_ARRAY_TYPECODE_FLAG_RW;
+ }
+
+ common_hal_is31fl3741_begin_transaction(self->is31fl3741);
+ common_hal_is31fl3741_send_reset(self->is31fl3741);
+ common_hal_is31fl3741_set_current(self->is31fl3741, 0xFE);
+
+ // set scale (brightness) to max for all LEDs
+ for (int i = 0; i < 351; i++) {
+ common_hal_is31fl3741_set_led(self->is31fl3741, i, 0xFF, 2);
+ }
+
+ common_hal_is31fl3741_send_enable(self->is31fl3741);
+ common_hal_is31fl3741_end_transaction(self->is31fl3741);
+
+ self->paused = 0;
+}
+
+void common_hal_is31fl3741_FrameBuffer_deinit(is31fl3741_FrameBuffer_obj_t *self) {
+ common_hal_is31fl3741_end_transaction(self->is31fl3741); // in case we still had a lock
+
+ common_hal_is31fl3741_IS31FL3741_deinit(self->is31fl3741);
+
+ if (self->mapping != 0) {
+ common_hal_is31fl3741_free_impl(self->mapping);
+ self->mapping = 0;
+ }
+
+ self->base.type = NULL;
+
+ // If a framebuffer was passed in to the constructor, NULL the reference
+ // here so that it will become GC'able
+ self->framebuffer = NULL;
+}
+
+void common_hal_is31fl3741_FrameBuffer_set_paused(is31fl3741_FrameBuffer_obj_t *self, bool paused) {
+ self->paused = paused;
+}
+
+bool common_hal_is31fl3741_FrameBuffer_get_paused(is31fl3741_FrameBuffer_obj_t *self) {
+ return self->paused;
+}
+
+void common_hal_is31fl3741_FrameBuffer_refresh(is31fl3741_FrameBuffer_obj_t *self, uint8_t *dirtyrows) {
+ if (!self->paused) {
+ common_hal_is31fl3741_begin_transaction(self->is31fl3741);
+
+ uint8_t dirty_row_flags = 0xFF; // only supports 8 rows gotta fix
+
+ if (self->scale) {
+ // Based on the Arduino IS31FL3741 driver code
+ // dirtyrows flag current not implemented for scaled displays
+ uint32_t *buffer = self->bufinfo.buf;
+
+ for (int x = 0; x < self->scale_width; x++) {
+ uint32_t *ptr = &buffer[x * 3]; // Entry along top scan line w/x offset
+ for (int y = 0; y < self->scale_height; y++) {
+ uint16_t rsum = 0, gsum = 0, bsum = 0;
+ // Inner x/y loops are row-major on purpose (less pointer math)
+ for (uint8_t yy = 0; yy < 3; yy++) {
+ for (uint8_t xx = 0; xx < 3; xx++) {
+ uint32_t rgb = ptr[xx];
+ rsum += rgb >> 16 & 0xFF;
+ gsum += (rgb >> 8) & 0xFF;
+ bsum += rgb & 0xFF;
+ }
+ ptr += self->width; // Advance one scan line
+ }
+ rsum = rsum / 9;
+ gsum = gsum / 9;
+ bsum = bsum / 9;
+ uint32_t color = 0;
+ if (self->auto_gamma) {
+ color = (IS31GammaTable[rsum] << 16) +
+ (IS31GammaTable[gsum] << 8) +
+ IS31GammaTable[bsum];
+ } else {
+ color = (rsum << 16) + (gsum << 8) + bsum;
+ }
+ common_hal_is31fl3741_draw_pixel(self->is31fl3741, x, y, color, self->mapping);
+ }
+ }
+ } else {
+ uint32_t *buffer = self->bufinfo.buf;
+ for (int y = 0; y < self->height; y++) {
+ if ((dirtyrows != 0) && ((y % 8) == 0)) {
+ dirty_row_flags = *dirtyrows++;
+ }
+
+ if ((dirty_row_flags >> (y % 8)) & 0x1) {
+ for (int x = 0; x < self->width; x++) {
+ uint32_t color = 0;
+ if (self->auto_gamma) {
+ color = (IS31GammaTable[((*buffer) >> 16 & 0xFF)] << 16) +
+ (IS31GammaTable[((*buffer) >> 8 & 0xFF)] << 8) +
+ IS31GammaTable[((*buffer) & 0xFF)];
+ } else {
+ color = *buffer;
+ }
+
+ common_hal_is31fl3741_draw_pixel(self->is31fl3741, x, y, color, self->mapping);
+ buffer++;
+ }
+ }
+ }
+ }
+ common_hal_is31fl3741_end_transaction(self->is31fl3741);
+ }
+}
+
+int common_hal_is31fl3741_FrameBuffer_get_width(is31fl3741_FrameBuffer_obj_t *self) {
+ return self->width;
+}
+
+int common_hal_is31fl3741_FrameBuffer_get_height(is31fl3741_FrameBuffer_obj_t *self) {
+ return self->height;
+}
+
+void *common_hal_is31fl3741_allocator_impl(size_t sz) {
+ supervisor_allocation *allocation = allocate_memory(align32_size(sz), false, true);
+ return allocation ? allocation->ptr : NULL;
+}
+
+void common_hal_is31fl3741_free_impl(void *ptr_in) {
+ free_memory(allocation_from_ptr(ptr_in));
+}
+
+void is31fl3741_FrameBuffer_collect_ptrs(is31fl3741_FrameBuffer_obj_t *self) {
+ gc_collect_ptr(self->framebuffer);
+ gc_collect_ptr(self->mapping);
+}
diff --git a/circuitpython/shared-module/is31fl3741/FrameBuffer.h b/circuitpython/shared-module/is31fl3741/FrameBuffer.h
new file mode 100644
index 0000000..6d0258c
--- /dev/null
+++ b/circuitpython/shared-module/is31fl3741/FrameBuffer.h
@@ -0,0 +1,46 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 Mark Komus
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include "py/obj.h"
+#include "lib/protomatter/src/core.h"
+#include "shared-module/is31fl3741/IS31FL3741.h"
+
+extern const mp_obj_type_t is31fl3741_FrameBuffer_type;
+typedef struct {
+ mp_obj_base_t base;
+ is31fl3741_IS31FL3741_obj_t *is31fl3741;
+ is31fl3741_IS31FL3741_obj_t inline_is31fl3741;
+ mp_obj_t framebuffer;
+ mp_buffer_info_t bufinfo;
+ uint16_t bufsize, width, height, scale_width, scale_height;
+ uint16_t *mapping;
+ uint8_t bit_depth;
+ bool paused;
+ bool scale;
+ bool auto_gamma;
+} is31fl3741_FrameBuffer_obj_t;
diff --git a/circuitpython/shared-module/is31fl3741/IS31FL3741.c b/circuitpython/shared-module/is31fl3741/IS31FL3741.c
new file mode 100644
index 0000000..bb92b5b
--- /dev/null
+++ b/circuitpython/shared-module/is31fl3741/IS31FL3741.c
@@ -0,0 +1,165 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 Mark Komus
+ *
+ * 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.
+ */
+
+#include <string.h>
+
+#include "py/gc.h"
+#include "py/obj.h"
+#include "py/objarray.h"
+#include "py/objproperty.h"
+#include "py/runtime.h"
+
+#include "shared-bindings/is31fl3741/IS31FL3741.h"
+#include "shared-bindings/util.h"
+#include "shared-bindings/busio/I2C.h"
+
+void common_hal_is31fl3741_IS31FL3741_construct(is31fl3741_IS31FL3741_obj_t *self, busio_i2c_obj_t *i2c, uint8_t addr) {
+ // Probe the bus to see if a device acknowledges the given address.
+ if (!common_hal_busio_i2c_probe(i2c, addr)) {
+ self->base.type = &mp_type_NoneType;
+ mp_raise_ValueError_varg(translate("Unable to find I2C Display at %x"), addr);
+ }
+
+ self->i2c = i2c;
+ self->device_address = addr;
+}
+
+void common_hal_is31fl3741_IS31FL3741_deinit(is31fl3741_IS31FL3741_obj_t *self) {
+ common_hal_is31fl3741_end_transaction(self); // in case we still had a lock
+
+ if (self->i2c == &self->inline_i2c) {
+ common_hal_busio_i2c_deinit(self->i2c);
+ self->i2c = NULL;
+ }
+
+ self->base.type = NULL;
+}
+
+void common_hal_is31fl3741_write(is31fl3741_IS31FL3741_obj_t *is31, const mp_obj_t *mapping, const uint8_t *pixels, size_t numBytes) {
+ common_hal_is31fl3741_begin_transaction(is31);
+
+ for (size_t i = 0; i < numBytes; i += 3) {
+ uint16_t ridx = mp_obj_get_int(mapping[i]);
+ if (ridx != 65535) {
+ common_hal_is31fl3741_set_led(is31, ridx, IS31GammaTable[pixels[i]], 0); // red
+ common_hal_is31fl3741_set_led(is31, mp_obj_get_int(mapping[i + 1]), IS31GammaTable[pixels[i + 1]], 0); // green
+ common_hal_is31fl3741_set_led(is31, mp_obj_get_int(mapping[i + 2]), IS31GammaTable[pixels[i + 2]], 0); // blue
+ }
+ }
+
+ common_hal_is31fl3741_end_transaction(is31);
+}
+
+void common_hal_is31fl3741_begin_transaction(is31fl3741_IS31FL3741_obj_t *self) {
+ while (!common_hal_busio_i2c_try_lock(self->i2c)) {
+ RUN_BACKGROUND_TASKS;
+ if (mp_hal_is_interrupted()) {
+ break;
+ }
+ }
+}
+
+void common_hal_is31fl3741_end_transaction(is31fl3741_IS31FL3741_obj_t *self) {
+ common_hal_busio_i2c_unlock(self->i2c);
+}
+
+uint8_t is31fl3741_cur_page = 99; // set to invalid page to start
+
+void common_hal_is31fl3741_send_unlock(is31fl3741_IS31FL3741_obj_t *self) {
+ uint8_t unlock[2] = { 0xFE, 0xC5 }; // unlock command
+ common_hal_busio_i2c_write(self->i2c, self->device_address, unlock, 2);
+}
+
+void common_hal_is31fl3741_set_page(is31fl3741_IS31FL3741_obj_t *self, uint8_t p) {
+ if (p == is31fl3741_cur_page) {
+ return;
+ }
+
+ is31fl3741_cur_page = p;
+ common_hal_is31fl3741_send_unlock(self);
+
+ uint8_t page[2] = { 0xFD, 0x00 }; // page command
+ page[1] = p;
+ common_hal_busio_i2c_write(self->i2c, self->device_address, page, 2);
+}
+
+void common_hal_is31fl3741_send_enable(is31fl3741_IS31FL3741_obj_t *self) {
+ common_hal_is31fl3741_set_page(self, 4);
+ uint8_t enable[2] = { 0x00, 0x01 }; // enable command
+ common_hal_busio_i2c_write(self->i2c, self->device_address, enable, 2);
+}
+
+void common_hal_is31fl3741_send_reset(is31fl3741_IS31FL3741_obj_t *self) {
+ common_hal_is31fl3741_set_page(self, 4);
+ uint8_t rst[2] = { 0x3F, 0xAE }; // reset command
+ common_hal_busio_i2c_write(self->i2c, self->device_address, rst, 2);
+}
+
+void common_hal_is31fl3741_set_current(is31fl3741_IS31FL3741_obj_t *self, uint8_t current) {
+ common_hal_is31fl3741_set_page(self, 4);
+ uint8_t gcur[2] = { 0x01, 0x00 }; // global current command
+ gcur[1] = current;
+ common_hal_busio_i2c_write(self->i2c, self->device_address, gcur, 2);
+}
+
+uint8_t common_hal_is31fl3741_get_current(is31fl3741_IS31FL3741_obj_t *self) {
+ common_hal_is31fl3741_set_page(self, 4);
+ uint8_t gcur = 0x01; // global current command
+ common_hal_busio_i2c_write_read(self->i2c, self->device_address, &gcur, 1, &gcur, 1);
+ return gcur;
+}
+
+void common_hal_is31fl3741_set_led(is31fl3741_IS31FL3741_obj_t *self, uint16_t led, uint8_t level, uint8_t page) {
+ uint8_t cmd[2] = { 0x00, 0x00 };
+
+ if (led < 180) {
+ common_hal_is31fl3741_set_page(self, page);
+ cmd[0] = (uint8_t)led;
+ } else {
+ common_hal_is31fl3741_set_page(self, page + 1);
+ cmd[0] = (uint8_t)(led - 180);
+ }
+
+ cmd[1] = level;
+
+ common_hal_busio_i2c_write(self->i2c, self->device_address, cmd, 2);
+}
+
+void common_hal_is31fl3741_draw_pixel(is31fl3741_IS31FL3741_obj_t *self, int16_t x, int16_t y, uint32_t color, uint16_t *mapping) {
+ uint8_t r = color >> 16 & 0xFF;
+ uint8_t g = color >> 8 & 0xFF;
+ uint8_t b = color & 0xFF;
+
+ int16_t x1 = (x * 5 + y) * 3;
+ uint16_t ridx = mapping[x1 + 2];
+ if (ridx != 65535) {
+ uint16_t gidx = mapping[x1 + 1];
+ uint16_t bidx = mapping[x1 + 0];
+ common_hal_is31fl3741_set_led(self, ridx, r, 0);
+ common_hal_is31fl3741_set_led(self, gidx, g, 0);
+ common_hal_is31fl3741_set_led(self, bidx, b, 0);
+ }
+}
diff --git a/circuitpython/shared-module/is31fl3741/IS31FL3741.h b/circuitpython/shared-module/is31fl3741/IS31FL3741.h
new file mode 100644
index 0000000..03f1902
--- /dev/null
+++ b/circuitpython/shared-module/is31fl3741/IS31FL3741.h
@@ -0,0 +1,61 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 Mark Komus
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include "py/obj.h"
+#include "lib/protomatter/src/core.h"
+#include "shared-bindings/busio/I2C.h"
+
+extern const mp_obj_type_t is31fl3741_is31fl3741_type;
+typedef struct {
+ mp_obj_base_t base;
+ busio_i2c_obj_t *i2c;
+ busio_i2c_obj_t inline_i2c;
+ uint8_t device_address;
+} is31fl3741_IS31FL3741_obj_t;
+
+// Gamma correction table
+static const uint8_t IS31GammaTable[256] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3,
+ 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6,
+ 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10,
+ 11, 11, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16, 17,
+ 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 24, 24, 25,
+ 25, 26, 27, 27, 28, 29, 29, 30, 31, 31, 32, 33, 34, 34, 35,
+ 36, 37, 38, 38, 39, 40, 41, 42, 42, 43, 44, 45, 46, 47, 48,
+ 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
+ 64, 65, 66, 68, 69, 70, 71, 72, 73, 75, 76, 77, 78, 80, 81,
+ 82, 84, 85, 86, 88, 89, 90, 92, 93, 94, 96, 97, 99, 100, 102,
+ 103, 105, 106, 108, 109, 111, 112, 114, 115, 117, 119, 120, 122, 124, 125,
+ 127, 129, 130, 132, 134, 136, 137, 139, 141, 143, 145, 146, 148, 150, 152,
+ 154, 156, 158, 160, 162, 164, 166, 168, 170, 172, 174, 176, 178, 180, 182,
+ 184, 186, 188, 191, 193, 195, 197, 199, 202, 204, 206, 209, 211, 213, 215,
+ 218, 220, 223, 225, 227, 230, 232, 235, 237, 240, 242, 245, 247, 250, 252,
+ 255
+};
diff --git a/circuitpython/shared-module/is31fl3741/__init__.c b/circuitpython/shared-module/is31fl3741/__init__.c
new file mode 100644
index 0000000..c267047
--- /dev/null
+++ b/circuitpython/shared-module/is31fl3741/__init__.c
@@ -0,0 +1,25 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 Mark Komus
+ *
+ * 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.
+ */
diff --git a/circuitpython/shared-module/is31fl3741/__init__.h b/circuitpython/shared-module/is31fl3741/__init__.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/circuitpython/shared-module/is31fl3741/__init__.h
diff --git a/circuitpython/shared-module/is31fl3741/allocator.h b/circuitpython/shared-module/is31fl3741/allocator.h
new file mode 100644
index 0000000..43906f5
--- /dev/null
+++ b/circuitpython/shared-module/is31fl3741/allocator.h
@@ -0,0 +1,35 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 Mark Komus
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include <stdbool.h>
+#include "py/gc.h"
+#include "py/misc.h"
+#include "supervisor/memory.h"
+
+extern void *common_hal_is31fl3741_allocator_impl(size_t sz);
+extern void common_hal_is31fl3741_free_impl(void *);
diff --git a/circuitpython/shared-module/keypad/Event.c b/circuitpython/shared-module/keypad/Event.c
new file mode 100644
index 0000000..70a30e3
--- /dev/null
+++ b/circuitpython/shared-module/keypad/Event.c
@@ -0,0 +1,50 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 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.
+ */
+
+#include "shared-module/keypad/Event.h"
+#include "shared-bindings/keypad/Event.h"
+
+void common_hal_keypad_event_construct(keypad_event_obj_t *self, mp_uint_t key_number, bool pressed, mp_obj_t timestamp) {
+ self->key_number = key_number;
+ self->pressed = pressed;
+ self->timestamp = timestamp;
+}
+
+mp_int_t common_hal_keypad_event_get_key_number(keypad_event_obj_t *self) {
+ return self->key_number;
+}
+
+bool common_hal_keypad_event_get_pressed(keypad_event_obj_t *self) {
+ return self->pressed;
+}
+
+bool common_hal_keypad_event_get_released(keypad_event_obj_t *self) {
+ return !self->pressed;
+}
+
+mp_obj_t common_hal_keypad_event_get_timestamp(keypad_event_obj_t *self) {
+ return self->timestamp;
+}
diff --git a/circuitpython/shared-module/keypad/Event.h b/circuitpython/shared-module/keypad/Event.h
new file mode 100644
index 0000000..c30eb9a
--- /dev/null
+++ b/circuitpython/shared-module/keypad/Event.h
@@ -0,0 +1,40 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 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.
+ */
+
+#ifndef MICROPY_INCLUDED_SHARED_MODULE_KEYPAD_EVENT_H
+#define MICROPY_INCLUDED_SHARED_MODULE_KEYPAD_EVENT_H
+
+#include "py/obj.h"
+
+typedef struct {
+ mp_obj_base_t base;
+ uint16_t key_number;
+ bool pressed;
+ mp_obj_t timestamp;
+} keypad_event_obj_t;
+
+
+#endif // MICROPY_INCLUDED_SHARED_MODULE_KEYPAD_EVENT_H
diff --git a/circuitpython/shared-module/keypad/EventQueue.c b/circuitpython/shared-module/keypad/EventQueue.c
new file mode 100644
index 0000000..eeeceb7
--- /dev/null
+++ b/circuitpython/shared-module/keypad/EventQueue.c
@@ -0,0 +1,98 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 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.
+ */
+
+#include "shared-bindings/keypad/Event.h"
+#include "shared-bindings/keypad/EventQueue.h"
+#include "shared-bindings/supervisor/__init__.h"
+#include "shared-module/keypad/EventQueue.h"
+
+// Key number is lower 15 bits of a 16-bit value.
+#define EVENT_PRESSED (1 << 15)
+#define EVENT_KEY_NUM_MASK ((1 << 15) - 1)
+
+void common_hal_keypad_eventqueue_construct(keypad_eventqueue_obj_t *self, size_t max_events) {
+ // Event queue is 16-bit values.
+ ringbuf_alloc(&self->encoded_events, max_events * (sizeof(uint16_t) + sizeof(mp_obj_t)), false);
+ self->overflowed = false;
+}
+
+bool common_hal_keypad_eventqueue_get_into(keypad_eventqueue_obj_t *self, keypad_event_obj_t *event) {
+ int encoded_event = ringbuf_get16(&self->encoded_events);
+ if (encoded_event == -1) {
+ return false;
+ }
+
+ mp_obj_t ticks;
+ ringbuf_get_n(&self->encoded_events, (uint8_t *)&ticks, sizeof(ticks));
+ // "Construct" using the existing event.
+ common_hal_keypad_event_construct(event, encoded_event & EVENT_KEY_NUM_MASK, encoded_event & EVENT_PRESSED, ticks);
+ return true;
+}
+
+mp_obj_t common_hal_keypad_eventqueue_get(keypad_eventqueue_obj_t *self) {
+ keypad_event_obj_t *event = m_new_obj(keypad_event_obj_t);
+ event->base.type = &keypad_event_type;
+ bool result = common_hal_keypad_eventqueue_get_into(self, event);
+ if (result) {
+ return event;
+ }
+ m_free(event);
+ return MP_ROM_NONE;
+}
+
+bool common_hal_keypad_eventqueue_get_overflowed(keypad_eventqueue_obj_t *self) {
+ return self->overflowed;
+}
+
+void common_hal_keypad_eventqueue_set_overflowed(keypad_eventqueue_obj_t *self, bool overflowed) {
+ self->overflowed = overflowed;
+}
+
+void common_hal_keypad_eventqueue_clear(keypad_eventqueue_obj_t *self) {
+ ringbuf_clear(&self->encoded_events);
+ common_hal_keypad_eventqueue_set_overflowed(self, false);
+}
+
+size_t common_hal_keypad_eventqueue_get_length(keypad_eventqueue_obj_t *self) {
+ return ringbuf_num_filled(&self->encoded_events);
+}
+
+bool keypad_eventqueue_record(keypad_eventqueue_obj_t *self, mp_uint_t key_number, bool pressed, mp_obj_t timestamp) {
+ if (ringbuf_num_empty(&self->encoded_events) == 0) {
+ // Queue is full. Set the overflow flag. The caller will decide what else to do.
+ common_hal_keypad_eventqueue_set_overflowed(self, true);
+ return false;
+ }
+
+ uint16_t encoded_event = key_number & EVENT_KEY_NUM_MASK;
+ if (pressed) {
+ encoded_event |= EVENT_PRESSED;
+ }
+ ringbuf_put16(&self->encoded_events, encoded_event);
+ ringbuf_put_n(&self->encoded_events, (uint8_t *)&timestamp, sizeof(mp_obj_t));
+
+ return true;
+}
diff --git a/circuitpython/shared-module/keypad/EventQueue.h b/circuitpython/shared-module/keypad/EventQueue.h
new file mode 100644
index 0000000..b523b16
--- /dev/null
+++ b/circuitpython/shared-module/keypad/EventQueue.h
@@ -0,0 +1,41 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 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.
+ */
+
+#ifndef MICROPY_INCLUDED_SHARED_MODULE_KEYPAD_EVENTQUEUE_H
+#define MICROPY_INCLUDED_SHARED_MODULE_KEYPAD_EVENTQUEUE_H
+
+#include "py/obj.h"
+#include "py/ringbuf.h"
+
+typedef struct _keypad_eventqueue_obj_t {
+ mp_obj_base_t base;
+ ringbuf_t encoded_events;
+ bool overflowed;
+} keypad_eventqueue_obj_t;
+
+bool keypad_eventqueue_record(keypad_eventqueue_obj_t *self, mp_uint_t key_number, bool pressed, mp_obj_t timestamp);
+
+#endif // MICROPY_INCLUDED_SHARED_MODULE_KEYPAD_EVENTQUEUE_H
diff --git a/circuitpython/shared-module/keypad/KeyMatrix.c b/circuitpython/shared-module/keypad/KeyMatrix.c
new file mode 100644
index 0000000..b252052
--- /dev/null
+++ b/circuitpython/shared-module/keypad/KeyMatrix.c
@@ -0,0 +1,165 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 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.
+ */
+
+#include <string.h>
+
+#include "py/gc.h"
+#include "py/runtime.h"
+#include "shared-bindings/digitalio/DigitalInOut.h"
+#include "shared-bindings/keypad/EventQueue.h"
+#include "shared-bindings/keypad/KeyMatrix.h"
+#include "shared-bindings/keypad/__init__.h"
+#include "shared-bindings/supervisor/__init__.h"
+#include "shared-bindings/util.h"
+#include "supervisor/port.h"
+#include "supervisor/shared/tick.h"
+
+static void keymatrix_scan_now(void *self_in, mp_obj_t timestamp);
+static size_t keymatrix_get_key_count(void *self_in);
+
+static keypad_scanner_funcs_t keymatrix_funcs = {
+ .scan_now = keymatrix_scan_now,
+ .get_key_count = keymatrix_get_key_count,
+};
+
+static mp_uint_t row_column_to_key_number(keypad_keymatrix_obj_t *self, mp_uint_t row, mp_uint_t column) {
+ return row * self->column_digitalinouts->len + column;
+}
+
+void common_hal_keypad_keymatrix_construct(keypad_keymatrix_obj_t *self, mp_uint_t num_row_pins, const mcu_pin_obj_t *row_pins[], mp_uint_t num_column_pins, const mcu_pin_obj_t *column_pins[], bool columns_to_anodes, mp_float_t interval, size_t max_events) {
+
+ mp_obj_t row_dios[num_row_pins];
+ for (size_t row = 0; row < num_row_pins; row++) {
+ digitalio_digitalinout_obj_t *dio = m_new_obj(digitalio_digitalinout_obj_t);
+ dio->base.type = &digitalio_digitalinout_type;
+ common_hal_digitalio_digitalinout_construct(dio, row_pins[row]);
+ common_hal_digitalio_digitalinout_switch_to_input(dio, columns_to_anodes ? PULL_UP : PULL_DOWN);
+ row_dios[row] = dio;
+ }
+ self->row_digitalinouts = mp_obj_new_tuple(num_row_pins, row_dios);
+
+ mp_obj_t column_dios[num_column_pins];
+ for (size_t column = 0; column < num_column_pins; column++) {
+ digitalio_digitalinout_obj_t *dio = m_new_obj(digitalio_digitalinout_obj_t);
+ dio->base.type = &digitalio_digitalinout_type;
+ common_hal_digitalio_digitalinout_construct(dio, column_pins[column]);
+ common_hal_digitalio_digitalinout_switch_to_input(dio, columns_to_anodes ? PULL_UP : PULL_DOWN);
+ column_dios[column] = dio;
+ }
+ self->column_digitalinouts = mp_obj_new_tuple(num_column_pins, column_dios);
+
+ self->currently_pressed = (bool *)gc_alloc(sizeof(bool) * num_row_pins * num_column_pins, false, false);
+ self->previously_pressed = (bool *)gc_alloc(sizeof(bool) * num_row_pins * num_column_pins, false, false);
+
+ self->columns_to_anodes = columns_to_anodes;
+ self->funcs = &keymatrix_funcs;
+
+ keypad_construct_common((keypad_scanner_obj_t *)self, interval, max_events);
+}
+
+void common_hal_keypad_keymatrix_deinit(keypad_keymatrix_obj_t *self) {
+ if (common_hal_keypad_deinited(self)) {
+ return;
+ }
+
+ // Remove self from the list of active keypad scanners first.
+ keypad_deregister_scanner((keypad_scanner_obj_t *)self);
+
+ for (size_t row = 0; row < common_hal_keypad_keymatrix_get_row_count(self); row++) {
+ common_hal_digitalio_digitalinout_deinit(self->row_digitalinouts->items[row]);
+ }
+ self->row_digitalinouts = MP_ROM_NONE;
+
+ for (size_t column = 0; column < common_hal_keypad_keymatrix_get_column_count(self); column++) {
+ common_hal_digitalio_digitalinout_deinit(self->column_digitalinouts->items[column]);
+ }
+ self->column_digitalinouts = MP_ROM_NONE;
+ common_hal_keypad_deinit_core(self);
+}
+
+size_t common_hal_keypad_keymatrix_get_row_count(keypad_keymatrix_obj_t *self) {
+ return self->row_digitalinouts->len;
+}
+
+size_t common_hal_keypad_keymatrix_get_column_count(keypad_keymatrix_obj_t *self) {
+ return self->column_digitalinouts->len;
+}
+
+mp_uint_t common_hal_keypad_keymatrix_row_column_to_key_number(keypad_keymatrix_obj_t *self, mp_uint_t row, mp_uint_t column) {
+ return row_column_to_key_number(self, row, column);
+}
+
+void common_hal_keypad_keymatrix_key_number_to_row_column(keypad_keymatrix_obj_t *self, mp_uint_t key_number, mp_uint_t *row, mp_uint_t *column) {
+ const size_t num_columns = common_hal_keypad_keymatrix_get_column_count(self);
+ *row = key_number / num_columns;
+ *column = key_number % num_columns;
+}
+
+static size_t keymatrix_get_key_count(void *self_in) {
+ keypad_keymatrix_obj_t *self = self_in;
+ return common_hal_keypad_keymatrix_get_column_count(self) * common_hal_keypad_keymatrix_get_row_count(self);
+}
+
+static void keymatrix_scan_now(void *self_in, mp_obj_t timestamp) {
+ keypad_keymatrix_obj_t *self = self_in;
+
+ // On entry, all pins are set to inputs with a pull-up or pull-down,
+ // depending on the diode orientation.
+ for (size_t row = 0; row < common_hal_keypad_keymatrix_get_row_count(self); row++) {
+ // Switch this row to an output and set level appropriately
+ // Set low if columns_to_anodes is true, else set high.
+ digitalio_digitalinout_obj_t *row_dio = self->row_digitalinouts->items[row];
+ common_hal_digitalio_digitalinout_switch_to_output(
+ row_dio, !self->columns_to_anodes, DRIVE_MODE_PUSH_PULL);
+
+ for (size_t column = 0; column < common_hal_keypad_keymatrix_get_column_count(self); column++) {
+ mp_uint_t key_number = row_column_to_key_number(self, row, column);
+ const bool previous = self->currently_pressed[key_number];
+ self->previously_pressed[key_number] = previous;
+
+ // Get the current state, by reading whether the column got pulled to the row value or not.
+ // If low and columns_to_anodes is true, the key is pressed.
+ // If high and columns_to_anodes is false, the key is pressed.
+ const bool current =
+ common_hal_digitalio_digitalinout_get_value(self->column_digitalinouts->items[column]) !=
+ self->columns_to_anodes;
+ self->currently_pressed[key_number] = current;
+
+ // Record any transitions.
+ if (previous != current) {
+ keypad_eventqueue_record(self->events, key_number, current, timestamp);
+ }
+ }
+
+ // Done with this row. Set its pin to its resting pull value briefly to shorten the time it takes
+ // to switch values. Just switching to an input with a (relatively weak) pullup/pulldown
+ // causes a slight delay in the output changing, which can cause false readings.
+ common_hal_digitalio_digitalinout_set_value(row_dio, self->columns_to_anodes);
+ // Switch the row back to an input, pulled appropriately
+ common_hal_digitalio_digitalinout_switch_to_input(
+ row_dio, self->columns_to_anodes ? PULL_UP : PULL_DOWN);
+ }
+}
diff --git a/circuitpython/shared-module/keypad/KeyMatrix.h b/circuitpython/shared-module/keypad/KeyMatrix.h
new file mode 100644
index 0000000..8049fcb
--- /dev/null
+++ b/circuitpython/shared-module/keypad/KeyMatrix.h
@@ -0,0 +1,46 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 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.
+ */
+
+#ifndef MICROPY_INCLUDED_SHARED_MODULE_KEYPAD_KEYMATRIX_H
+#define MICROPY_INCLUDED_SHARED_MODULE_KEYPAD_KEYMATRIX_H
+
+#include "py/obj.h"
+#include "py/objtuple.h"
+
+#include "common-hal/digitalio/DigitalInOut.h"
+#include "shared-module/keypad/__init__.h"
+#include "shared-module/keypad/EventQueue.h"
+
+typedef struct {
+ KEYPAD_SCANNER_COMMON_FIELDS;
+ mp_obj_tuple_t *row_digitalinouts;
+ mp_obj_tuple_t *column_digitalinouts;
+ bool columns_to_anodes;
+} keypad_keymatrix_obj_t;
+
+void keypad_keymatrix_scan(keypad_keymatrix_obj_t *self);
+
+#endif // MICROPY_INCLUDED_SHARED_MODULE_KEYPAD_KEYMATRIX_H
diff --git a/circuitpython/shared-module/keypad/Keys.c b/circuitpython/shared-module/keypad/Keys.c
new file mode 100644
index 0000000..d74a3ab
--- /dev/null
+++ b/circuitpython/shared-module/keypad/Keys.c
@@ -0,0 +1,111 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 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.
+ */
+
+#include <string.h>
+
+#include "py/gc.h"
+#include "py/runtime.h"
+#include "shared-bindings/digitalio/DigitalInOut.h"
+#include "shared-bindings/keypad/EventQueue.h"
+#include "shared-bindings/keypad/Keys.h"
+#include "shared-bindings/keypad/__init__.h"
+#include "shared-bindings/supervisor/__init__.h"
+#include "supervisor/port.h"
+#include "supervisor/shared/tick.h"
+
+static void keypad_keys_scan_now(void *self_in, mp_obj_t timestamp);
+static size_t keys_get_key_count(void *self_in);
+
+static keypad_scanner_funcs_t keys_funcs = {
+ .scan_now = keypad_keys_scan_now,
+ .get_key_count = keys_get_key_count,
+};
+
+void common_hal_keypad_keys_construct(keypad_keys_obj_t *self, mp_uint_t num_pins, const mcu_pin_obj_t *pins[], bool value_when_pressed, bool pull, mp_float_t interval, size_t max_events) {
+ mp_obj_t dios[num_pins];
+
+ for (size_t i = 0; i < num_pins; i++) {
+ digitalio_digitalinout_obj_t *dio = m_new_obj(digitalio_digitalinout_obj_t);
+ dio->base.type = &digitalio_digitalinout_type;
+ common_hal_digitalio_digitalinout_construct(dio, pins[i]);
+ if (pull) {
+ common_hal_digitalio_digitalinout_set_pull(dio, value_when_pressed ? PULL_DOWN : PULL_UP);
+ }
+ dios[i] = dio;
+ }
+
+ self->digitalinouts = mp_obj_new_tuple(num_pins, dios);
+ self->currently_pressed = (bool *)gc_alloc(sizeof(bool) * num_pins, false, false);
+ self->previously_pressed = (bool *)gc_alloc(sizeof(bool) * num_pins, false, false);
+ self->value_when_pressed = value_when_pressed;
+ self->funcs = &keys_funcs;
+
+ keypad_construct_common((keypad_scanner_obj_t *)self, interval, max_events);
+
+}
+
+void common_hal_keypad_keys_deinit(keypad_keys_obj_t *self) {
+ if (common_hal_keypad_deinited(self)) {
+ return;
+ }
+
+ // Remove self from the list of active keypad scanners first.
+ keypad_deregister_scanner((keypad_scanner_obj_t *)self);
+
+ for (size_t key = 0; key < keys_get_key_count(self); key++) {
+ common_hal_digitalio_digitalinout_deinit(self->digitalinouts->items[key]);
+ }
+ self->digitalinouts = MP_ROM_NONE;
+
+ common_hal_keypad_deinit_core(self);
+}
+
+size_t keys_get_key_count(void *self_in) {
+ keypad_keys_obj_t *self = self_in;
+ return self->digitalinouts->len;
+}
+
+static void keypad_keys_scan_now(void *self_in, mp_obj_t timestamp) {
+ keypad_keys_obj_t *self = self_in;
+ size_t key_count = keys_get_key_count(self);
+
+ for (mp_uint_t key_number = 0; key_number < key_count; key_number++) {
+ // Remember the previous up/down state.
+ const bool previous = self->currently_pressed[key_number];
+ self->previously_pressed[key_number] = previous;
+
+ // Get the current state.
+ const bool current =
+ common_hal_digitalio_digitalinout_get_value(self->digitalinouts->items[key_number]) ==
+ self->value_when_pressed;
+ self->currently_pressed[key_number] = current;
+
+ // Record any transitions.
+ if (previous != current) {
+ keypad_eventqueue_record(self->events, key_number, current, timestamp);
+ }
+ }
+}
diff --git a/circuitpython/shared-module/keypad/Keys.h b/circuitpython/shared-module/keypad/Keys.h
new file mode 100644
index 0000000..6bd7d7a
--- /dev/null
+++ b/circuitpython/shared-module/keypad/Keys.h
@@ -0,0 +1,45 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 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.
+ */
+
+#ifndef MICROPY_INCLUDED_SHARED_MODULE_KEYPAD_KEYS_H
+#define MICROPY_INCLUDED_SHARED_MODULE_KEYPAD_KEYS_H
+
+#include "py/obj.h"
+#include "py/objtuple.h"
+
+#include "common-hal/digitalio/DigitalInOut.h"
+#include "shared-module/keypad/__init__.h"
+#include "shared-module/keypad/EventQueue.h"
+
+typedef struct {
+ KEYPAD_SCANNER_COMMON_FIELDS;
+ mp_obj_tuple_t *digitalinouts;
+ bool value_when_pressed;
+} keypad_keys_obj_t;
+
+void keypad_keys_scan(keypad_keys_obj_t *self);
+
+#endif // MICROPY_INCLUDED_SHARED_MODULE_KEYPAD_KEYS_H
diff --git a/circuitpython/shared-module/keypad/ShiftRegisterKeys.c b/circuitpython/shared-module/keypad/ShiftRegisterKeys.c
new file mode 100644
index 0000000..b2b10c6
--- /dev/null
+++ b/circuitpython/shared-module/keypad/ShiftRegisterKeys.c
@@ -0,0 +1,134 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 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.
+ */
+
+#include <string.h>
+
+#include "py/gc.h"
+#include "py/runtime.h"
+#include "shared-bindings/digitalio/DigitalInOut.h"
+#include "shared-bindings/keypad/EventQueue.h"
+#include "shared-bindings/keypad/ShiftRegisterKeys.h"
+#include "shared-bindings/keypad/__init__.h"
+#include "shared-bindings/supervisor/__init__.h"
+#include "supervisor/port.h"
+#include "supervisor/shared/tick.h"
+
+static void shiftregisterkeys_scan_now(void *self, mp_obj_t timestamp);
+static size_t shiftregisterkeys_get_key_count(void *self);
+
+static keypad_scanner_funcs_t shiftregisterkeys_funcs = {
+ .scan_now = shiftregisterkeys_scan_now,
+ .get_key_count = shiftregisterkeys_get_key_count,
+};
+
+void common_hal_keypad_shiftregisterkeys_construct(keypad_shiftregisterkeys_obj_t *self, const mcu_pin_obj_t *clock_pin, const mcu_pin_obj_t *data_pin, const mcu_pin_obj_t *latch_pin, bool value_to_latch, size_t key_count, bool value_when_pressed, mp_float_t interval, size_t max_events) {
+
+ digitalio_digitalinout_obj_t *clock = m_new_obj(digitalio_digitalinout_obj_t);
+ clock->base.type = &digitalio_digitalinout_type;
+ common_hal_digitalio_digitalinout_construct(clock, clock_pin);
+ common_hal_digitalio_digitalinout_switch_to_output(clock, false, DRIVE_MODE_PUSH_PULL);
+ self->clock = clock;
+
+ digitalio_digitalinout_obj_t *data = m_new_obj(digitalio_digitalinout_obj_t);
+ data->base.type = &digitalio_digitalinout_type;
+ common_hal_digitalio_digitalinout_construct(data, data_pin);
+ common_hal_digitalio_digitalinout_switch_to_input(data, PULL_NONE);
+ self->data = data;
+
+ digitalio_digitalinout_obj_t *latch = m_new_obj(digitalio_digitalinout_obj_t);
+ latch->base.type = &digitalio_digitalinout_type;
+
+ common_hal_digitalio_digitalinout_construct(latch, latch_pin);
+ common_hal_digitalio_digitalinout_switch_to_output(latch, true, DRIVE_MODE_PUSH_PULL);
+ self->latch = latch;
+ self->value_to_latch = value_to_latch;
+
+ self->value_when_pressed = value_when_pressed;
+ self->key_count = key_count;
+ self->funcs = &shiftregisterkeys_funcs;
+
+ keypad_construct_common((keypad_scanner_obj_t *)self, interval, max_events);
+}
+
+void common_hal_keypad_shiftregisterkeys_deinit(keypad_shiftregisterkeys_obj_t *self) {
+ if (common_hal_keypad_deinited(self)) {
+ return;
+ }
+
+ // Remove self from the list of active keypad scanners first.
+ keypad_deregister_scanner((keypad_scanner_obj_t *)self);
+
+
+ common_hal_digitalio_digitalinout_deinit(self->clock);
+ self->clock = MP_ROM_NONE;
+
+ common_hal_digitalio_digitalinout_deinit(self->data);
+ self->data = MP_ROM_NONE;
+
+ common_hal_digitalio_digitalinout_deinit(self->latch);
+ self->latch = MP_ROM_NONE;
+
+ common_hal_keypad_deinit_core(self);
+}
+
+size_t shiftregisterkeys_get_key_count(void *self_in) {
+ keypad_shiftregisterkeys_obj_t *self = self_in;
+ return self->key_count;
+}
+
+static void shiftregisterkeys_scan_now(void *self_in, mp_obj_t timestamp) {
+ keypad_shiftregisterkeys_obj_t *self = self_in;
+
+ // Latch (freeze) the current state of the input pins.
+ common_hal_digitalio_digitalinout_set_value(self->latch, self->value_to_latch);
+
+ const size_t key_count = shiftregisterkeys_get_key_count(self);
+
+ for (mp_uint_t key_number = 0; key_number < key_count; key_number++) {
+ // Zero-th data appears on on the data pin immediately, without shifting.
+ common_hal_digitalio_digitalinout_set_value(self->clock, false);
+
+ // Remember the previous up/down state.
+ const bool previous = self->currently_pressed[key_number];
+ self->previously_pressed[key_number] = previous;
+
+ // Get the current state.
+ const bool current =
+ common_hal_digitalio_digitalinout_get_value(self->data) == self->value_when_pressed;
+ self->currently_pressed[key_number] = current;
+
+ // Trigger a shift to get the next bit.
+ common_hal_digitalio_digitalinout_set_value(self->clock, true);
+
+ // Record any transitions.
+ if (previous != current) {
+ keypad_eventqueue_record(self->events, key_number, current, timestamp);
+ }
+ }
+
+ // Start reading the input pins again.
+ common_hal_digitalio_digitalinout_set_value(self->latch, !self->value_to_latch);
+}
diff --git a/circuitpython/shared-module/keypad/ShiftRegisterKeys.h b/circuitpython/shared-module/keypad/ShiftRegisterKeys.h
new file mode 100644
index 0000000..84c66ef
--- /dev/null
+++ b/circuitpython/shared-module/keypad/ShiftRegisterKeys.h
@@ -0,0 +1,49 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 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.
+ */
+
+#ifndef MICROPY_INCLUDED_SHARED_MODULE_KEYPAD_SHIFTREGISTERKEYS_H
+#define MICROPY_INCLUDED_SHARED_MODULE_KEYPAD_SHIFTREGISTERKEYS_H
+
+#include "py/obj.h"
+#include "py/objtuple.h"
+
+#include "common-hal/digitalio/DigitalInOut.h"
+#include "shared-module/keypad/__init__.h"
+#include "shared-module/keypad/EventQueue.h"
+
+typedef struct {
+ KEYPAD_SCANNER_COMMON_FIELDS;
+ digitalio_digitalinout_obj_t *clock;
+ digitalio_digitalinout_obj_t *data;
+ digitalio_digitalinout_obj_t *latch;
+ size_t key_count;
+ bool value_when_pressed;
+ bool value_to_latch;
+} keypad_shiftregisterkeys_obj_t;
+
+void keypad_shiftregisterkeys_scan(keypad_shiftregisterkeys_obj_t *self);
+
+#endif // MICROPY_INCLUDED_SHARED_MODULE_KEYPAD_SHIFTREGISTERKEYS_H
diff --git a/circuitpython/shared-module/keypad/__init__.c b/circuitpython/shared-module/keypad/__init__.c
new file mode 100644
index 0000000..d2f5521
--- /dev/null
+++ b/circuitpython/shared-module/keypad/__init__.c
@@ -0,0 +1,157 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 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.
+ */
+
+#include <string.h>
+#include "shared-bindings/keypad/__init__.h"
+#include "shared-bindings/keypad/EventQueue.h"
+#include "shared-bindings/keypad/Keys.h"
+#include "shared-bindings/keypad/KeyMatrix.h"
+#include "shared-bindings/keypad/ShiftRegisterKeys.h"
+#include "shared-bindings/supervisor/__init__.h"
+#include "supervisor/port.h"
+#include "supervisor/shared/lock.h"
+#include "supervisor/shared/tick.h"
+
+supervisor_lock_t keypad_scanners_linked_list_lock;
+static void keypad_scan_now(keypad_scanner_obj_t *self, uint64_t now);
+static void keypad_scan_maybe(keypad_scanner_obj_t *self, uint64_t now);
+
+void keypad_tick(void) {
+ // Fast path. Return immediately there are no scanners.
+ if (!MP_STATE_VM(keypad_scanners_linked_list)) {
+ return;
+ }
+
+ // Skip scanning if someone else has the lock. Don't wait for the lock.
+ if (supervisor_try_lock(&keypad_scanners_linked_list_lock)) {
+ uint64_t now = port_get_raw_ticks(NULL);
+ mp_obj_t scanner = MP_STATE_VM(keypad_scanners_linked_list);
+ while (scanner) {
+ keypad_scan_maybe(scanner, now);
+ scanner = ((keypad_scanner_obj_t *)scanner)->next;
+ }
+ supervisor_release_lock(&keypad_scanners_linked_list_lock);
+ }
+}
+
+void keypad_reset(void) {
+ while (MP_STATE_VM(keypad_scanners_linked_list)) {
+ keypad_deregister_scanner(MP_STATE_VM(keypad_scanners_linked_list));
+ }
+}
+
+// Register a Keys, KeyMatrix, etc. that will be scanned in the background
+void keypad_register_scanner(keypad_scanner_obj_t *scanner) {
+ supervisor_acquire_lock(&keypad_scanners_linked_list_lock);
+ scanner->next = MP_STATE_VM(keypad_scanners_linked_list);
+ MP_STATE_VM(keypad_scanners_linked_list) = scanner;
+ supervisor_release_lock(&keypad_scanners_linked_list_lock);
+
+ // One more request for ticks.
+ supervisor_enable_tick();
+}
+
+// Remove scanner from the list of active scanners.
+void keypad_deregister_scanner(keypad_scanner_obj_t *scanner) {
+ // One less request for ticks.
+ supervisor_disable_tick();
+
+ supervisor_acquire_lock(&keypad_scanners_linked_list_lock);
+ if (MP_STATE_VM(keypad_scanners_linked_list) == scanner) {
+ // Scanner is at the front; splice it out.
+ MP_STATE_VM(keypad_scanners_linked_list) = scanner->next;
+ scanner->next = NULL;
+ } else {
+ keypad_scanner_obj_t *current = MP_STATE_VM(keypad_scanners_linked_list);
+ while (current) {
+ if (current->next == scanner) {
+ // Splice myself out.
+ current->next = scanner->next;
+ scanner->next = NULL;
+ break;
+ }
+ current = current->next;
+ }
+ }
+ supervisor_release_lock(&keypad_scanners_linked_list_lock);
+}
+
+void keypad_construct_common(keypad_scanner_obj_t *self, mp_float_t interval, size_t max_events) {
+ size_t key_count = common_hal_keypad_generic_get_key_count(self);
+ self->currently_pressed = (bool *)gc_alloc(sizeof(bool) * key_count, false, false);
+ self->previously_pressed = (bool *)gc_alloc(sizeof(bool) * key_count, false, false);
+
+ self->interval_ticks = (mp_uint_t)(interval * 1024); // interval * 1000 * (1024/1000)
+
+ keypad_eventqueue_obj_t *events = m_new_obj(keypad_eventqueue_obj_t);
+ events->base.type = &keypad_eventqueue_type;
+ common_hal_keypad_eventqueue_construct(events, max_events);
+ self->events = events;
+
+ // Add self to the list of active keypad scanners.
+ keypad_register_scanner(self);
+ keypad_scan_now(self, port_get_raw_ticks(NULL));
+}
+
+static void keypad_scan_now(keypad_scanner_obj_t *self, uint64_t now) {
+ self->next_scan_ticks = now + self->interval_ticks;
+ self->funcs->scan_now(self, supervisor_ticks_ms());
+}
+
+static void keypad_scan_maybe(keypad_scanner_obj_t *self, uint64_t now) {
+ if (now < self->next_scan_ticks) {
+ return;
+ }
+ keypad_scan_now(self, now);
+}
+
+void common_hal_keypad_generic_reset(void *self_in) {
+ keypad_scanner_obj_t *self = self_in;
+ size_t key_count = common_hal_keypad_generic_get_key_count(self);
+ memset(self->previously_pressed, false, key_count);
+ memset(self->currently_pressed, false, key_count);
+ keypad_scan_now(self, port_get_raw_ticks(NULL));
+}
+
+void common_hal_keypad_deinit_core(void *self_in) {
+ keypad_scanner_obj_t *self = self_in;
+ self->events = NULL;
+}
+
+bool common_hal_keypad_deinited(void *self_in) {
+ keypad_scanner_obj_t *self = self_in;
+ return !self->events;
+}
+
+size_t common_hal_keypad_generic_get_key_count(void *self_in) {
+ keypad_scanner_obj_t *self = self_in;
+ return self->funcs->get_key_count(self);
+}
+
+mp_obj_t common_hal_keypad_generic_get_events(void *self_in) {
+ keypad_scanner_obj_t *self = self_in;
+ return self->events;
+}
diff --git a/circuitpython/shared-module/keypad/__init__.h b/circuitpython/shared-module/keypad/__init__.h
new file mode 100644
index 0000000..cc792ff
--- /dev/null
+++ b/circuitpython/shared-module/keypad/__init__.h
@@ -0,0 +1,67 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 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.
+ */
+
+#ifndef SHARED_MODULE_KEYPAD_H
+#define SHARED_MODULE_KEYPAD_H
+
+#include "py/obj.h"
+#include "supervisor/shared/lock.h"
+
+typedef struct _keypad_scanner_funcs_t {
+ void (*scan_now)(void *self_in, mp_obj_t timestamp);
+ size_t (*get_key_count)(void *self_in);
+} keypad_scanner_funcs_t;
+
+// All scanners must begin with these common fields.
+// This is an ad hoc "superclass" struct for scanners, though they do
+// not actually have a superclass relationship.
+#define KEYPAD_SCANNER_COMMON_FIELDS \
+ mp_obj_base_t base; \
+ struct _keypad_scanner_obj_t *next; \
+ keypad_scanner_funcs_t *funcs; \
+ uint64_t next_scan_ticks; \
+ bool *previously_pressed; \
+ bool *currently_pressed; \
+ struct _keypad_eventqueue_obj_t *events; \
+ mp_uint_t interval_ticks
+
+typedef struct _keypad_scanner_obj_t {
+ KEYPAD_SCANNER_COMMON_FIELDS;
+} keypad_scanner_obj_t;
+
+extern supervisor_lock_t keypad_scanners_linked_list_lock;
+
+void keypad_tick(void);
+void keypad_reset(void);
+
+void keypad_register_scanner(keypad_scanner_obj_t *scanner);
+void keypad_deregister_scanner(keypad_scanner_obj_t *scanner);
+void keypad_construct_common(keypad_scanner_obj_t *scanner, mp_float_t interval, size_t max_events);
+
+size_t common_hal_keypad_generic_get_key_count(void *scanner);
+void common_hal_keypad_deinit_core(void *scanner);
+
+#endif // SHARED_MODULE_KEYPAD_H
diff --git a/circuitpython/shared-module/memorymonitor/AllocationAlarm.c b/circuitpython/shared-module/memorymonitor/AllocationAlarm.c
new file mode 100644
index 0000000..473a193
--- /dev/null
+++ b/circuitpython/shared-module/memorymonitor/AllocationAlarm.c
@@ -0,0 +1,94 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Scott Shawcroft 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.
+ */
+
+#include "shared-bindings/memorymonitor/__init__.h"
+#include "shared-bindings/memorymonitor/AllocationAlarm.h"
+
+#include "py/gc.h"
+#include "py/mpstate.h"
+#include "py/runtime.h"
+
+void common_hal_memorymonitor_allocationalarm_construct(memorymonitor_allocationalarm_obj_t *self, size_t minimum_block_count) {
+ self->minimum_block_count = minimum_block_count;
+ self->next = NULL;
+ self->previous = NULL;
+}
+
+void common_hal_memorymonitor_allocationalarm_set_ignore(memorymonitor_allocationalarm_obj_t *self, mp_int_t count) {
+ self->count = count;
+}
+
+void common_hal_memorymonitor_allocationalarm_pause(memorymonitor_allocationalarm_obj_t *self) {
+ // Check to make sure we aren't already paused. We can be if we're exiting from an exception we
+ // caused.
+ if (self->previous == NULL) {
+ return;
+ }
+ *self->previous = self->next;
+ self->next = NULL;
+ self->previous = NULL;
+}
+
+void common_hal_memorymonitor_allocationalarm_resume(memorymonitor_allocationalarm_obj_t *self) {
+ if (self->previous != NULL) {
+ mp_raise_RuntimeError(translate("Already running"));
+ }
+ self->next = MP_STATE_VM(active_allocationalarms);
+ self->previous = (memorymonitor_allocationalarm_obj_t **)&MP_STATE_VM(active_allocationalarms);
+ if (self->next != NULL) {
+ self->next->previous = &self->next;
+ }
+ MP_STATE_VM(active_allocationalarms) = self;
+}
+
+void memorymonitor_allocationalarms_allocation(size_t block_count) {
+ memorymonitor_allocationalarm_obj_t *alarm = MP_OBJ_TO_PTR(MP_STATE_VM(active_allocationalarms));
+ size_t alert_count = 0;
+ while (alarm != NULL) {
+ // Hold onto next in case we remove the alarm from the list.
+ memorymonitor_allocationalarm_obj_t *next = alarm->next;
+ if (block_count >= alarm->minimum_block_count) {
+ if (alarm->count > 0) {
+ alarm->count--;
+ } else {
+ // Uncomment the breakpoint below if you want to use a C debugger to figure out the C
+ // call stack for an allocation.
+ // asm("bkpt");
+ // Pause now because we may alert when throwing the exception too.
+ common_hal_memorymonitor_allocationalarm_pause(alarm);
+ alert_count++;
+ }
+ }
+ alarm = next;
+ }
+ if (alert_count > 0) {
+ mp_raise_memorymonitor_AllocationError(translate("Attempt to allocate %d blocks"), block_count);
+ }
+}
+
+void memorymonitor_allocationalarms_reset(void) {
+ MP_STATE_VM(active_allocationalarms) = NULL;
+}
diff --git a/circuitpython/shared-module/memorymonitor/AllocationAlarm.h b/circuitpython/shared-module/memorymonitor/AllocationAlarm.h
new file mode 100644
index 0000000..1f6e3d0
--- /dev/null
+++ b/circuitpython/shared-module/memorymonitor/AllocationAlarm.h
@@ -0,0 +1,51 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Scott Shawcroft 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.
+ */
+
+#ifndef MICROPY_INCLUDED_SHARED_MODULE_MEMORYMONITOR_ALLOCATIONALARM_H
+#define MICROPY_INCLUDED_SHARED_MODULE_MEMORYMONITOR_ALLOCATIONALARM_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "py/obj.h"
+
+typedef struct _memorymonitor_allocationalarm_obj_t memorymonitor_allocationalarm_obj_t;
+
+#define ALLOCATION_SIZE_BUCKETS 16
+
+typedef struct _memorymonitor_allocationalarm_obj_t {
+ mp_obj_base_t base;
+ size_t minimum_block_count;
+ mp_int_t count;
+ // Store the location that points to us so we can remove ourselves.
+ memorymonitor_allocationalarm_obj_t **previous;
+ memorymonitor_allocationalarm_obj_t *next;
+} memorymonitor_allocationalarm_obj_t;
+
+void memorymonitor_allocationalarms_allocation(size_t block_count);
+void memorymonitor_allocationalarms_reset(void);
+
+#endif // MICROPY_INCLUDED_SHARED_MODULE_MEMORYMONITOR_ALLOCATIONALARM_H
diff --git a/circuitpython/shared-module/memorymonitor/AllocationSize.c b/circuitpython/shared-module/memorymonitor/AllocationSize.c
new file mode 100644
index 0000000..42fae3b
--- /dev/null
+++ b/circuitpython/shared-module/memorymonitor/AllocationSize.c
@@ -0,0 +1,91 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Scott Shawcroft 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.
+ */
+
+#include "shared-bindings/memorymonitor/AllocationSize.h"
+
+#include "py/gc.h"
+#include "py/mpstate.h"
+#include "py/runtime.h"
+
+void common_hal_memorymonitor_allocationsize_construct(memorymonitor_allocationsize_obj_t *self) {
+ common_hal_memorymonitor_allocationsize_clear(self);
+ self->next = NULL;
+ self->previous = NULL;
+}
+
+void common_hal_memorymonitor_allocationsize_pause(memorymonitor_allocationsize_obj_t *self) {
+ *self->previous = self->next;
+ self->next = NULL;
+ self->previous = NULL;
+}
+
+void common_hal_memorymonitor_allocationsize_resume(memorymonitor_allocationsize_obj_t *self) {
+ if (self->previous != NULL) {
+ mp_raise_RuntimeError(translate("Already running"));
+ }
+ self->next = MP_STATE_VM(active_allocationsizes);
+ self->previous = (memorymonitor_allocationsize_obj_t **)&MP_STATE_VM(active_allocationsizes);
+ if (self->next != NULL) {
+ self->next->previous = &self->next;
+ }
+ MP_STATE_VM(active_allocationsizes) = self;
+}
+
+void common_hal_memorymonitor_allocationsize_clear(memorymonitor_allocationsize_obj_t *self) {
+ for (size_t i = 0; i < ALLOCATION_SIZE_BUCKETS; i++) {
+ self->buckets[i] = 0;
+ }
+}
+
+uint16_t common_hal_memorymonitor_allocationsize_get_len(memorymonitor_allocationsize_obj_t *self) {
+ return ALLOCATION_SIZE_BUCKETS;
+}
+
+size_t common_hal_memorymonitor_allocationsize_get_bytes_per_block(memorymonitor_allocationsize_obj_t *self) {
+ return BYTES_PER_BLOCK;
+}
+
+uint16_t common_hal_memorymonitor_allocationsize_get_item(memorymonitor_allocationsize_obj_t *self, int16_t index) {
+ return self->buckets[index];
+}
+
+void memorymonitor_allocationsizes_track_allocation(size_t block_count) {
+ memorymonitor_allocationsize_obj_t *as = MP_OBJ_TO_PTR(MP_STATE_VM(active_allocationsizes));
+ size_t power_of_two = 0;
+ block_count >>= 1;
+ while (block_count != 0) {
+ power_of_two++;
+ block_count >>= 1;
+ }
+ while (as != NULL) {
+ as->buckets[power_of_two]++;
+ as = as->next;
+ }
+}
+
+void memorymonitor_allocationsizes_reset(void) {
+ MP_STATE_VM(active_allocationsizes) = NULL;
+}
diff --git a/circuitpython/shared-module/memorymonitor/AllocationSize.h b/circuitpython/shared-module/memorymonitor/AllocationSize.h
new file mode 100644
index 0000000..3af1a1a
--- /dev/null
+++ b/circuitpython/shared-module/memorymonitor/AllocationSize.h
@@ -0,0 +1,51 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Scott Shawcroft 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.
+ */
+
+#ifndef MICROPY_INCLUDED_SHARED_MODULE_MEMORYMONITOR_ALLOCATIONSIZE_H
+#define MICROPY_INCLUDED_SHARED_MODULE_MEMORYMONITOR_ALLOCATIONSIZE_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "py/obj.h"
+
+typedef struct _memorymonitor_allocationsize_obj_t memorymonitor_allocationsize_obj_t;
+
+#define ALLOCATION_SIZE_BUCKETS 16
+
+typedef struct _memorymonitor_allocationsize_obj_t {
+ mp_obj_base_t base;
+ uint16_t buckets[ALLOCATION_SIZE_BUCKETS];
+ // Store the location that points to us so we can remove ourselves.
+ memorymonitor_allocationsize_obj_t **previous;
+ memorymonitor_allocationsize_obj_t *next;
+ bool paused;
+} memorymonitor_allocationsize_obj_t;
+
+void memorymonitor_allocationsizes_track_allocation(size_t block_count);
+void memorymonitor_allocationsizes_reset(void);
+
+#endif // MICROPY_INCLUDED_SHARED_MODULE_MEMORYMONITOR_ALLOCATIONSIZE_H
diff --git a/circuitpython/shared-module/memorymonitor/__init__.c b/circuitpython/shared-module/memorymonitor/__init__.c
new file mode 100644
index 0000000..6cb4241
--- /dev/null
+++ b/circuitpython/shared-module/memorymonitor/__init__.c
@@ -0,0 +1,39 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Scott Shawcroft
+ *
+ * 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.
+ */
+
+#include "shared-module/memorymonitor/__init__.h"
+#include "shared-module/memorymonitor/AllocationAlarm.h"
+#include "shared-module/memorymonitor/AllocationSize.h"
+
+void memorymonitor_track_allocation(size_t block_count) {
+ memorymonitor_allocationalarms_allocation(block_count);
+ memorymonitor_allocationsizes_track_allocation(block_count);
+}
+
+void memorymonitor_reset(void) {
+ memorymonitor_allocationalarms_reset();
+ memorymonitor_allocationsizes_reset();
+}
diff --git a/circuitpython/shared-module/memorymonitor/__init__.h b/circuitpython/shared-module/memorymonitor/__init__.h
new file mode 100644
index 0000000..f47f643
--- /dev/null
+++ b/circuitpython/shared-module/memorymonitor/__init__.h
@@ -0,0 +1,35 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Scott Shawcroft 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.
+ */
+
+#ifndef MICROPY_INCLUDED_MEMORYMONITOR___INIT___H
+#define MICROPY_INCLUDED_MEMORYMONITOR___INIT___H
+
+#include <stddef.h>
+
+void memorymonitor_track_allocation(size_t block_count);
+void memorymonitor_reset(void);
+
+#endif // MICROPY_INCLUDED_MEMORYMONITOR___INIT___H
diff --git a/circuitpython/shared-module/msgpack/__init__.c b/circuitpython/shared-module/msgpack/__init__.c
new file mode 100644
index 0000000..e1b98a9
--- /dev/null
+++ b/circuitpython/shared-module/msgpack/__init__.c
@@ -0,0 +1,502 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Bernhard Boser
+ *
+ * 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.
+ */
+
+#include <stdio.h>
+#include <inttypes.h>
+
+#include "py/obj.h"
+#include "py/binary.h"
+#include "py/objarray.h"
+#include "py/objlist.h"
+#include "py/objstringio.h"
+#include "py/parsenum.h"
+#include "py/runtime.h"
+#include "py/stream.h"
+
+#include "supervisor/shared/translate.h"
+#include "shared-bindings/msgpack/ExtType.h"
+#include "shared-bindings/msgpack/__init__.h"
+#include "shared-module/msgpack/__init__.h"
+
+////////////////////////////////////////////////////////////////
+// stream management
+
+typedef struct _msgpack_stream_t {
+ mp_obj_t stream_obj;
+ mp_uint_t (*read)(mp_obj_t obj, void *buf, mp_uint_t size, int *errcode);
+ mp_uint_t (*write)(mp_obj_t obj, const void *buf, mp_uint_t size, int *errcode);
+ int errcode;
+} msgpack_stream_t;
+
+STATIC msgpack_stream_t get_stream(mp_obj_t stream_obj, int flags) {
+ const mp_stream_p_t *stream_p = mp_get_stream_raise(stream_obj, flags);
+ msgpack_stream_t s = {stream_obj, stream_p->read, stream_p->write, 0};
+ return s;
+}
+
+////////////////////////////////////////////////////////////////
+// readers
+
+STATIC void read(msgpack_stream_t *s, void *buf, mp_uint_t size) {
+ if (size == 0) {
+ return;
+ }
+ mp_uint_t ret = s->read(s->stream_obj, buf, size, &s->errcode);
+ if (s->errcode != 0) {
+ mp_raise_OSError(s->errcode);
+ }
+ if (ret == 0) {
+ mp_raise_msg(&mp_type_EOFError, NULL);
+ }
+ if (ret < size) {
+ mp_raise_ValueError(translate("short read"));
+ }
+}
+
+STATIC uint8_t read1(msgpack_stream_t *s) {
+ uint8_t res = 0;
+ read(s, &res, 1);
+ return res;
+}
+
+STATIC uint16_t read2(msgpack_stream_t *s) {
+ uint16_t res = 0;
+ read(s, &res, 2);
+ int n = 1;
+ if (*(char *)&n == 1) {
+ res = __builtin_bswap16(res);
+ }
+ return res;
+}
+
+STATIC uint32_t read4(msgpack_stream_t *s) {
+ uint32_t res = 0;
+ read(s, &res, 4);
+ int n = 1;
+ if (*(char *)&n == 1) {
+ res = __builtin_bswap32(res);
+ }
+ return res;
+}
+
+STATIC size_t read_size(msgpack_stream_t *s, uint8_t len_index) {
+ size_t res = 0;
+ switch (len_index) {
+ case 0:
+ res = (size_t)read1(s);
+ break;
+ case 1:
+ res = (size_t)read2(s);
+ break;
+ case 2:
+ res = (size_t)read4(s);
+ break;
+ }
+ return res;
+}
+
+////////////////////////////////////////////////////////////////
+// writers
+
+STATIC void write(msgpack_stream_t *s, const void *buf, mp_uint_t size) {
+ mp_uint_t ret = s->write(s->stream_obj, buf, size, &s->errcode);
+ if (s->errcode != 0) {
+ mp_raise_OSError(s->errcode);
+ }
+ if (ret == 0) {
+ mp_raise_msg(&mp_type_EOFError, NULL);
+ }
+}
+
+STATIC void write1(msgpack_stream_t *s, uint8_t obj) {
+ write(s, &obj, 1);
+}
+
+STATIC void write2(msgpack_stream_t *s, uint16_t obj) {
+ int n = 1;
+ if (*(char *)&n == 1) {
+ obj = __builtin_bswap16(obj);
+ }
+ write(s, &obj, 2);
+}
+
+STATIC void write4(msgpack_stream_t *s, uint32_t obj) {
+ int n = 1;
+ if (*(char *)&n == 1) {
+ obj = __builtin_bswap32(obj);
+ }
+ write(s, &obj, 4);
+}
+
+// compute and write msgpack size code (array structures)
+STATIC void write_size(msgpack_stream_t *s, uint8_t code, size_t size) {
+ if ((uint8_t)size == size) {
+ write1(s, code);
+ write1(s, size);
+ } else if ((uint16_t)size == size) {
+ write1(s, code + 1);
+ write2(s, size);
+ } else {
+ write1(s, code + 2);
+ write4(s, size);
+ }
+}
+
+////////////////////////////////////////////////////////////////
+// packers
+
+// This is a helper function to iterate through a dictionary. The state of
+// the iteration is held in *cur and should be initialised with zero for the
+// first call. Will return NULL when no more elements are available.
+STATIC mp_map_elem_t *dict_iter_next(mp_obj_dict_t *dict, size_t *cur) {
+ size_t max = dict->map.alloc;
+ mp_map_t *map = &dict->map;
+
+ for (size_t i = *cur; i < max; i++) {
+ if (mp_map_slot_is_filled(map, i)) {
+ *cur = i + 1;
+ return &(map->table[i]);
+ }
+ }
+
+ return NULL;
+}
+
+STATIC void pack_int(msgpack_stream_t *s, int32_t x) {
+ if (x > -32 && x < 128) {
+ write1(s, x);
+ } else if ((int8_t)x == x) {
+ write1(s, 0xd0);
+ write1(s, x);
+ } else if ((int16_t)x == x) {
+ write1(s, 0xd1);
+ write2(s, x);
+ } else {
+ write1(s, 0xd2);
+ write4(s, x);
+ }
+}
+
+STATIC void pack_bin(msgpack_stream_t *s, const uint8_t *data, size_t len) {
+ write_size(s, 0xc4, len);
+ if (len > 0) {
+ write(s, data, len);
+ }
+}
+
+STATIC void pack_ext(msgpack_stream_t *s, int8_t code, const uint8_t *data, size_t len) {
+ if (len == 1) {
+ write1(s, 0xd4);
+ } else if (len == 2) {
+ write1(s, 0xd5);
+ } else if (len == 4) {
+ write1(s, 0xd6);
+ } else if (len == 8) {
+ write1(s, 0xd7);
+ } else if (len == 16) {
+ write1(s, 0xd8);
+ } else {
+ write_size(s, 0xc7, len);
+ }
+ write1(s, code); // type byte
+ if (len > 0) {
+ write(s, data, len);
+ }
+}
+
+STATIC void pack_str(msgpack_stream_t *s, const char *str, size_t len) {
+ if (len < 32) {
+ write1(s, 0b10100000 | (uint8_t)len);
+ } else {
+ write_size(s, 0xd9, len);
+ }
+ if (len > 0) {
+ write(s, str, len);
+ }
+}
+
+STATIC void pack_array(msgpack_stream_t *s, size_t len) {
+ // only writes the header, manually write the objects after calling pack_array!
+ if (len < 16) {
+ write1(s, 0b10010000 | (uint8_t)len);
+ } else if (len < 0x10000) {
+ write1(s, 0xdc);
+ write2(s, len);
+ } else {
+ write1(s, 0xdd);
+ write4(s, len);
+ }
+}
+
+STATIC void pack_dict(msgpack_stream_t *s, size_t len) {
+ // only writes the header, manually write the objects after calling pack_array!
+ if (len < 16) {
+ write1(s, 0b10000000 | (uint8_t)len);
+ } else if (len < 0x10000) {
+ write1(s, 0xde);
+ write2(s, len);
+ } else {
+ write1(s, 0xdf);
+ write4(s, len);
+ }
+}
+
+STATIC void pack(mp_obj_t obj, msgpack_stream_t *s, mp_obj_t default_handler) {
+ if (mp_obj_is_small_int(obj)) {
+ // int
+ int32_t x = MP_OBJ_SMALL_INT_VALUE(obj);
+ pack_int(s, x);
+ } else if (mp_obj_is_str(obj)) {
+ // string
+ size_t len;
+ const char *data = mp_obj_str_get_data(obj, &len);
+ pack_str(s, data, len);
+ } else if (mp_obj_is_type(obj, &mod_msgpack_exttype_type)) {
+ mod_msgpack_extype_obj_t *ext = MP_OBJ_TO_PTR(obj);
+ mp_buffer_info_t bufinfo;
+ mp_get_buffer_raise(ext->data, &bufinfo, MP_BUFFER_READ);
+ pack_ext(s, ext->code, bufinfo.buf, bufinfo.len);
+ } else if (mp_obj_is_type(obj, &mp_type_tuple)) {
+ // tuple
+ mp_obj_tuple_t *self = MP_OBJ_TO_PTR(obj);
+ pack_array(s, self->len);
+ for (size_t i = 0; i < self->len; i++) {
+ pack(self->items[i], s, default_handler);
+ }
+ } else if (mp_obj_is_type(obj, &mp_type_list)) {
+ // list (layout differs from tuple)
+ mp_obj_list_t *self = MP_OBJ_TO_PTR(obj);
+ pack_array(s, self->len);
+ for (size_t i = 0; i < self->len; i++) {
+ pack(self->items[i], s, default_handler);
+ }
+ } else if (mp_obj_is_type(obj, &mp_type_dict)) {
+ // dict
+ mp_obj_dict_t *self = MP_OBJ_TO_PTR(obj);
+ pack_dict(s, self->map.used);
+ size_t cur = 0;
+ mp_map_elem_t *next = NULL;
+ while ((next = dict_iter_next(self, &cur)) != NULL) {
+ pack(next->key, s, default_handler);
+ pack(next->value, s, default_handler);
+ }
+ } else if (mp_obj_is_float(obj)) {
+ union Float { mp_float_t f;
+ uint32_t u;
+ };
+ union Float data;
+ data.f = mp_obj_float_get(obj);
+ write1(s, 0xca);
+ write4(s, data.u);
+ } else if (obj == mp_const_none) {
+ write1(s, 0xc0);
+ } else if (obj == mp_const_false) {
+ write1(s, 0xc2);
+ } else if (obj == mp_const_true) {
+ write1(s, 0xc3);
+ } else {
+ mp_buffer_info_t bufinfo;
+ if (mp_get_buffer(obj, &bufinfo, MP_BUFFER_READ)) {
+ // bytes (bin type)
+ pack_bin(s, bufinfo.buf, bufinfo.len);
+ } else if (default_handler != mp_const_none) {
+ // set default_handler to mp_const_none to avoid infinite recursion
+ // this also precludes some valid outputs
+ pack(mp_call_function_1(default_handler, obj), s, mp_const_none);
+ } else {
+ mp_raise_ValueError(translate("no default packer"));
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////////
+// unpacker
+
+STATIC mp_obj_t unpack(msgpack_stream_t *s, mp_obj_t ext_hook, bool use_list);
+
+STATIC mp_obj_t unpack_array_elements(msgpack_stream_t *s, size_t size, mp_obj_t ext_hook, bool use_list) {
+ if (use_list) {
+ mp_obj_list_t *t = MP_OBJ_TO_PTR(mp_obj_new_list(size, NULL));
+ for (size_t i = 0; i < size; i++) {
+ t->items[i] = unpack(s, ext_hook, use_list);
+ }
+ return MP_OBJ_FROM_PTR(t);
+ } else {
+ mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(size, NULL));
+ for (size_t i = 0; i < size; i++) {
+ t->items[i] = unpack(s, ext_hook, use_list);
+ }
+ return MP_OBJ_FROM_PTR(t);
+ }
+}
+
+STATIC mp_obj_t unpack_bytes(msgpack_stream_t *s, size_t size) {
+ vstr_t vstr;
+ vstr_init_len(&vstr, size);
+ byte *p = (byte *)vstr.buf;
+ // read in chunks: (some drivers - e.g. UART) limit the
+ // maximum number of bytes that can be read at once
+ // read(s, p, size);
+ while (size > 0) {
+ int n = size > 256 ? 256 : size;
+ read(s, p, n);
+ size -= n;
+ p += n;
+ }
+ return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr);
+}
+
+STATIC mp_obj_t unpack_ext(msgpack_stream_t *s, size_t size, mp_obj_t ext_hook) {
+ int8_t code = read1(s);
+ mp_obj_t data = unpack_bytes(s, size);
+ if (ext_hook != mp_const_none) {
+ return mp_call_function_2(ext_hook, MP_OBJ_NEW_SMALL_INT(code), data);
+ } else {
+ mod_msgpack_extype_obj_t *o = m_new_obj(mod_msgpack_extype_obj_t);
+ o->base.type = &mod_msgpack_exttype_type;
+ o->code = code;
+ o->data = data;
+ return MP_OBJ_FROM_PTR(o);
+ }
+}
+
+STATIC mp_obj_t unpack(msgpack_stream_t *s, mp_obj_t ext_hook, bool use_list) {
+ uint8_t code = read1(s);
+ if (((code & 0b10000000) == 0) || ((code & 0b11100000) == 0b11100000)) {
+ // int
+ return MP_OBJ_NEW_SMALL_INT((int8_t)code);
+ }
+ if ((code & 0b11100000) == 0b10100000) {
+ // str
+ size_t len = code & 0b11111;
+ // allocate on stack; len < 32
+ char str[len];
+ read(s, &str, len);
+ return mp_obj_new_str(str, len);
+ }
+ if ((code & 0b11110000) == 0b10010000) {
+ // array (list / tuple)
+ return unpack_array_elements(s, code & 0b1111, ext_hook, use_list);
+ }
+ if ((code & 0b11110000) == 0b10000000) {
+ // map (dict)
+ size_t len = code & 0b1111;
+ mp_obj_dict_t *d = MP_OBJ_TO_PTR(mp_obj_new_dict(len));
+ for (size_t i = 0; i < len; i++) {
+ mp_obj_dict_store(d, unpack(s, ext_hook, use_list), unpack(s, ext_hook, use_list));
+ }
+ return MP_OBJ_FROM_PTR(d);
+ }
+ switch (code) {
+ case 0xc0:
+ return mp_const_none;
+ case 0xc2:
+ return mp_const_false;
+ case 0xc3:
+ return mp_const_true;
+ case 0xc4:
+ case 0xc5:
+ case 0xc6: {
+ // bin 8, 16, 32
+ return unpack_bytes(s, read_size(s, code - 0xc4));
+ }
+ case 0xcc: // uint8
+ case 0xd0: // int8
+ return MP_OBJ_NEW_SMALL_INT((int8_t)read1(s));
+ case 0xcd: // uint16
+ case 0xd1: // int16
+ return MP_OBJ_NEW_SMALL_INT((int16_t)read2(s));
+ case 0xce: // uint32
+ case 0xd2: // int32
+ return MP_OBJ_NEW_SMALL_INT((int32_t)read4(s));
+ case 0xca: {
+ union Float { mp_float_t f;
+ uint32_t u;
+ };
+ union Float data;
+ data.u = read4(s);
+ return mp_obj_new_float(data.f);
+ }
+ case 0xd9:
+ case 0xda:
+ case 0xdb: {
+ // str 8, 16, 32
+ size_t size = read_size(s, code - 0xd9);
+ vstr_t vstr;
+ vstr_init_len(&vstr, size);
+ byte *p = (byte *)vstr.buf;
+ read(s, p, size);
+ return mp_obj_new_str_from_vstr(&mp_type_str, &vstr);
+ }
+ case 0xde:
+ case 0xdf: {
+ // map 16 & 32
+ size_t len = read_size(s, code - 0xde + 1);
+ mp_obj_dict_t *d = MP_OBJ_TO_PTR(mp_obj_new_dict(len));
+ for (size_t i = 0; i < len; i++) {
+ mp_obj_dict_store(d, unpack(s, ext_hook, use_list), unpack(s, ext_hook, use_list));
+ }
+ return MP_OBJ_FROM_PTR(d);
+ }
+ case 0xdc:
+ case 0xdd: {
+ // array 16 & 32
+ size_t size = read_size(s, code - 0xdc + 1);
+ return unpack_array_elements(s, size, ext_hook, use_list);
+ }
+ case 0xd4: // fixenxt 1
+ return unpack_ext(s, 1, ext_hook);
+ case 0xd5: // fixenxt 2
+ return unpack_ext(s, 2, ext_hook);
+ case 0xd6: // fixenxt 4
+ return unpack_ext(s, 4, ext_hook);
+ case 0xd7: // fixenxt 8
+ return unpack_ext(s, 8, ext_hook);
+ case 0xd8: // fixenxt 16
+ return unpack_ext(s, 16, ext_hook);
+ case 0xc7: // ext 8
+ case 0xc8: // ext 16
+ case 0xc9:
+ // ext 8, 16, 32
+ return unpack_ext(s, read_size(s, code - 0xc7), ext_hook);
+ case 0xc1: // never used
+ case 0xcb: // float 64
+ case 0xcf: // uint 64
+ case 0xd3: // int 64
+ default:
+ mp_raise_NotImplementedError(translate("64 bit types"));
+ }
+}
+
+void common_hal_msgpack_pack(mp_obj_t obj, mp_obj_t stream_obj, mp_obj_t default_handler) {
+ msgpack_stream_t stream = get_stream(stream_obj, MP_STREAM_OP_WRITE);
+ pack(obj, &stream, default_handler);
+}
+
+mp_obj_t common_hal_msgpack_unpack(mp_obj_t stream_obj, mp_obj_t ext_hook, bool use_list) {
+ msgpack_stream_t stream = get_stream(stream_obj, MP_STREAM_OP_READ);
+ return unpack(&stream, ext_hook, use_list);
+}
diff --git a/circuitpython/shared-module/msgpack/__init__.h b/circuitpython/shared-module/msgpack/__init__.h
new file mode 100644
index 0000000..88b4809
--- /dev/null
+++ b/circuitpython/shared-module/msgpack/__init__.h
@@ -0,0 +1,34 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2017 Scott Shawcroft 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.
+ */
+#ifndef MICROPY_INCLUDED_SHARED_MODULE_MSGPACK___INIT___H
+#define MICROPY_INCLUDED_SHARED_MODULE_MSGPACK___INIT___H
+
+#include "py/stream.h"
+
+void common_hal_msgpack_pack(mp_obj_t obj, mp_obj_t stream_obj, mp_obj_t default_handler);
+mp_obj_t common_hal_msgpack_unpack(mp_obj_t stream_obj, mp_obj_t ext_hook, bool use_list);
+
+#endif
diff --git a/circuitpython/shared-module/multiterminal/__init__.c b/circuitpython/shared-module/multiterminal/__init__.c
new file mode 100644
index 0000000..361a6dc
--- /dev/null
+++ b/circuitpython/shared-module/multiterminal/__init__.c
@@ -0,0 +1,50 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Paul Sokolovsky
+ * Copyright (c) 2017 Scott Shawcroft 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.
+ */
+
+#include "py/mpstate.h"
+
+#include "shared-bindings/multiterminal/__init__.h"
+
+mp_obj_t shared_module_multiterminal_get_secondary_terminal() {
+ if (MP_STATE_VM(dupterm_objs[0]) == MP_OBJ_NULL) {
+ return mp_const_none;
+ } else {
+ return MP_STATE_VM(dupterm_objs[0]);
+ }
+}
+
+void shared_module_multiterminal_set_secondary_terminal(mp_obj_t secondary_terminal) {
+ MP_STATE_VM(dupterm_objs[0]) = secondary_terminal;
+ if (MP_STATE_PORT(dupterm_arr_obj) == MP_OBJ_NULL) {
+ MP_STATE_PORT(dupterm_arr_obj) = mp_obj_new_bytearray(1, "");
+ }
+}
+
+void shared_module_multiterminal_clear_secondary_terminal() {
+ MP_STATE_VM(dupterm_objs[0]) = MP_OBJ_NULL;
+ MP_STATE_PORT(dupterm_arr_obj) = MP_OBJ_NULL;
+}
diff --git a/circuitpython/shared-module/multiterminal/__init__.h b/circuitpython/shared-module/multiterminal/__init__.h
new file mode 100644
index 0000000..30044da
--- /dev/null
+++ b/circuitpython/shared-module/multiterminal/__init__.h
@@ -0,0 +1,34 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2017 Scott Shawcroft 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.
+ */
+
+#ifndef SHARED_MODULE_MULTITERMINAL___INIT___H
+#define SHARED_MODULE_MULTITERMINAL___INIT___H
+
+mp_obj_t shared_module_multiterminal_get_secondary_terminal();
+void shared_module_multiterminal_set_secondary_terminal(mp_obj_t secondary_terminal);
+void shared_module_multiterminal_clear_secondary_terminal();
+
+#endif // SHARED_MODULE_MULTITERMINAL___INIT___H
diff --git a/circuitpython/shared-module/onewireio/OneWire.c b/circuitpython/shared-module/onewireio/OneWire.c
new file mode 100644
index 0000000..aeb4dcb
--- /dev/null
+++ b/circuitpython/shared-module/onewireio/OneWire.c
@@ -0,0 +1,89 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2017 Scott Shawcroft 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.
+ */
+
+#include "common-hal/microcontroller/Pin.h"
+#include "shared-bindings/onewireio/OneWire.h"
+#include "shared-bindings/microcontroller/__init__.h"
+#include "shared-bindings/digitalio/DigitalInOut.h"
+
+// Durations are taken from here: https://www.maximintegrated.com/en/app-notes/index.mvp/id/126
+
+void common_hal_onewireio_onewire_construct(onewireio_onewire_obj_t *self,
+ const mcu_pin_obj_t *pin) {
+ self->pin.base.type = &digitalio_digitalinout_type;
+ common_hal_digitalio_digitalinout_construct(&self->pin, pin);
+}
+
+bool common_hal_onewireio_onewire_deinited(onewireio_onewire_obj_t *self) {
+ return common_hal_digitalio_digitalinout_deinited(&self->pin);
+}
+
+void common_hal_onewireio_onewire_deinit(onewireio_onewire_obj_t *self) {
+ if (common_hal_onewireio_onewire_deinited(self)) {
+ return;
+ }
+ common_hal_digitalio_digitalinout_deinit(&self->pin);
+}
+
+// We use common_hal_mcu_delay_us(). It should not be dependent on interrupts
+// to do accurate timekeeping, since we disable interrupts during the delays below.
+
+bool common_hal_onewireio_onewire_reset(onewireio_onewire_obj_t *self) {
+ common_hal_mcu_disable_interrupts();
+ common_hal_digitalio_digitalinout_switch_to_output(&self->pin, false, DRIVE_MODE_OPEN_DRAIN);
+ common_hal_mcu_delay_us(480);
+ common_hal_digitalio_digitalinout_switch_to_input(&self->pin, PULL_NONE);
+ common_hal_mcu_delay_us(70);
+ bool value = common_hal_digitalio_digitalinout_get_value(&self->pin);
+ common_hal_mcu_delay_us(410);
+ common_hal_mcu_enable_interrupts();
+ return value;
+}
+
+bool common_hal_onewireio_onewire_read_bit(onewireio_onewire_obj_t *self) {
+ common_hal_mcu_disable_interrupts();
+ common_hal_digitalio_digitalinout_switch_to_output(&self->pin, false, DRIVE_MODE_OPEN_DRAIN);
+ common_hal_mcu_delay_us(6);
+ common_hal_digitalio_digitalinout_switch_to_input(&self->pin, PULL_NONE);
+ // TODO(tannewt): Test with more devices and maybe make the delays
+ // configurable. This should be 9 by the datasheet but all bits read as 1
+ // then.
+ common_hal_mcu_delay_us(6);
+ bool value = common_hal_digitalio_digitalinout_get_value(&self->pin);
+ common_hal_mcu_delay_us(55);
+ common_hal_mcu_enable_interrupts();
+ return value;
+}
+
+void common_hal_onewireio_onewire_write_bit(onewireio_onewire_obj_t *self,
+ bool bit) {
+ common_hal_mcu_disable_interrupts();
+ common_hal_digitalio_digitalinout_switch_to_output(&self->pin, false, DRIVE_MODE_OPEN_DRAIN);
+ common_hal_mcu_delay_us(bit? 6 : 60);
+ common_hal_digitalio_digitalinout_switch_to_input(&self->pin, PULL_NONE);
+ common_hal_mcu_delay_us(bit? 64 : 10);
+ common_hal_mcu_enable_interrupts();
+}
diff --git a/circuitpython/shared-module/onewireio/OneWire.h b/circuitpython/shared-module/onewireio/OneWire.h
new file mode 100644
index 0000000..594478f
--- /dev/null
+++ b/circuitpython/shared-module/onewireio/OneWire.h
@@ -0,0 +1,39 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Scott Shawcroft
+ *
+ * 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.
+ */
+
+#ifndef MICROPY_INCLUDED_SHARED_MODULE_ONEWIREIO_ONEWIRE_H
+#define MICROPY_INCLUDED_SHARED_MODULE_ONEWIREIO_ONEWIRE_H
+
+#include "common-hal/digitalio/DigitalInOut.h"
+
+#include "py/obj.h"
+
+typedef struct {
+ mp_obj_base_t base;
+ digitalio_digitalinout_obj_t pin;
+} onewireio_onewire_obj_t;
+
+#endif // MICROPY_INCLUDED_SHARED_MODULE_ONEWIREIO_ONEWIRE_H
diff --git a/circuitpython/shared-module/onewireio/__init__.c b/circuitpython/shared-module/onewireio/__init__.c
new file mode 100644
index 0000000..674343c
--- /dev/null
+++ b/circuitpython/shared-module/onewireio/__init__.c
@@ -0,0 +1,27 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Scott Shawcroft
+ *
+ * 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.
+ */
+
+// Nothing now.
diff --git a/circuitpython/shared-module/os/__init__.c b/circuitpython/shared-module/os/__init__.c
new file mode 100644
index 0000000..89c7952
--- /dev/null
+++ b/circuitpython/shared-module/os/__init__.c
@@ -0,0 +1,214 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * SPDX-FileCopyrightText: Copyright (c) 2013, 2014 Damien P. George
+ * Copyright (c) 2015 Josef Gajdusek
+ * Copyright (c) 2017 Scott Shawcroft 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.
+ */
+
+#include <string.h>
+
+#include "extmod/vfs.h"
+#include "py/mperrno.h"
+#include "py/mpstate.h"
+#include "py/obj.h"
+#include "py/objstr.h"
+#include "py/runtime.h"
+#include "shared-bindings/os/__init__.h"
+
+// This provides all VFS related OS functions so that ports can share the code
+// as needed. It does not provide uname.
+
+// Version of mp_vfs_lookup_path that takes and returns uPy string objects.
+STATIC mp_vfs_mount_t *lookup_path(const char *path, mp_obj_t *path_out) {
+ const char *p_out;
+ *path_out = mp_const_none;
+ mp_vfs_mount_t *vfs = mp_vfs_lookup_path(path, &p_out);
+ if (vfs != MP_VFS_NONE && vfs != MP_VFS_ROOT) {
+ *path_out = mp_obj_new_str_of_type(&mp_type_str,
+ (const byte *)p_out, strlen(p_out));
+ }
+ return vfs;
+}
+
+// Strip off trailing slashes to please underlying libraries
+STATIC mp_vfs_mount_t *lookup_dir_path(const char *path, mp_obj_t *path_out) {
+ const char *p_out;
+ *path_out = mp_const_none;
+ mp_vfs_mount_t *vfs = mp_vfs_lookup_path(path, &p_out);
+ if (vfs != MP_VFS_NONE && vfs != MP_VFS_ROOT) {
+ size_t len = strlen(p_out);
+ while (len > 1 && p_out[len - 1] == '/') {
+ len--;
+ }
+ *path_out = mp_obj_new_str_of_type(&mp_type_str, (const byte *)p_out, len);
+ }
+ return vfs;
+}
+
+STATIC mp_obj_t mp_vfs_proxy_call(mp_vfs_mount_t *vfs, qstr meth_name, size_t n_args, const mp_obj_t *args) {
+ if (vfs == MP_VFS_NONE) {
+ // mount point not found
+ mp_raise_OSError(MP_ENODEV);
+ }
+ if (vfs == MP_VFS_ROOT) {
+ // can't do operation on root dir
+ mp_raise_OSError(MP_EPERM);
+ }
+ mp_obj_t meth[n_args + 2];
+ mp_load_method(vfs->obj, meth_name, meth);
+ if (args != NULL) {
+ memcpy(meth + 2, args, n_args * sizeof(*args));
+ }
+ return mp_call_method_n_kw(n_args, 0, meth);
+}
+
+void common_hal_os_chdir(const char *path) {
+ mp_obj_t path_out;
+ mp_vfs_mount_t *vfs = lookup_dir_path(path, &path_out);
+ MP_STATE_VM(vfs_cur) = vfs;
+ if (vfs == MP_VFS_ROOT) {
+ // If we change to the root dir and a VFS is mounted at the root then
+ // we must change that VFS's current dir to the root dir so that any
+ // subsequent relative paths begin at the root of that VFS.
+ for (vfs = MP_STATE_VM(vfs_mount_table); vfs != NULL; vfs = vfs->next) {
+ if (vfs->len == 1) {
+ mp_obj_t root = mp_obj_new_str("/", 1);
+ mp_vfs_proxy_call(vfs, MP_QSTR_chdir, 1, &root);
+ break;
+ }
+ }
+ } else {
+ mp_vfs_proxy_call(vfs, MP_QSTR_chdir, 1, &path_out);
+ }
+}
+
+mp_obj_t common_hal_os_getcwd(void) {
+ return mp_vfs_getcwd();
+}
+
+mp_obj_t common_hal_os_listdir(const char *path) {
+ mp_obj_t path_out;
+ mp_vfs_mount_t *vfs = lookup_dir_path(path, &path_out);
+
+ mp_vfs_ilistdir_it_t iter;
+ mp_obj_t iter_obj = MP_OBJ_FROM_PTR(&iter);
+
+ if (vfs == MP_VFS_ROOT) {
+ // list the root directory
+ iter.base.type = &mp_type_polymorph_iter;
+ iter.iternext = mp_vfs_ilistdir_it_iternext;
+ iter.cur.vfs = MP_STATE_VM(vfs_mount_table);
+ iter.is_str = true;
+ iter.is_iter = false;
+ } else {
+ iter_obj = mp_vfs_proxy_call(vfs, MP_QSTR_ilistdir, 1, &path_out);
+ }
+
+ mp_obj_t dir_list = mp_obj_new_list(0, NULL);
+ mp_obj_t next;
+ while ((next = mp_iternext(iter_obj)) != MP_OBJ_STOP_ITERATION) {
+ // next[0] is the filename.
+ mp_obj_list_append(dir_list, mp_obj_subscr(next, MP_OBJ_NEW_SMALL_INT(0), MP_OBJ_SENTINEL));
+ RUN_BACKGROUND_TASKS;
+ }
+ return dir_list;
+}
+
+void common_hal_os_mkdir(const char *path) {
+ mp_obj_t path_out;
+ mp_vfs_mount_t *vfs = lookup_dir_path(path, &path_out);
+ if (vfs == MP_VFS_ROOT || (vfs != MP_VFS_NONE && !strcmp(mp_obj_str_get_str(path_out), "/"))) {
+ mp_raise_OSError(MP_EEXIST);
+ }
+ mp_vfs_proxy_call(vfs, MP_QSTR_mkdir, 1, &path_out);
+}
+
+void common_hal_os_remove(const char *path) {
+ mp_obj_t path_out;
+ mp_vfs_mount_t *vfs = lookup_path(path, &path_out);
+ mp_vfs_proxy_call(vfs, MP_QSTR_remove, 1, &path_out);
+}
+
+void common_hal_os_rename(const char *old_path, const char *new_path) {
+ mp_obj_t args[2];
+ mp_vfs_mount_t *old_vfs = lookup_path(old_path, &args[0]);
+ mp_vfs_mount_t *new_vfs = lookup_path(new_path, &args[1]);
+ if (old_vfs != new_vfs) {
+ // can't rename across filesystems
+ mp_raise_OSError(MP_EPERM);
+ }
+ mp_vfs_proxy_call(old_vfs, MP_QSTR_rename, 2, args);
+}
+
+void common_hal_os_rmdir(const char *path) {
+ mp_obj_t path_out;
+ mp_vfs_mount_t *vfs = lookup_dir_path(path, &path_out);
+ mp_vfs_proxy_call(vfs, MP_QSTR_rmdir, 1, &path_out);
+}
+
+mp_obj_t common_hal_os_stat(const char *path) {
+ mp_obj_t path_out;
+ mp_vfs_mount_t *vfs = lookup_path(path, &path_out);
+ if (vfs == MP_VFS_ROOT) {
+ mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(10, NULL));
+ t->items[0] = MP_OBJ_NEW_SMALL_INT(MP_S_IFDIR); // st_mode
+ for (int i = 1; i <= 9; ++i) {
+ t->items[i] = MP_OBJ_NEW_SMALL_INT(0); // dev, nlink, uid, gid, size, atime, mtime, ctime
+ }
+ return MP_OBJ_FROM_PTR(t);
+ }
+ return mp_vfs_proxy_call(vfs, MP_QSTR_stat, 1, &path_out);
+}
+
+mp_obj_t common_hal_os_statvfs(const char *path) {
+ mp_obj_t path_out;
+ mp_vfs_mount_t *vfs = lookup_path(path, &path_out);
+ if (vfs == MP_VFS_ROOT) {
+ // statvfs called on the root directory, see if there's anything mounted there
+ for (vfs = MP_STATE_VM(vfs_mount_table); vfs != NULL; vfs = vfs->next) {
+ if (vfs->len == 1) {
+ break;
+ }
+ }
+
+ // If there's nothing mounted at root then return a mostly-empty tuple
+ if (vfs == NULL) {
+ mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(10, NULL));
+
+ // fill in: bsize, frsize, blocks, bfree, bavail, files, ffree, favail, flags
+ for (int i = 0; i <= 8; ++i) {
+ t->items[i] = MP_OBJ_NEW_SMALL_INT(0);
+ }
+
+ // Put something sensible in f_namemax
+ t->items[9] = MP_OBJ_NEW_SMALL_INT(MICROPY_ALLOC_PATH_MAX);
+
+ return MP_OBJ_FROM_PTR(t);
+ }
+
+ // VFS mounted at root so delegate the call to it
+ path_out = MP_OBJ_NEW_QSTR(MP_QSTR__slash_);
+ }
+ return mp_vfs_proxy_call(vfs, MP_QSTR_statvfs, 1, &path_out);
+}
diff --git a/circuitpython/shared-module/paralleldisplay/ParallelBus.c b/circuitpython/shared-module/paralleldisplay/ParallelBus.c
new file mode 100644
index 0000000..3a94959
--- /dev/null
+++ b/circuitpython/shared-module/paralleldisplay/ParallelBus.c
@@ -0,0 +1,37 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Scott Shawcroft 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.
+ */
+
+#include "shared-bindings/paralleldisplay/ParallelBus.h"
+#include "py/runtime.h"
+
+// If non-sequential pins aren't supported, then this default (weak)
+// implementation will raise an exception for you.
+__attribute__((weak))
+void common_hal_paralleldisplay_parallelbus_construct_nonsequential(paralleldisplay_parallelbus_obj_t *self,
+ uint8_t n_pins, const mcu_pin_obj_t **data_pins, const mcu_pin_obj_t *command, const mcu_pin_obj_t *chip_select,
+ const mcu_pin_obj_t *write, const mcu_pin_obj_t *read, const mcu_pin_obj_t *reset, uint32_t frequency) {
+ mp_raise_NotImplementedError(translate("This microcontroller only supports data0=, not data_pins=, because it requires contiguous pins."));
+}
diff --git a/circuitpython/shared-module/qrio/QRDecoder.c b/circuitpython/shared-module/qrio/QRDecoder.c
new file mode 100644
index 0000000..26a609f
--- /dev/null
+++ b/circuitpython/shared-module/qrio/QRDecoder.c
@@ -0,0 +1,137 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 Jeff Epler 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.
+ */
+
+#include <string.h>
+
+#include "py/gc.h"
+#include "py/objnamedtuple.h"
+#include "shared-bindings/qrio/__init__.h"
+#include "shared-bindings/qrio/QRInfo.h"
+#include "shared-module/qrio/QRDecoder.h"
+
+void shared_module_qrio_qrdecoder_construct(qrdecoder_qrdecoder_obj_t *self, int width, int height) {
+ self->quirc = quirc_new();
+ quirc_resize(self->quirc, width, height);
+}
+
+int shared_module_qrio_qrdecoder_get_height(qrdecoder_qrdecoder_obj_t *self) {
+ int height;
+ quirc_begin(self->quirc, NULL, &height);
+ return height;
+}
+
+int shared_module_qrio_qrdecoder_get_width(qrdecoder_qrdecoder_obj_t *self) {
+ int width;
+ quirc_begin(self->quirc, &width, NULL);
+ return width;
+}
+void shared_module_qrio_qrdecoder_set_height(qrdecoder_qrdecoder_obj_t *self, int height) {
+ if (height != shared_module_qrio_qrdecoder_get_height(self)) {
+ int width = shared_module_qrio_qrdecoder_get_width(self);
+ quirc_resize(self->quirc, width, height);
+ }
+}
+
+void shared_module_qrio_qrdecoder_set_width(qrdecoder_qrdecoder_obj_t *self, int width) {
+ if (width != shared_module_qrio_qrdecoder_get_width(self)) {
+ int height = shared_module_qrio_qrdecoder_get_height(self);
+ quirc_resize(self->quirc, width, height);
+ }
+}
+
+STATIC mp_obj_t data_type(int type) {
+ switch (type) {
+ case QUIRC_ECI_ISO_8859_1:
+ return MP_OBJ_NEW_QSTR(MP_QSTR_iso_8859_hyphen_1);
+ case QUIRC_ECI_IBM437:
+ return MP_OBJ_NEW_QSTR(MP_QSTR_cp437);
+ case QUIRC_ECI_ISO_8859_2:
+ return MP_OBJ_NEW_QSTR(MP_QSTR_iso_8859_hyphen_2);
+ case QUIRC_ECI_ISO_8859_3:
+ return MP_OBJ_NEW_QSTR(MP_QSTR_iso_8859_hyphen_3);
+ case QUIRC_ECI_ISO_8859_4:
+ return MP_OBJ_NEW_QSTR(MP_QSTR_iso_8859_hyphen_4);
+ case QUIRC_ECI_ISO_8859_5:
+ return MP_OBJ_NEW_QSTR(MP_QSTR_iso_8859_hyphen_5);
+ case QUIRC_ECI_ISO_8859_6:
+ return MP_OBJ_NEW_QSTR(MP_QSTR_iso_8859_hyphen_6);
+ case QUIRC_ECI_ISO_8859_7:
+ return MP_OBJ_NEW_QSTR(MP_QSTR_iso_8859_hyphen_7);
+ case QUIRC_ECI_ISO_8859_8:
+ return MP_OBJ_NEW_QSTR(MP_QSTR_iso_8859_hyphen_8);
+ case QUIRC_ECI_ISO_8859_9:
+ return MP_OBJ_NEW_QSTR(MP_QSTR_iso_8859_hyphen_9);
+ case QUIRC_ECI_WINDOWS_874:
+ return MP_OBJ_NEW_QSTR(MP_QSTR_cp874);
+ case QUIRC_ECI_ISO_8859_13:
+ return MP_OBJ_NEW_QSTR(MP_QSTR_iso_8859_hyphen_13);
+ case QUIRC_ECI_ISO_8859_15:
+ return MP_OBJ_NEW_QSTR(MP_QSTR_iso_8859_hyphen_15);
+ case QUIRC_ECI_SHIFT_JIS:
+ return MP_OBJ_NEW_QSTR(MP_QSTR_shift_underscore_jis);
+ case QUIRC_ECI_UTF_8:
+ return MP_OBJ_NEW_QSTR(MP_QSTR_utf_hyphen_8);
+ }
+ return mp_obj_new_int(type);
+}
+
+mp_obj_t shared_module_qrio_qrdecoder_decode(qrdecoder_qrdecoder_obj_t *self, const mp_buffer_info_t *bufinfo, qrio_pixel_policy_t policy) {
+ int width, height;
+ uint8_t *framebuffer = quirc_begin(self->quirc, &width, &height);
+ uint8_t *src = bufinfo->buf;
+
+ switch (policy) {
+ case QRIO_EVERY_BYTE:
+ memcpy(framebuffer, src, width * height);
+ break;
+
+ case QRIO_ODD_BYTES:
+ src++;
+ MP_FALLTHROUGH;
+
+ case QRIO_EVEN_BYTES:
+ for (int i = 0; i < width * height; i++) {
+ framebuffer[i] = src[2 * i];
+ }
+ }
+ quirc_end(self->quirc);
+
+ int count = quirc_count(self->quirc);
+ mp_obj_t result = mp_obj_new_list(0, NULL);
+ for (int i = 0; i < count; i++) {
+ quirc_extract(self->quirc, i, &self->code);
+ if (quirc_decode(&self->code, &self->data) != QUIRC_SUCCESS) {
+ continue;
+ }
+ mp_obj_t elems[2] = {
+ mp_obj_new_bytes(self->data.payload, self->data.payload_len),
+ data_type(self->data.data_type),
+ };
+ mp_obj_t code_obj = namedtuple_make_new((const mp_obj_type_t *)&qrio_qrinfo_type_obj, 2, 0, elems);
+ mp_obj_list_append(result, code_obj);
+ }
+ return result;
+}
diff --git a/circuitpython/shared-module/qrio/QRDecoder.h b/circuitpython/shared-module/qrio/QRDecoder.h
new file mode 100644
index 0000000..096dcd2
--- /dev/null
+++ b/circuitpython/shared-module/qrio/QRDecoder.h
@@ -0,0 +1,45 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 Jeff Epler 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.
+ */
+
+#pragma once
+
+#include "py/obj.h"
+#include "lib/quirc/lib/quirc.h"
+#include "shared-bindings/qrio/PixelPolicy.h"
+
+typedef struct qrio_qrdecoder_obj {
+ mp_obj_base_t base;
+ struct quirc *quirc;
+ struct quirc_code code;
+ struct quirc_data data;
+} qrdecoder_qrdecoder_obj_t;
+
+void shared_module_qrio_qrdecoder_construct(qrdecoder_qrdecoder_obj_t *, int width, int height);
+int shared_module_qrio_qrdecoder_get_height(qrdecoder_qrdecoder_obj_t *);
+int shared_module_qrio_qrdecoder_get_width(qrdecoder_qrdecoder_obj_t *);
+void shared_module_qrio_qrdecoder_set_height(qrdecoder_qrdecoder_obj_t *, int height);
+void shared_module_qrio_qrdecoder_set_width(qrdecoder_qrdecoder_obj_t *, int width);
+mp_obj_t shared_module_qrio_qrdecoder_decode(qrdecoder_qrdecoder_obj_t *, const mp_buffer_info_t *bufinfo, qrio_pixel_policy_t policy);
diff --git a/circuitpython/shared-module/qrio/__init__.c b/circuitpython/shared-module/qrio/__init__.c
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/circuitpython/shared-module/qrio/__init__.c
diff --git a/circuitpython/shared-module/qrio/quirc_alloc.h b/circuitpython/shared-module/qrio/quirc_alloc.h
new file mode 100644
index 0000000..b01fac5
--- /dev/null
+++ b/circuitpython/shared-module/qrio/quirc_alloc.h
@@ -0,0 +1,14 @@
+#pragma once
+
+#include "py/gc.h"
+
+#if !MICROPY_GC_CONSERVATIVE_CLEAR
+// so that we can implement calloc as m_malloc
+#error Requires MICROPY_GC_CONSERVATIVE_CLEAR
+#endif
+
+#define QUIRC_MALLOC(x) gc_alloc((x), 0, false)
+#define QUIRC_CALLOC(x,y) gc_alloc((x) * (y), 0, false)
+#define QUIRC_FREE(x) gc_free((x))
+
+#define QUIRC_SMALL_STACK (1)
diff --git a/circuitpython/shared-module/rainbowio/__init__.c b/circuitpython/shared-module/rainbowio/__init__.c
new file mode 100644
index 0000000..b1745c3
--- /dev/null
+++ b/circuitpython/shared-module/rainbowio/__init__.c
@@ -0,0 +1,47 @@
+/*
+ * This file is part of the CircuitPython project, https://github.com/adafruit/circuitpython
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 Kattni Rembor
+ *
+ * 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.
+ */
+
+#include "shared-bindings/rainbowio/__init__.h"
+
+int32_t colorwheel(mp_float_t pos) {
+ pos = pos - ((uint32_t)(pos / 256) * 256);
+ int shift1, shift2;
+ if (pos < 85) {
+ shift1 = 8;
+ shift2 = 16;
+ } else if (pos < 170) {
+ pos -= 85;
+ shift1 = 0;
+ shift2 = 8;
+ } else {
+ pos -= 170;
+ shift1 = 16;
+ shift2 = 0;
+ }
+ int p = (int)(pos * 3);
+ p = (p < 256) ? p : 255;
+ return (p << shift1) | ((255 - p) << shift2);
+}
diff --git a/circuitpython/shared-module/random/__init__.c b/circuitpython/shared-module/random/__init__.c
new file mode 100644
index 0000000..438eb24
--- /dev/null
+++ b/circuitpython/shared-module/random/__init__.c
@@ -0,0 +1,132 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Paul Sokolovsky
+ * Copyright (c) 2017 Scott Shawcroft 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.
+ */
+
+#include <assert.h>
+#include <string.h>
+
+#include "py/runtime.h"
+#include "shared-bindings/os/__init__.h"
+#include "shared-bindings/random/__init__.h"
+#include "shared-bindings/time/__init__.h"
+
+// Yasmarang random number generator
+// by Ilya Levin
+// http://www.literatecode.com/yasmarang
+// Public Domain
+
+STATIC uint32_t yasmarang_pad = 0xeda4baba, yasmarang_n = 69, yasmarang_d = 233;
+STATIC uint8_t yasmarang_dat = 0;
+
+STATIC uint32_t yasmarang(void) {
+ if (yasmarang_pad == 0xeda4baba) {
+ if (!common_hal_os_urandom((uint8_t *)&yasmarang_pad, sizeof(uint32_t))) {
+ yasmarang_pad = common_hal_time_monotonic_ms() & 0xffffffff;
+ }
+ }
+ yasmarang_pad += yasmarang_dat + yasmarang_d * yasmarang_n;
+ yasmarang_pad = (yasmarang_pad << 3) + (yasmarang_pad >> 29);
+ yasmarang_n = yasmarang_pad | 2;
+ yasmarang_d ^= (yasmarang_pad << 31) + (yasmarang_pad >> 1);
+ yasmarang_dat ^= (char)yasmarang_pad ^ (yasmarang_d >> 8) ^ 1;
+
+ return yasmarang_pad ^ (yasmarang_d << 5) ^ (yasmarang_pad >> 18) ^ (yasmarang_dat << 1);
+} /* yasmarang */
+
+// End of Yasmarang
+
+// returns an unsigned integer below the given argument
+// n must not be zero
+STATIC uint32_t yasmarang_randbelow(uint32_t n) {
+ uint32_t mask = 1;
+ while ((n & mask) < n) {
+ mask = (mask << 1) | 1;
+ }
+ uint32_t r;
+ do {
+ r = yasmarang() & mask;
+ } while (r >= n);
+ return r;
+}
+
+void shared_modules_random_seed(mp_uint_t seed) {
+ yasmarang_pad = seed;
+ yasmarang_n = 69;
+ yasmarang_d = 233;
+ yasmarang_dat = 0;
+}
+
+mp_uint_t shared_modules_random_getrandbits(uint8_t n) {
+ uint32_t mask = ~0;
+ // Beware of C undefined behavior when shifting by >= than bit size
+ mask >>= (32 - n);
+ return yasmarang() & mask;
+}
+
+mp_int_t shared_modules_random_randrange(mp_int_t start, mp_int_t stop, mp_int_t step) {
+ mp_int_t n;
+ if (step > 0) {
+ n = (stop - start + step - 1) / step;
+ } else {
+ n = (stop - start + step + 1) / step;
+ }
+ return start + step * yasmarang_randbelow(n);
+}
+
+// returns a number in the range [0..1) using Yasmarang to fill in the fraction bits
+STATIC mp_float_t yasmarang_float(void) {
+ #if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE
+ typedef uint64_t mp_float_int_t;
+ #elif MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
+ typedef uint32_t mp_float_int_t;
+ #endif
+ union {
+ mp_float_t f;
+ #if MP_ENDIANNESS_LITTLE
+ struct { mp_float_int_t frc : MP_FLOAT_FRAC_BITS, exp : MP_FLOAT_EXP_BITS, sgn : 1;
+ } p;
+ #else
+ struct { mp_float_int_t sgn : 1, exp : MP_FLOAT_EXP_BITS, frc : MP_FLOAT_FRAC_BITS;
+ } p;
+ #endif
+ } u;
+ u.p.sgn = 0;
+ u.p.exp = (1 << (MP_FLOAT_EXP_BITS - 1)) - 1;
+ if (MP_FLOAT_FRAC_BITS <= 32) {
+ u.p.frc = yasmarang();
+ } else {
+ u.p.frc = ((uint64_t)yasmarang() << 32) | (uint64_t)yasmarang();
+ }
+ return u.f - 1;
+}
+
+mp_float_t shared_modules_random_random(void) {
+ return yasmarang_float();
+}
+
+mp_float_t shared_modules_random_uniform(mp_float_t a, mp_float_t b) {
+ return a + (b - a) * yasmarang_float();
+}
diff --git a/circuitpython/shared-module/rgbmatrix/RGBMatrix.c b/circuitpython/shared-module/rgbmatrix/RGBMatrix.c
new file mode 100644
index 0000000..c6dbfc0
--- /dev/null
+++ b/circuitpython/shared-module/rgbmatrix/RGBMatrix.c
@@ -0,0 +1,224 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Jeff Epler 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.
+ */
+
+#include <string.h>
+
+#include "py/gc.h"
+#include "py/obj.h"
+#include "py/objarray.h"
+#include "py/objproperty.h"
+#include "py/runtime.h"
+
+#include "common-hal/rgbmatrix/RGBMatrix.h"
+#include "shared-module/rgbmatrix/allocator.h"
+#include "shared-bindings/rgbmatrix/RGBMatrix.h"
+#include "shared-bindings/microcontroller/Pin.h"
+#include "shared-bindings/microcontroller/__init__.h"
+#include "shared-bindings/util.h"
+#include "shared-module/framebufferio/FramebufferDisplay.h"
+
+extern Protomatter_core *_PM_protoPtr;
+
+void common_hal_rgbmatrix_rgbmatrix_construct(rgbmatrix_rgbmatrix_obj_t *self, int width, int bit_depth, uint8_t rgb_count, uint8_t *rgb_pins, uint8_t addr_count, uint8_t *addr_pins, uint8_t clock_pin, uint8_t latch_pin, uint8_t oe_pin, bool doublebuffer, mp_obj_t framebuffer, int8_t tile, bool serpentine, void *timer) {
+ self->width = width;
+ self->bit_depth = bit_depth;
+ self->rgb_count = rgb_count;
+ memcpy(self->rgb_pins, rgb_pins, rgb_count);
+ self->addr_count = addr_count;
+ memcpy(self->addr_pins, addr_pins, addr_count);
+ self->clock_pin = clock_pin;
+ self->oe_pin = oe_pin;
+ self->latch_pin = latch_pin;
+ self->doublebuffer = doublebuffer;
+ self->tile = tile;
+ self->serpentine = serpentine;
+
+ self->timer = timer ? timer : common_hal_rgbmatrix_timer_allocate(self);
+ if (self->timer == NULL) {
+ mp_raise_ValueError(translate("No timer available"));
+ }
+
+ self->width = width;
+ self->bufsize = 2 * width * common_hal_rgbmatrix_rgbmatrix_get_height(self);
+
+ common_hal_rgbmatrix_rgbmatrix_reconstruct(self, framebuffer);
+}
+
+void common_hal_rgbmatrix_rgbmatrix_reconstruct(rgbmatrix_rgbmatrix_obj_t *self, mp_obj_t framebuffer) {
+ self->paused = 1;
+
+ common_hal_rgbmatrix_timer_disable(self->timer);
+ if (framebuffer) {
+ self->framebuffer = framebuffer;
+ mp_get_buffer_raise(self->framebuffer, &self->bufinfo, MP_BUFFER_READ);
+ if (mp_get_buffer(self->framebuffer, &self->bufinfo, MP_BUFFER_RW)) {
+ self->bufinfo.typecode = 'H' | MP_OBJ_ARRAY_TYPECODE_FLAG_RW;
+ } else {
+ self->bufinfo.typecode = 'H';
+ }
+ // verify that the matrix is big enough
+ mp_get_index(mp_obj_get_type(self->framebuffer), self->bufinfo.len, MP_OBJ_NEW_SMALL_INT(self->bufsize - 1), false);
+ } else {
+ common_hal_rgbmatrix_free_impl(self->bufinfo.buf);
+ common_hal_rgbmatrix_free_impl(self->protomatter.rgbPins);
+ common_hal_rgbmatrix_free_impl(self->protomatter.addr);
+ common_hal_rgbmatrix_free_impl(self->protomatter.screenData);
+
+ self->framebuffer = NULL;
+ self->bufinfo.buf = common_hal_rgbmatrix_allocator_impl(self->bufsize);
+ self->bufinfo.len = self->bufsize;
+ self->bufinfo.typecode = 'H' | MP_OBJ_ARRAY_TYPECODE_FLAG_RW;
+ }
+
+ memset(&self->protomatter, 0, sizeof(self->protomatter));
+ ProtomatterStatus stat = _PM_init(&self->protomatter,
+ self->width, self->bit_depth,
+ self->rgb_count / 6, self->rgb_pins,
+ self->addr_count, self->addr_pins,
+ self->clock_pin, self->latch_pin, self->oe_pin,
+ self->doublebuffer, self->serpentine ? -self->tile : self->tile,
+ self->timer);
+
+ if (stat == PROTOMATTER_OK) {
+ _PM_protoPtr = &self->protomatter;
+ common_hal_rgbmatrix_timer_enable(self->timer);
+ stat = _PM_begin(&self->protomatter);
+
+ if (stat == PROTOMATTER_OK) {
+ _PM_convert_565(&self->protomatter, self->bufinfo.buf, self->width);
+ _PM_swapbuffer_maybe(&self->protomatter);
+ }
+ }
+
+ if (stat != PROTOMATTER_OK) {
+ common_hal_rgbmatrix_rgbmatrix_deinit(self);
+ switch (stat) {
+ case PROTOMATTER_ERR_PINS:
+ mp_raise_ValueError(translate("Invalid pin"));
+ break;
+ case PROTOMATTER_ERR_ARG:
+ mp_raise_ValueError(translate("Invalid argument"));
+ break;
+ case PROTOMATTER_ERR_MALLOC:
+ mp_raise_msg_varg(&mp_type_MemoryError, translate("Failed to allocate %q buffer"), MP_QSTR_RGBMatrix);
+ break;
+ default:
+ mp_raise_msg_varg(&mp_type_RuntimeError,
+ translate("Internal error #%d"), (int)stat);
+ break;
+ }
+ }
+
+ self->paused = 0;
+}
+
+STATIC void free_pin(uint8_t *pin) {
+ if (*pin != COMMON_HAL_MCU_NO_PIN) {
+ common_hal_mcu_pin_reset_number(*pin);
+ }
+ *pin = COMMON_HAL_MCU_NO_PIN;
+}
+
+STATIC void free_pin_seq(uint8_t *seq, int count) {
+ for (int i = 0; i < count; i++) {
+ free_pin(&seq[i]);
+ }
+}
+
+void common_hal_rgbmatrix_rgbmatrix_deinit(rgbmatrix_rgbmatrix_obj_t *self) {
+ if (self->timer) {
+ common_hal_rgbmatrix_timer_free(self->timer);
+ self->timer = 0;
+ }
+
+ if (_PM_protoPtr == &self->protomatter) {
+ _PM_protoPtr = NULL;
+ }
+
+ free_pin_seq(self->rgb_pins, self->rgb_count);
+ free_pin_seq(self->addr_pins, self->addr_count);
+ free_pin(&self->clock_pin);
+ free_pin(&self->latch_pin);
+ free_pin(&self->oe_pin);
+
+ if (self->protomatter.rgbPins) {
+ _PM_deallocate(&self->protomatter);
+ }
+ memset(&self->protomatter, 0, sizeof(self->protomatter));
+
+ // If it was supervisor-allocated, it is supervisor-freed and the pointer
+ // is zeroed, otherwise the pointer is just zeroed
+ _PM_free(self->bufinfo.buf);
+ self->base.type = NULL;
+
+ // If a framebuffer was passed in to the constructor, NULL the reference
+ // here so that it will become GC'able
+ self->framebuffer = NULL;
+}
+
+void rgbmatrix_rgbmatrix_collect_ptrs(rgbmatrix_rgbmatrix_obj_t *self) {
+ gc_collect_ptr(self->framebuffer);
+}
+
+void common_hal_rgbmatrix_rgbmatrix_set_paused(rgbmatrix_rgbmatrix_obj_t *self, bool paused) {
+ if (paused && !self->paused) {
+ _PM_stop(&self->protomatter);
+ } else if (!paused && self->paused) {
+ _PM_resume(&self->protomatter);
+ _PM_convert_565(&self->protomatter, self->bufinfo.buf, self->width);
+ _PM_swapbuffer_maybe(&self->protomatter);
+ }
+ self->paused = paused;
+}
+
+bool common_hal_rgbmatrix_rgbmatrix_get_paused(rgbmatrix_rgbmatrix_obj_t *self) {
+ return self->paused;
+}
+
+void common_hal_rgbmatrix_rgbmatrix_refresh(rgbmatrix_rgbmatrix_obj_t *self) {
+ if (!self->paused) {
+ _PM_convert_565(&self->protomatter, self->bufinfo.buf, self->width);
+ _PM_swapbuffer_maybe(&self->protomatter);
+ }
+}
+
+int common_hal_rgbmatrix_rgbmatrix_get_width(rgbmatrix_rgbmatrix_obj_t *self) {
+ return self->width;
+}
+
+int common_hal_rgbmatrix_rgbmatrix_get_height(rgbmatrix_rgbmatrix_obj_t *self) {
+ int computed_height = (self->rgb_count / 3) * (1 << (self->addr_count)) * self->tile;
+ return computed_height;
+}
+
+void *common_hal_rgbmatrix_allocator_impl(size_t sz) {
+ supervisor_allocation *allocation = allocate_memory(align32_size(sz), false, true);
+ return allocation ? allocation->ptr : NULL;
+}
+
+void common_hal_rgbmatrix_free_impl(void *ptr_in) {
+ free_memory(allocation_from_ptr(ptr_in));
+}
diff --git a/circuitpython/shared-module/rgbmatrix/RGBMatrix.h b/circuitpython/shared-module/rgbmatrix/RGBMatrix.h
new file mode 100644
index 0000000..65bfc97
--- /dev/null
+++ b/circuitpython/shared-module/rgbmatrix/RGBMatrix.h
@@ -0,0 +1,50 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Jeff Epler 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.
+ */
+
+#pragma once
+
+#include "py/obj.h"
+#include "lib/protomatter/src/core.h"
+
+extern const mp_obj_type_t rgbmatrix_RGBMatrix_type;
+typedef struct {
+ mp_obj_base_t base;
+ mp_obj_t framebuffer;
+ mp_buffer_info_t bufinfo;
+ Protomatter_core protomatter;
+ void *timer;
+ uint16_t bufsize, width;
+ uint8_t rgb_pins[30];
+ uint8_t addr_pins[10];
+ uint8_t clock_pin, latch_pin, oe_pin;
+ uint8_t rgb_count, addr_count;
+ uint8_t bit_depth;
+ bool core_is_initialized;
+ bool paused;
+ bool doublebuffer;
+ bool serpentine;
+ int8_t tile;
+} rgbmatrix_rgbmatrix_obj_t;
diff --git a/circuitpython/shared-module/rgbmatrix/__init__.c b/circuitpython/shared-module/rgbmatrix/__init__.c
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/circuitpython/shared-module/rgbmatrix/__init__.c
diff --git a/circuitpython/shared-module/rgbmatrix/__init__.h b/circuitpython/shared-module/rgbmatrix/__init__.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/circuitpython/shared-module/rgbmatrix/__init__.h
diff --git a/circuitpython/shared-module/rgbmatrix/allocator.h b/circuitpython/shared-module/rgbmatrix/allocator.h
new file mode 100644
index 0000000..5c11ad5
--- /dev/null
+++ b/circuitpython/shared-module/rgbmatrix/allocator.h
@@ -0,0 +1,37 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Jeff Epler 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.
+ */
+
+#pragma once
+
+#include <stdbool.h>
+#include "py/gc.h"
+#include "py/misc.h"
+#include "supervisor/memory.h"
+
+#define _PM_allocate common_hal_rgbmatrix_allocator_impl
+#define _PM_free(x) (common_hal_rgbmatrix_free_impl((x)), (x) = NULL, (void)0)
+extern void *common_hal_rgbmatrix_allocator_impl(size_t sz);
+extern void common_hal_rgbmatrix_free_impl(void *);
diff --git a/circuitpython/shared-module/rotaryio/IncrementalEncoder.c b/circuitpython/shared-module/rotaryio/IncrementalEncoder.c
new file mode 100644
index 0000000..27b9cdb
--- /dev/null
+++ b/circuitpython/shared-module/rotaryio/IncrementalEncoder.c
@@ -0,0 +1,90 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 Jeff Epler 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.
+ */
+
+#if CIRCUITPY_ROTARYIO && CIRCUITPY_ROTARYIO_SOFTENCODER
+#include "shared-bindings/rotaryio/IncrementalEncoder.h"
+#include "shared-module/rotaryio/IncrementalEncoder.h"
+#include "common-hal/rotaryio/IncrementalEncoder.h"
+
+void shared_module_softencoder_state_init(rotaryio_incrementalencoder_obj_t *self, uint8_t quiescent_state) {
+ self->state = quiescent_state;
+ self->sub_count = 0;
+ common_hal_rotaryio_incrementalencoder_set_position(self, 0);
+}
+
+void shared_module_softencoder_state_update(rotaryio_incrementalencoder_obj_t *self, uint8_t new_state) {
+ static const int8_t transitions[16] = {
+ 0, // 00 -> 00 no movement
+ -1, // 00 -> 01 3/4 ccw (11 detent) or 1/4 ccw (00 at detent)
+ +1, // 00 -> 10 3/4 cw or 1/4 cw
+ 0, // 00 -> 11 non-Gray-code transition
+ +1, // 01 -> 00 2/4 or 4/4 cw
+ 0, // 01 -> 01 no movement
+ 0, // 01 -> 10 non-Gray-code transition
+ -1, // 01 -> 11 4/4 or 2/4 ccw
+ -1, // 10 -> 00 2/4 or 4/4 ccw
+ 0, // 10 -> 01 non-Gray-code transition
+ 0, // 10 -> 10 no movement
+ +1, // 10 -> 11 4/4 or 2/4 cw
+ 0, // 11 -> 00 non-Gray-code transition
+ +1, // 11 -> 01 1/4 or 3/4 cw
+ -1, // 11 -> 10 1/4 or 3/4 ccw
+ 0, // 11 -> 11 no movement
+ };
+
+ new_state &= 0x3;
+ int idx = (self->state << 2) | new_state;
+ self->state = new_state;
+
+ int8_t sub_incr = transitions[idx];
+
+ self->sub_count += sub_incr;
+
+ if (self->sub_count >= self->divisor) {
+ self->position += 1;
+ self->sub_count = 0;
+ } else if (self->sub_count <= -self->divisor) {
+ self->position -= 1;
+ self->sub_count = 0;
+ }
+}
+
+mp_int_t common_hal_rotaryio_incrementalencoder_get_position(rotaryio_incrementalencoder_obj_t *self) {
+ return self->position;
+}
+
+void common_hal_rotaryio_incrementalencoder_set_position(rotaryio_incrementalencoder_obj_t *self, mp_int_t position) {
+ self->position = position;
+}
+
+mp_int_t common_hal_rotaryio_incrementalencoder_get_divisor(rotaryio_incrementalencoder_obj_t *self) {
+ return self->divisor;
+}
+
+void common_hal_rotaryio_incrementalencoder_set_divisor(rotaryio_incrementalencoder_obj_t *self, mp_int_t divisor) {
+ self->divisor = divisor;
+}
+#endif
diff --git a/circuitpython/shared-module/rotaryio/IncrementalEncoder.h b/circuitpython/shared-module/rotaryio/IncrementalEncoder.h
new file mode 100644
index 0000000..82a5644
--- /dev/null
+++ b/circuitpython/shared-module/rotaryio/IncrementalEncoder.h
@@ -0,0 +1,34 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 Jeff Epler 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.
+ */
+
+#pragma once
+
+#include "common-hal/rotaryio/IncrementalEncoder.h"
+
+void shared_module_softencoder_state_init(rotaryio_incrementalencoder_obj_t *self, uint8_t quiescent_state);
+void shared_module_softencoder_state_update(rotaryio_incrementalencoder_obj_t *self, uint8_t new_state);
+mp_int_t common_hal_rotaryio_incrementalencoder_get_position(rotaryio_incrementalencoder_obj_t *self);
+void common_hal_rotaryio_incrementalencoder_set_position(rotaryio_incrementalencoder_obj_t *self, mp_int_t position);
diff --git a/circuitpython/shared-module/sdcardio/SDCard.c b/circuitpython/shared-module/sdcardio/SDCard.c
new file mode 100644
index 0000000..182eb0e
--- /dev/null
+++ b/circuitpython/shared-module/sdcardio/SDCard.c
@@ -0,0 +1,504 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Jeff Epler 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.
+ */
+
+// This implementation largely follows the structure of adafruit_sdcard.py
+
+#include "shared-bindings/busio/SPI.h"
+#include "shared-bindings/digitalio/DigitalInOut.h"
+#include "shared-bindings/sdcardio/SDCard.h"
+#include "shared-bindings/time/__init__.h"
+#include "shared-bindings/util.h"
+#include "shared-module/sdcardio/SDCard.h"
+
+#include "py/mperrno.h"
+
+#if 0
+#define DEBUG_PRINT(...) ((void)mp_printf(&mp_plat_print,##__VA_ARGS__))
+#else
+#define DEBUG_PRINT(...) ((void)0)
+#endif
+
+#define CMD_TIMEOUT (200)
+
+#define R1_IDLE_STATE (1 << 0)
+#define R1_ILLEGAL_COMMAND (1 << 2)
+
+#define TOKEN_CMD25 (0xFC)
+#define TOKEN_STOP_TRAN (0xFD)
+#define TOKEN_DATA (0xFE)
+
+STATIC bool lock_and_configure_bus(sdcardio_sdcard_obj_t *self) {
+ if (!common_hal_busio_spi_try_lock(self->bus)) {
+ return false;
+ }
+ common_hal_busio_spi_configure(self->bus, self->baudrate, 0, 0, 8);
+ common_hal_digitalio_digitalinout_set_value(&self->cs, false);
+ return true;
+}
+
+STATIC void lock_bus_or_throw(sdcardio_sdcard_obj_t *self) {
+ if (!lock_and_configure_bus(self)) {
+ mp_raise_OSError(EAGAIN);
+ }
+}
+
+STATIC void clock_card(sdcardio_sdcard_obj_t *self, int bytes) {
+ uint8_t buf[] = {0xff};
+ common_hal_digitalio_digitalinout_set_value(&self->cs, true);
+ for (int i = 0; i < bytes; i++) {
+ common_hal_busio_spi_write(self->bus, buf, 1);
+ }
+}
+
+STATIC void extraclock_and_unlock_bus(sdcardio_sdcard_obj_t *self) {
+ clock_card(self, 1);
+ common_hal_busio_spi_unlock(self->bus);
+}
+
+static uint8_t CRC7(const uint8_t *data, uint8_t n) {
+ uint8_t crc = 0;
+ for (uint8_t i = 0; i < n; i++) {
+ uint8_t d = data[i];
+ for (uint8_t j = 0; j < 8; j++) {
+ crc <<= 1;
+ if ((d & 0x80) ^ (crc & 0x80)) {
+ crc ^= 0x09;
+ }
+ d <<= 1;
+ }
+ }
+ return (crc << 1) | 1;
+}
+
+#define READY_TIMEOUT_NS (300 * 1000 * 1000) // 300ms
+STATIC int wait_for_ready(sdcardio_sdcard_obj_t *self) {
+ uint64_t deadline = common_hal_time_monotonic_ns() + READY_TIMEOUT_NS;
+ while (common_hal_time_monotonic_ns() < deadline) {
+ uint8_t b;
+ common_hal_busio_spi_read(self->bus, &b, 1, 0xff);
+ if (b == 0xff) {
+ return 0;
+ }
+ }
+ return -ETIMEDOUT;
+}
+
+// Note: this is never called while "in cmd25" (in fact, it's only used by `exit_cmd25`)
+STATIC bool cmd_nodata(sdcardio_sdcard_obj_t *self, int cmd, int response) {
+ uint8_t cmdbuf[2] = {cmd, 0xff};
+
+ assert(!self->in_cmd25);
+
+ common_hal_busio_spi_write(self->bus, cmdbuf, sizeof(cmdbuf));
+
+ // Wait for the response (response[7] == response)
+ for (int i = 0; i < CMD_TIMEOUT; i++) {
+ common_hal_busio_spi_read(self->bus, cmdbuf, 1, 0xff);
+ if (cmdbuf[0] == response) {
+ return 0;
+ }
+ }
+ return -EIO;
+}
+
+
+STATIC int exit_cmd25(sdcardio_sdcard_obj_t *self) {
+ if (self->in_cmd25) {
+ DEBUG_PRINT("exit cmd25\n");
+ self->in_cmd25 = false;
+ return cmd_nodata(self, TOKEN_STOP_TRAN, 0);
+ }
+ return 0;
+}
+
+// In Python API, defaults are response=None, data_block=True, wait=True
+STATIC int cmd(sdcardio_sdcard_obj_t *self, int cmd, int arg, void *response_buf, size_t response_len, bool data_block, bool wait) {
+ int r = exit_cmd25(self);
+ if (r < 0) {
+ return r;
+ }
+
+ DEBUG_PRINT("cmd % 3d [%02x] arg=% 11d [%08x] len=%d%s%s\n", cmd, cmd, arg, arg, response_len, data_block ? " data" : "", wait ? " wait" : "");
+ uint8_t cmdbuf[6];
+ cmdbuf[0] = cmd | 0x40;
+ cmdbuf[1] = (arg >> 24) & 0xff;
+ cmdbuf[2] = (arg >> 16) & 0xff;
+ cmdbuf[3] = (arg >> 8) & 0xff;
+ cmdbuf[4] = arg & 0xff;
+ cmdbuf[5] = CRC7(cmdbuf, 5);
+
+ if (wait) {
+ r = wait_for_ready(self);
+ if (r < 0) {
+ return r;
+ }
+ }
+
+ common_hal_busio_spi_write(self->bus, cmdbuf, sizeof(cmdbuf));
+
+ // Wait for the response (response[7] == 0)
+ bool response_received = false;
+ for (int i = 0; i < CMD_TIMEOUT; i++) {
+ common_hal_busio_spi_read(self->bus, cmdbuf, 1, 0xff);
+ if ((cmdbuf[0] & 0x80) == 0) {
+ response_received = true;
+ break;
+ }
+ }
+
+ if (!response_received) {
+ return -EIO;
+ }
+
+ if (response_buf) {
+
+ if (data_block) {
+ cmdbuf[1] = 0xff;
+ do {
+ // Wait for the start block byte
+ common_hal_busio_spi_read(self->bus, cmdbuf + 1, 1, 0xff);
+ } while (cmdbuf[1] != 0xfe);
+ }
+
+ common_hal_busio_spi_read(self->bus, response_buf, response_len, 0xff);
+
+ if (data_block) {
+ // Read and discard the CRC-CCITT checksum
+ common_hal_busio_spi_read(self->bus, cmdbuf + 1, 2, 0xff);
+ }
+
+ }
+
+ return cmdbuf[0];
+}
+
+STATIC int block_cmd(sdcardio_sdcard_obj_t *self, int cmd_, int block, void *response_buf, size_t response_len, bool data_block, bool wait) {
+ return cmd(self, cmd_, block * self->cdv, response_buf, response_len, true, true);
+}
+
+STATIC const compressed_string_t *init_card_v1(sdcardio_sdcard_obj_t *self) {
+ for (int i = 0; i < CMD_TIMEOUT; i++) {
+ if (cmd(self, 41, 0, NULL, 0, true, true) == 0) {
+ return NULL;
+ }
+ }
+ return translate("timeout waiting for v1 card");
+}
+
+STATIC const compressed_string_t *init_card_v2(sdcardio_sdcard_obj_t *self) {
+ for (int i = 0; i < CMD_TIMEOUT; i++) {
+ uint8_t ocr[4];
+ common_hal_time_delay_ms(50);
+ cmd(self, 58, 0, ocr, sizeof(ocr), false, true);
+ cmd(self, 55, 0, NULL, 0, true, true);
+ if (cmd(self, 41, 0x40000000, NULL, 0, true, true) == 0) {
+ cmd(self, 58, 0, ocr, sizeof(ocr), false, true);
+ if ((ocr[0] & 0x40) != 0) {
+ self->cdv = 1;
+ }
+ return NULL;
+ }
+ }
+ return translate("timeout waiting for v2 card");
+}
+
+STATIC const compressed_string_t *init_card(sdcardio_sdcard_obj_t *self) {
+ clock_card(self, 10);
+
+ common_hal_digitalio_digitalinout_set_value(&self->cs, false);
+
+ assert(!self->in_cmd25);
+ self->in_cmd25 = false; // should be false already
+
+ // CMD0: init card: should return _R1_IDLE_STATE (allow 5 attempts)
+ {
+ bool reached_idle_state = false;
+ for (int i = 0; i < 5; i++) {
+ // do not call cmd with wait=true, because that will return
+ // prematurely if the idle state is not reached. we can't depend on
+ // this when the card is not yet in SPI mode
+ (void)wait_for_ready(self);
+ if (cmd(self, 0, 0, NULL, 0, true, false) == R1_IDLE_STATE) {
+ reached_idle_state = true;
+ break;
+ }
+ }
+ if (!reached_idle_state) {
+ return translate("no SD card");
+ }
+ }
+
+ // CMD8: determine card version
+ {
+ uint8_t rb7[4];
+ int response = cmd(self, 8, 0x1AA, rb7, sizeof(rb7), false, true);
+ if (response == R1_IDLE_STATE) {
+ const compressed_string_t *result = init_card_v2(self);
+ if (result != NULL) {
+ return result;
+ }
+ } else if (response == (R1_IDLE_STATE | R1_ILLEGAL_COMMAND)) {
+ const compressed_string_t *result = init_card_v1(self);
+ if (result != NULL) {
+ return result;
+ }
+ } else {
+ DEBUG_PRINT("Reading card version, response=0x%02x\n", response);
+ return translate("couldn't determine SD card version");
+ }
+ }
+
+ // CMD9: get number of sectors
+ {
+ uint8_t csd[16];
+ int response = cmd(self, 9, 0, csd, sizeof(csd), true, true);
+ if (response != 0) {
+ return translate("no response from SD card");
+ }
+ int csd_version = (csd[0] & 0xC0) >> 6;
+ if (csd_version >= 2) {
+ return translate("SD card CSD format not supported");
+ }
+
+ if (csd_version == 1) {
+ self->sectors = ((csd[8] << 8 | csd[9]) + 1) * 1024;
+ } else {
+ uint32_t block_length = 1 << (csd[5] & 0xF);
+ uint32_t c_size = ((csd[6] & 0x3) << 10) | (csd[7] << 2) | ((csd[8] & 0xC) >> 6);
+ uint32_t mult = 1 << (((csd[9] & 0x3) << 1 | (csd[10] & 0x80) >> 7) + 2);
+ self->sectors = block_length / 512 * mult * (c_size + 1);
+ }
+ }
+
+ // CMD16: set block length to 512 bytes
+ {
+ int response = cmd(self, 16, 512, NULL, 0, true, true);
+ if (response != 0) {
+ return translate("can't set 512 block size");
+ }
+ }
+
+ return NULL;
+}
+
+void common_hal_sdcardio_sdcard_construct(sdcardio_sdcard_obj_t *self, busio_spi_obj_t *bus, const mcu_pin_obj_t *cs, int baudrate) {
+ self->bus = bus;
+ common_hal_digitalio_digitalinout_construct(&self->cs, cs);
+ common_hal_digitalio_digitalinout_switch_to_output(&self->cs, true, DRIVE_MODE_PUSH_PULL);
+
+ self->cdv = 512;
+ self->sectors = 0;
+ self->baudrate = 250000;
+
+ lock_bus_or_throw(self);
+ const compressed_string_t *result = init_card(self);
+ extraclock_and_unlock_bus(self);
+
+ if (result != NULL) {
+ common_hal_digitalio_digitalinout_deinit(&self->cs);
+ mp_raise_OSError_msg(result);
+ }
+
+ self->baudrate = baudrate;
+}
+
+void common_hal_sdcardio_sdcard_deinit(sdcardio_sdcard_obj_t *self) {
+ if (!self->bus) {
+ return;
+ }
+ common_hal_sdcardio_sdcard_sync(self);
+ self->bus = 0;
+ common_hal_digitalio_digitalinout_deinit(&self->cs);
+}
+
+STATIC void common_hal_sdcardio_check_for_deinit(sdcardio_sdcard_obj_t *self) {
+ if (!self->bus) {
+ raise_deinited_error();
+ }
+}
+
+int common_hal_sdcardio_sdcard_get_blockcount(sdcardio_sdcard_obj_t *self) {
+ common_hal_sdcardio_check_for_deinit(self);
+ return self->sectors;
+}
+
+STATIC int readinto(sdcardio_sdcard_obj_t *self, void *buf, size_t size) {
+ uint8_t aux[2] = {0, 0};
+ while (aux[0] != 0xfe) {
+ common_hal_busio_spi_read(self->bus, aux, 1, 0xff);
+ }
+
+ common_hal_busio_spi_read(self->bus, buf, size, 0xff);
+
+ // Read checksum and throw it away
+ common_hal_busio_spi_read(self->bus, aux, sizeof(aux), 0xff);
+ return 0;
+}
+
+STATIC int readblocks(sdcardio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *buf) {
+ uint32_t nblocks = buf->len / 512;
+ if (nblocks == 1) {
+ // Use CMD17 to read a single block
+ return block_cmd(self, 17, start_block, buf->buf, buf->len, true, true);
+ } else {
+ // Use CMD18 to read multiple blocks
+ int r = block_cmd(self, 18, start_block, NULL, 0, true, true);
+ if (r < 0) {
+ return r;
+ }
+
+ uint8_t *ptr = buf->buf;
+ while (nblocks--) {
+ r = readinto(self, ptr, 512);
+ if (r < 0) {
+ return r;
+ }
+ ptr += 512;
+ }
+
+ // End the multi-block read
+ r = cmd(self, 12, 0, NULL, 0, true, false);
+
+ // Return first status 0 or last before card ready (0xff)
+ while (r != 0) {
+ uint8_t single_byte;
+ common_hal_busio_spi_read(self->bus, &single_byte, 1, 0xff);
+ if (single_byte & 0x80) {
+ return r;
+ }
+ r = single_byte;
+ }
+ }
+ return 0;
+}
+
+int common_hal_sdcardio_sdcard_readblocks(sdcardio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *buf) {
+ common_hal_sdcardio_check_for_deinit(self);
+ if (buf->len % 512 != 0) {
+ mp_raise_ValueError(translate("Buffer length must be a multiple of 512"));
+ }
+
+ lock_and_configure_bus(self);
+ int r = readblocks(self, start_block, buf);
+ extraclock_and_unlock_bus(self);
+ return r;
+}
+
+STATIC int _write(sdcardio_sdcard_obj_t *self, uint8_t token, void *buf, size_t size) {
+ wait_for_ready(self);
+
+ uint8_t cmd[2];
+ cmd[0] = token;
+
+ common_hal_busio_spi_write(self->bus, cmd, 1);
+ common_hal_busio_spi_write(self->bus, buf, size);
+
+ cmd[0] = cmd[1] = 0xff;
+ common_hal_busio_spi_write(self->bus, cmd, 2);
+
+ // Check the response
+ // This differs from the traditional adafruit_sdcard handling,
+ // but adafruit_sdcard also ignored the return value of SDCard._write(!)
+ // so nobody noticed
+ //
+ //
+ // Response is as follows:
+ // x x x 0 STAT 1
+ // 7 6 5 4 3..1 0
+ // with STATUS 010 indicating "data accepted", and other status bit
+ // combinations indicating failure.
+ // In practice, I was seeing cmd[0] as 0xe5, indicating success
+ for (int i = 0; i < CMD_TIMEOUT; i++) {
+ common_hal_busio_spi_read(self->bus, cmd, 1, 0xff);
+ DEBUG_PRINT("i=%02d cmd[0] = 0x%02x\n", i, cmd[0]);
+ if ((cmd[0] & 0b00010001) == 0b00000001) {
+ if ((cmd[0] & 0x1f) != 0x5) {
+ return -EIO;
+ } else {
+ break;
+ }
+ }
+ }
+
+ // Wait for the write to finish
+ do {
+ common_hal_busio_spi_read(self->bus, cmd, 1, 0xff);
+ } while (cmd[0] == 0);
+
+ // Success
+ return 0;
+}
+
+STATIC int writeblocks(sdcardio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *buf) {
+ common_hal_sdcardio_check_for_deinit(self);
+ uint32_t nblocks = buf->len / 512;
+
+ DEBUG_PRINT("cmd25? %d next_block %d start_block %d\n", self->in_cmd25, self->next_block, start_block);
+
+ if (!self->in_cmd25 || start_block != self->next_block) {
+ DEBUG_PRINT("entering CMD25 at %d\n", (int)start_block);
+ // Use CMD25 to write multiple block
+ int r = block_cmd(self, 25, start_block, NULL, 0, true, true);
+ if (r < 0) {
+ return r;
+ }
+ self->in_cmd25 = true;
+ }
+
+ self->next_block = start_block;
+
+ uint8_t *ptr = buf->buf;
+ while (nblocks--) {
+ int r = _write(self, TOKEN_CMD25, ptr, 512);
+ if (r < 0) {
+ self->in_cmd25 = false;
+ return r;
+ }
+ self->next_block++;
+ ptr += 512;
+ }
+
+ return 0;
+}
+
+int common_hal_sdcardio_sdcard_sync(sdcardio_sdcard_obj_t *self) {
+ common_hal_sdcardio_check_for_deinit(self);
+ lock_and_configure_bus(self);
+ int r = exit_cmd25(self);
+ extraclock_and_unlock_bus(self);
+ return r;
+}
+
+int common_hal_sdcardio_sdcard_writeblocks(sdcardio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *buf) {
+ common_hal_sdcardio_check_for_deinit(self);
+ if (buf->len % 512 != 0) {
+ mp_raise_ValueError(translate("Buffer length must be a multiple of 512"));
+ }
+ lock_and_configure_bus(self);
+ int r = writeblocks(self, start_block, buf);
+ extraclock_and_unlock_bus(self);
+ return r;
+}
diff --git a/circuitpython/shared-module/sdcardio/SDCard.h b/circuitpython/shared-module/sdcardio/SDCard.h
new file mode 100644
index 0000000..ff89b89
--- /dev/null
+++ b/circuitpython/shared-module/sdcardio/SDCard.h
@@ -0,0 +1,54 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Jeff Epler 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.
+ */
+
+#pragma once
+
+#include "py/obj.h"
+#include "py/objproperty.h"
+#include "py/runtime.h"
+#include "py/objarray.h"
+
+#include "common-hal/busio/SPI.h"
+#include "common-hal/digitalio/DigitalInOut.h"
+
+typedef struct {
+ mp_obj_base_t base;
+ busio_spi_obj_t *bus;
+ digitalio_digitalinout_obj_t cs;
+ int cdv;
+ int baudrate;
+ uint32_t sectors;
+ uint32_t next_block;
+ bool in_cmd25;
+} sdcardio_sdcard_obj_t;
+
+void common_hal_sdcardio_sdcard_construct(sdcardio_sdcard_obj_t *self, busio_spi_obj_t *spi, const mcu_pin_obj_t *cs, int baudrate);
+void common_hal_sdcardio_sdcard_deinit(sdcardio_sdcard_obj_t *self);
+void common_hal_sdcardio_sdcard_check_for_deinit(sdcardio_sdcard_obj_t *self);
+int common_hal_sdcardio_sdcard_get_blockcount(sdcardio_sdcard_obj_t *self);
+int common_hal_sdcardio_sdcard_readblocks(sdcardio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *buf);
+int common_hal_sdcardio_sdcard_sync(sdcardio_sdcard_obj_t *self);
+int common_hal_sdcardio_sdcard_writeblocks(sdcardio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *buf);
diff --git a/circuitpython/shared-module/sdcardio/__init__.c b/circuitpython/shared-module/sdcardio/__init__.c
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/circuitpython/shared-module/sdcardio/__init__.c
diff --git a/circuitpython/shared-module/sdcardio/__init__.h b/circuitpython/shared-module/sdcardio/__init__.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/circuitpython/shared-module/sdcardio/__init__.h
diff --git a/circuitpython/shared-module/sharpdisplay/SharpMemoryFramebuffer.c b/circuitpython/shared-module/sharpdisplay/SharpMemoryFramebuffer.c
new file mode 100644
index 0000000..285abb1
--- /dev/null
+++ b/circuitpython/shared-module/sharpdisplay/SharpMemoryFramebuffer.c
@@ -0,0 +1,249 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Jeff Epler 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.
+ */
+
+#include <string.h>
+
+#include "py/gc.h"
+
+#include "shared-bindings/board/__init__.h"
+#include "shared-bindings/microcontroller/Pin.h"
+#include "shared-bindings/sharpdisplay/SharpMemoryFramebuffer.h"
+#include "shared-module/sharpdisplay/SharpMemoryFramebuffer.h"
+
+#include "supervisor/memory.h"
+
+#define SHARPMEM_BIT_WRITECMD_LSB (0x80)
+#define SHARPMEM_BIT_VCOM_LSB (0x40)
+
+STATIC uint8_t bitrev(uint8_t n) {
+ uint8_t r = 0;
+ for (int i = 0; i < 8; i++) {r |= ((n >> i) & 1) << (7 - i);
+ }
+ return r;
+}
+
+int common_hal_sharpdisplay_framebuffer_get_width(sharpdisplay_framebuffer_obj_t *self) {
+ return self->width;
+}
+
+int common_hal_sharpdisplay_framebuffer_get_height(sharpdisplay_framebuffer_obj_t *self) {
+ return self->height;
+}
+
+STATIC int common_hal_sharpdisplay_framebuffer_get_row_stride(sharpdisplay_framebuffer_obj_t *self) {
+ return (self->width + 7) / 8 + 2;
+}
+
+STATIC int common_hal_sharpdisplay_framebuffer_get_first_pixel_offset(sharpdisplay_framebuffer_obj_t *self) {
+ return 2;
+}
+
+STATIC bool common_hal_sharpdisplay_framebuffer_get_reverse_pixels_in_byte(sharpdisplay_framebuffer_obj_t *self) {
+ return true;
+}
+
+STATIC bool common_hal_sharpdisplay_framebuffer_get_pixels_in_byte_share_row(sharpdisplay_framebuffer_obj_t *self) {
+ return true;
+}
+
+void common_hal_sharpdisplay_framebuffer_reset(sharpdisplay_framebuffer_obj_t *self) {
+ if (self->bus != &self->inline_bus
+ #if CIRCUITPY_BOARD_SPI
+ && !common_hal_board_is_spi(self->bus)
+ #endif
+ ) {
+ memcpy(&self->inline_bus, self->bus, sizeof(busio_spi_obj_t));
+ self->bus = &self->inline_bus;
+ }
+}
+
+void common_hal_sharpdisplay_framebuffer_reconstruct(sharpdisplay_framebuffer_obj_t *self) {
+ // Look up the allocation by the old pointer and get the new pointer from it.
+ supervisor_allocation *alloc = allocation_from_ptr(self->bufinfo.buf);
+ self->bufinfo.buf = alloc ? alloc->ptr : NULL;
+}
+
+void common_hal_sharpdisplay_framebuffer_get_bufinfo(sharpdisplay_framebuffer_obj_t *self, mp_buffer_info_t *bufinfo) {
+ if (!self->bufinfo.buf) {
+ int row_stride = common_hal_sharpdisplay_framebuffer_get_row_stride(self);
+ int height = common_hal_sharpdisplay_framebuffer_get_height(self);
+ self->bufinfo.len = row_stride * height + 2;
+ supervisor_allocation *alloc = allocate_memory(align32_size(self->bufinfo.len), false, true);
+ if (alloc == NULL) {
+ m_malloc_fail(self->bufinfo.len);
+ }
+ self->bufinfo.buf = alloc->ptr;
+ memset(alloc->ptr, 0, self->bufinfo.len);
+
+ uint8_t *data = self->bufinfo.buf;
+ *data++ = SHARPMEM_BIT_WRITECMD_LSB;
+
+ for (int y = 0; y < height; y++) {
+ *data = bitrev(y + 1);
+ data += row_stride;
+ }
+ self->full_refresh = true;
+ }
+ if (bufinfo) {
+ *bufinfo = self->bufinfo;
+ }
+}
+
+void common_hal_sharpdisplay_framebuffer_deinit(sharpdisplay_framebuffer_obj_t *self) {
+ if (self->base.type != &sharpdisplay_framebuffer_type) {
+ return;
+ }
+
+ if (self->bus == &self->inline_bus) {
+ common_hal_busio_spi_deinit(self->bus);
+ }
+
+ common_hal_reset_pin(self->chip_select.pin);
+
+ free_memory(allocation_from_ptr(self->bufinfo.buf));
+
+ memset(self, 0, sizeof(*self));
+}
+
+void common_hal_sharpdisplay_framebuffer_construct(sharpdisplay_framebuffer_obj_t *self, busio_spi_obj_t *spi, const mcu_pin_obj_t *chip_select, int baudrate, int width, int height) {
+ common_hal_digitalio_digitalinout_construct(&self->chip_select, chip_select);
+ common_hal_digitalio_digitalinout_switch_to_output(&self->chip_select, true, DRIVE_MODE_PUSH_PULL);
+ common_hal_never_reset_pin(chip_select);
+
+ self->bus = spi;
+ common_hal_busio_spi_never_reset(self->bus);
+
+ self->width = width;
+ self->height = height;
+ self->baudrate = baudrate;
+
+ common_hal_sharpdisplay_framebuffer_get_bufinfo(self, NULL);
+}
+
+STATIC void common_hal_sharpdisplay_framebuffer_swapbuffers(sharpdisplay_framebuffer_obj_t *self, uint8_t *dirty_row_bitmask) {
+ // claim SPI bus
+ if (!common_hal_busio_spi_try_lock(self->bus)) {
+ return;
+ }
+ common_hal_busio_spi_configure(self->bus, self->baudrate, 0, 0, 8);
+
+ // set chip select high
+ common_hal_digitalio_digitalinout_set_value(&self->chip_select, true);
+
+ // output the toggling signal
+ uint8_t *data = self->bufinfo.buf;
+ data[0] ^= SHARPMEM_BIT_VCOM_LSB;
+
+ common_hal_busio_spi_write(self->bus, data++, 1);
+
+ // output each changed row
+ size_t row_stride = common_hal_sharpdisplay_framebuffer_get_row_stride(self);
+ for (int y = 0; y < self->height; y++) {
+ if (self->full_refresh || (dirty_row_bitmask[y / 8] & (1 << (y & 7)))) {
+ common_hal_busio_spi_write(self->bus, data, row_stride);
+ }
+ data += row_stride;
+ }
+
+ // output a trailing zero
+ common_hal_busio_spi_write(self->bus, data, 1);
+
+ // set chip select low
+ common_hal_digitalio_digitalinout_set_value(&self->chip_select, false);
+
+ // release SPI bus
+ common_hal_busio_spi_unlock(self->bus);
+
+ self->full_refresh = false;
+}
+
+STATIC void sharpdisplay_framebuffer_deinit(mp_obj_t self_in) {
+ sharpdisplay_framebuffer_obj_t *self = self_in;
+ common_hal_sharpdisplay_framebuffer_deinit(self);
+}
+
+STATIC void sharpdisplay_framebuffer_get_bufinfo(mp_obj_t self_in, mp_buffer_info_t *bufinfo) {
+ sharpdisplay_framebuffer_obj_t *self = self_in;
+ common_hal_sharpdisplay_framebuffer_get_bufinfo(self, bufinfo);
+}
+
+STATIC int sharpdisplay_framebuffer_get_color_depth(mp_obj_t self_in) {
+ return 1;
+}
+
+STATIC int sharpdisplay_framebuffer_get_height(mp_obj_t self_in) {
+ sharpdisplay_framebuffer_obj_t *self = self_in;
+ return common_hal_sharpdisplay_framebuffer_get_height(self);
+}
+
+STATIC int sharpdisplay_framebuffer_get_width(mp_obj_t self_in) {
+ sharpdisplay_framebuffer_obj_t *self = self_in;
+ return common_hal_sharpdisplay_framebuffer_get_width(self);
+}
+
+STATIC int sharpdisplay_framebuffer_get_first_pixel_offset(mp_obj_t self_in) {
+ sharpdisplay_framebuffer_obj_t *self = self_in;
+ return common_hal_sharpdisplay_framebuffer_get_first_pixel_offset(self);
+}
+
+STATIC bool sharpdisplay_framebuffer_get_pixels_in_byte_share_row(mp_obj_t self_in) {
+ sharpdisplay_framebuffer_obj_t *self = self_in;
+ return common_hal_sharpdisplay_framebuffer_get_pixels_in_byte_share_row(self);
+}
+
+STATIC bool sharpdisplay_framebuffer_get_reverse_pixels_in_byte(mp_obj_t self_in) {
+ sharpdisplay_framebuffer_obj_t *self = self_in;
+ return common_hal_sharpdisplay_framebuffer_get_reverse_pixels_in_byte(self);
+}
+
+STATIC int sharpdisplay_framebuffer_get_row_stride(mp_obj_t self_in) {
+ sharpdisplay_framebuffer_obj_t *self = self_in;
+ return common_hal_sharpdisplay_framebuffer_get_row_stride(self);
+}
+
+STATIC void sharpdisplay_framebuffer_swapbuffers(mp_obj_t self_in, uint8_t *dirty_row_bitmask) {
+ sharpdisplay_framebuffer_obj_t *self = self_in;
+ common_hal_sharpdisplay_framebuffer_swapbuffers(self, dirty_row_bitmask);
+}
+
+const framebuffer_p_t sharpdisplay_framebuffer_proto = {
+ MP_PROTO_IMPLEMENT(MP_QSTR_protocol_framebuffer)
+ .deinit = sharpdisplay_framebuffer_deinit,
+ .get_bufinfo = sharpdisplay_framebuffer_get_bufinfo,
+ .get_color_depth = sharpdisplay_framebuffer_get_color_depth,
+ .get_height = sharpdisplay_framebuffer_get_height,
+ .get_width = sharpdisplay_framebuffer_get_width,
+ .swapbuffers = sharpdisplay_framebuffer_swapbuffers,
+
+ .get_first_pixel_offset = sharpdisplay_framebuffer_get_first_pixel_offset,
+ .get_pixels_in_byte_share_row = sharpdisplay_framebuffer_get_pixels_in_byte_share_row,
+ .get_reverse_pixels_in_byte = sharpdisplay_framebuffer_get_reverse_pixels_in_byte,
+ .get_row_stride = sharpdisplay_framebuffer_get_row_stride,
+};
+
+void common_hal_sharpdisplay_framebuffer_collect_ptrs(sharpdisplay_framebuffer_obj_t *self) {
+ gc_collect_ptr(self->bus);
+}
diff --git a/circuitpython/shared-module/sharpdisplay/SharpMemoryFramebuffer.h b/circuitpython/shared-module/sharpdisplay/SharpMemoryFramebuffer.h
new file mode 100644
index 0000000..abc951b
--- /dev/null
+++ b/circuitpython/shared-module/sharpdisplay/SharpMemoryFramebuffer.h
@@ -0,0 +1,59 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Jeff Epler 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.
+ */
+
+#pragma once
+
+#include "py/obj.h"
+#include "shared-bindings/busio/SPI.h"
+#include "shared-bindings/digitalio/DigitalInOut.h"
+#include "shared-module/framebufferio/FramebufferDisplay.h"
+
+typedef struct {
+ mp_obj_base_t base;
+ busio_spi_obj_t *bus;
+ busio_spi_obj_t inline_bus;
+ digitalio_digitalinout_obj_t chip_select;
+ mp_buffer_info_t bufinfo;
+
+ uint16_t width, height;
+ uint32_t baudrate;
+
+ bool full_refresh : 1;
+} sharpdisplay_framebuffer_obj_t;
+
+void common_hal_sharpdisplay_framebuffer_construct(sharpdisplay_framebuffer_obj_t *self, busio_spi_obj_t *spi, const mcu_pin_obj_t *chip_select, int baudrate, int width, int height);
+void common_hal_sharpdisplay_framebuffer_swap_buffers(sharpdisplay_framebuffer_obj_t *self, uint8_t *dirty_row_bitmask);
+void common_hal_sharpdisplay_framebuffer_deinit(sharpdisplay_framebuffer_obj_t *self);
+void common_hal_sharpdisplay_framebuffer_get_bufinfo(sharpdisplay_framebuffer_obj_t *self, mp_buffer_info_t *bufinfo);
+int common_hal_sharpdisplay_framebuffer_get_height(sharpdisplay_framebuffer_obj_t *self);
+int common_hal_sharpdisplay_framebuffer_get_width(sharpdisplay_framebuffer_obj_t *self);
+void common_hal_sharpdisplay_framebuffer_swap_buffers(sharpdisplay_framebuffer_obj_t *self, uint8_t *dirty_row_bitmask);
+void common_hal_sharpdisplay_framebuffer_reset(sharpdisplay_framebuffer_obj_t *self);
+void common_hal_sharpdisplay_framebuffer_reconstruct(sharpdisplay_framebuffer_obj_t *self);
+
+extern const framebuffer_p_t sharpdisplay_framebuffer_proto;
+
+void common_hal_sharpdisplay_framebuffer_collect_ptrs(sharpdisplay_framebuffer_obj_t *);
diff --git a/circuitpython/shared-module/sharpdisplay/__init__.c b/circuitpython/shared-module/sharpdisplay/__init__.c
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/circuitpython/shared-module/sharpdisplay/__init__.c
diff --git a/circuitpython/shared-module/sharpdisplay/__init__.h b/circuitpython/shared-module/sharpdisplay/__init__.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/circuitpython/shared-module/sharpdisplay/__init__.h
diff --git a/circuitpython/shared-module/storage/__init__.c b/circuitpython/shared-module/storage/__init__.c
new file mode 100644
index 0000000..e36d3ec
--- /dev/null
+++ b/circuitpython/shared-module/storage/__init__.c
@@ -0,0 +1,277 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * SPDX-FileCopyrightText: Copyright (c) 2013, 2014 Damien P. George
+ * Copyright (c) 2015 Josef Gajdusek
+ * Copyright (c) 2017 Scott Shawcroft 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.
+ */
+
+#include <string.h>
+
+#include "extmod/vfs.h"
+#include "py/mperrno.h"
+#include "py/mphal.h"
+#include "py/obj.h"
+#include "py/runtime.h"
+#include "shared-bindings/microcontroller/__init__.h"
+#include "shared-bindings/os/__init__.h"
+#include "shared-bindings/storage/__init__.h"
+#include "supervisor/filesystem.h"
+#include "supervisor/flash.h"
+#include "supervisor/usb.h"
+
+#if CIRCUITPY_USB_MSC
+#include "tusb.h"
+
+static const uint8_t usb_msc_descriptor_template[] = {
+ // MSC Interface Descriptor
+ 0x09, // 0 bLength
+ 0x04, // 1 bDescriptorType (Interface)
+ 0xFF, // 2 bInterfaceNumber [SET AT RUNTIME]
+#define MSC_INTERFACE_INDEX (2)
+ 0x00, // 3 bAlternateSetting
+ 0x02, // 4 bNumEndpoints 2
+ 0x08, // 5 bInterfaceClass: MSC
+ 0x06, // 6 bInterfaceSubClass: TRANSPARENT
+ 0x50, // 7 bInterfaceProtocol: BULK
+ 0xFF, // 8 iInterface (String Index) [SET AT RUNTIME]
+#define MSC_INTERFACE_STRING_INDEX (8)
+
+ // MSC Endpoint IN Descriptor
+ 0x07, // 9 bLength
+ 0x05, // 10 bDescriptorType (Endpoint)
+ 0xFF, // 11 bEndpointAddress (IN/D2H) [SET AT RUNTIME: 0x80 | number]
+#define MSC_IN_ENDPOINT_INDEX (11)
+ 0x02, // 12 bmAttributes (Bulk)
+ #if USB_HIGHSPEED
+ 0x00, 0x02, // 13,14 wMaxPacketSize 512
+ #else
+ 0x40, 0x00, // 13,14 wMaxPacketSize 64
+ #endif
+ 0x00, // 15 bInterval 0 (unit depends on device speed)
+
+ // MSC Endpoint OUT Descriptor
+ 0x07, // 16 bLength
+ 0x05, // 17 bDescriptorType (Endpoint)
+ 0xFF, // 18 bEndpointAddress (OUT/H2D) [SET AT RUNTIME]
+#define MSC_OUT_ENDPOINT_INDEX (18)
+ 0x02, // 19 bmAttributes (Bulk)
+ #if USB_HIGHSPEED
+ 0x00, 0x02, // 20,21 wMaxPacketSize 512
+ #else
+ 0x40, 0x00, // 20,21 wMaxPacketSize 64
+ #endif
+ 0x00, // 22 bInterval 0 (unit depends on device speed)
+};
+
+// Is the MSC device enabled?
+bool storage_usb_is_enabled;
+
+void storage_usb_set_defaults(void) {
+ storage_usb_is_enabled = CIRCUITPY_USB_MSC_ENABLED_DEFAULT;
+}
+
+bool storage_usb_enabled(void) {
+ return storage_usb_is_enabled;
+}
+
+size_t storage_usb_descriptor_length(void) {
+ return sizeof(usb_msc_descriptor_template);
+}
+
+static const char storage_interface_name[] = USB_INTERFACE_NAME " Mass Storage";
+
+size_t storage_usb_add_descriptor(uint8_t *descriptor_buf, descriptor_counts_t *descriptor_counts, uint8_t *current_interface_string) {
+ memcpy(descriptor_buf, usb_msc_descriptor_template, sizeof(usb_msc_descriptor_template));
+ descriptor_buf[MSC_INTERFACE_INDEX] = descriptor_counts->current_interface;
+ descriptor_counts->current_interface++;
+
+ descriptor_buf[MSC_IN_ENDPOINT_INDEX] =
+ 0x80 | (USB_MSC_EP_NUM_IN ? USB_MSC_EP_NUM_IN : descriptor_counts->current_endpoint);
+ descriptor_counts->num_in_endpoints++;
+ descriptor_buf[MSC_OUT_ENDPOINT_INDEX] =
+ USB_MSC_EP_NUM_OUT ? USB_MSC_EP_NUM_OUT : descriptor_counts->current_endpoint;
+ descriptor_counts->num_out_endpoints++;
+ descriptor_counts->current_endpoint++;
+
+ usb_add_interface_string(*current_interface_string, storage_interface_name);
+ descriptor_buf[MSC_INTERFACE_STRING_INDEX] = *current_interface_string;
+ (*current_interface_string)++;
+
+ return sizeof(usb_msc_descriptor_template);
+}
+
+static bool usb_drive_set_enabled(bool enabled) {
+ // We can't change the descriptors once we're connected.
+ if (tud_connected()) {
+ return false;
+ }
+ storage_usb_is_enabled = enabled;
+ return true;
+}
+
+bool common_hal_storage_disable_usb_drive(void) {
+ return usb_drive_set_enabled(false);
+}
+
+bool common_hal_storage_enable_usb_drive(void) {
+ return usb_drive_set_enabled(true);
+}
+#else
+bool common_hal_storage_disable_usb_drive(void) {
+ return false;
+}
+
+bool common_hal_storage_enable_usb_drive(void) {
+ return false;
+}
+#endif // CIRCUITPY_USB_MSC
+
+STATIC mp_obj_t mp_vfs_proxy_call(mp_vfs_mount_t *vfs, qstr meth_name, size_t n_args, const mp_obj_t *args) {
+ if (vfs == MP_VFS_NONE) {
+ // mount point not found
+ mp_raise_OSError(MP_ENODEV);
+ }
+ if (vfs == MP_VFS_ROOT) {
+ // can't do operation on root dir
+ mp_raise_OSError(MP_EPERM);
+ }
+ mp_obj_t meth[n_args + 2];
+ mp_load_method(vfs->obj, meth_name, meth);
+ if (args != NULL) {
+ memcpy(meth + 2, args, n_args * sizeof(*args));
+ }
+ return mp_call_method_n_kw(n_args, 0, meth);
+}
+
+void common_hal_storage_mount(mp_obj_t vfs_obj, const char *mount_path, bool readonly) {
+ // create new object
+ mp_vfs_mount_t *vfs = m_new_obj(mp_vfs_mount_t);
+ vfs->str = mount_path;
+ vfs->len = strlen(mount_path);
+ vfs->obj = vfs_obj;
+ vfs->next = NULL;
+
+ mp_obj_t args[2];
+ args[0] = readonly ? mp_const_true : mp_const_false;
+ args[1] = mp_const_false; // Don't make the file system automatically when mounting.
+
+ // Check that there's no file or directory with the same name as the mount point.
+ // But it's ok to mount '/' in any case.
+ if (strcmp(vfs->str, "/") != 0) {
+ nlr_buf_t nlr;
+ if (nlr_push(&nlr) == 0) {
+ common_hal_os_stat(mount_path);
+ nlr_pop();
+ // Something with the same name exists.
+ mp_raise_OSError(MP_EEXIST);
+ }
+ }
+
+ // check that the destination mount point is unused
+ const char *path_out;
+ mp_vfs_mount_t *existing_mount = mp_vfs_lookup_path(mount_path, &path_out);
+ if (existing_mount != MP_VFS_NONE && existing_mount != MP_VFS_ROOT) {
+ if (vfs->len != 1 && existing_mount->len == 1) {
+ // if root dir is mounted, still allow to mount something within a subdir of root
+ } else {
+ // mount point in use
+ mp_raise_OSError(MP_EPERM);
+ }
+ }
+
+ // call the underlying object to do any mounting operation
+ mp_vfs_proxy_call(vfs, MP_QSTR_mount, 2, (mp_obj_t *)&args);
+
+ // Insert the vfs into the mount table by pushing it onto the front of the
+ // mount table.
+ mp_vfs_mount_t **vfsp = &MP_STATE_VM(vfs_mount_table);
+ vfs->next = *vfsp;
+ *vfsp = vfs;
+}
+
+void common_hal_storage_umount_object(mp_obj_t vfs_obj) {
+ // remove vfs from the mount table
+ mp_vfs_mount_t *vfs = NULL;
+ for (mp_vfs_mount_t **vfsp = &MP_STATE_VM(vfs_mount_table); *vfsp != NULL; vfsp = &(*vfsp)->next) {
+ if ((*vfsp)->obj == vfs_obj) {
+ vfs = *vfsp;
+ *vfsp = (*vfsp)->next;
+ break;
+ }
+ }
+
+ if (vfs == NULL) {
+ mp_raise_OSError(MP_EINVAL);
+ }
+
+ // if we unmounted the current device then set current to root
+ if (MP_STATE_VM(vfs_cur) == vfs) {
+ MP_STATE_VM(vfs_cur) = MP_VFS_ROOT;
+ }
+
+ // call the underlying object to do any unmounting operation
+ mp_vfs_proxy_call(vfs, MP_QSTR_umount, 0, NULL);
+}
+
+STATIC mp_obj_t storage_object_from_path(const char *mount_path) {
+ for (mp_vfs_mount_t **vfsp = &MP_STATE_VM(vfs_mount_table); *vfsp != NULL; vfsp = &(*vfsp)->next) {
+ if (strcmp(mount_path, (*vfsp)->str) == 0) {
+ return (*vfsp)->obj;
+ }
+ }
+ mp_raise_OSError(MP_EINVAL);
+}
+
+void common_hal_storage_umount_path(const char *mount_path) {
+ common_hal_storage_umount_object(storage_object_from_path(mount_path));
+}
+
+mp_obj_t common_hal_storage_getmount(const char *mount_path) {
+ return storage_object_from_path(mount_path);
+}
+
+void common_hal_storage_remount(const char *mount_path, bool readonly, bool disable_concurrent_write_protection) {
+ if (strcmp(mount_path, "/") != 0) {
+ mp_raise_OSError(MP_EINVAL);
+ }
+
+ #if CIRCUITPY_USB_MSC
+ if (!usb_msc_ejected() && storage_usb_is_enabled) {
+ mp_raise_RuntimeError(translate("Cannot remount '/' when visible via USB."));
+ }
+ #endif
+
+ filesystem_set_internal_writable_by_usb(readonly);
+ filesystem_set_internal_concurrent_write_protection(!disable_concurrent_write_protection);
+}
+
+void common_hal_storage_erase_filesystem(void) {
+ #if CIRCUITPY_USB
+ usb_disconnect();
+ #endif
+ mp_hal_delay_ms(1000);
+ (void)filesystem_init(false, true); // Force a re-format. Ignore failure.
+ common_hal_mcu_reset();
+ // We won't actually get here, since we're resetting.
+}
diff --git a/circuitpython/shared-module/storage/__init__.h b/circuitpython/shared-module/storage/__init__.h
new file mode 100644
index 0000000..a07e2c2
--- /dev/null
+++ b/circuitpython/shared-module/storage/__init__.h
@@ -0,0 +1,40 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 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.
+ */
+
+#ifndef SHARED_MODULE_STORAGE___INIT___H
+#define SHARED_MODULE_STORAGE___INIT___H
+
+#include "py/mpconfig.h"
+#include "supervisor/usb.h"
+
+#if CIRCUITPY_USB
+bool storage_usb_enabled(void);
+void storage_usb_set_defaults(void);
+size_t storage_usb_descriptor_length(void);
+size_t storage_usb_add_descriptor(uint8_t *descriptor_buf, descriptor_counts_t *descriptor_counts, uint8_t *current_interface_string);
+#endif
+
+#endif // SHARED_MODULE_STORAGE___INIT___H
diff --git a/circuitpython/shared-module/struct/__init__.c b/circuitpython/shared-module/struct/__init__.c
new file mode 100644
index 0000000..a0abd16
--- /dev/null
+++ b/circuitpython/shared-module/struct/__init__.c
@@ -0,0 +1,218 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Paul Sokolovsky
+ * Copyright (c) 2017 Scott Shawcroft for Adafruit Industries
+ * Copyright (c) 2017 Michael McWethy
+ *
+ * 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.
+ */
+#include <assert.h>
+#include <string.h>
+
+#include "py/runtime.h"
+#include "py/binary.h"
+#include "py/parsenum.h"
+#include "supervisor/shared/translate.h"
+#include "shared-bindings/struct/__init__.h"
+
+STATIC void struct_validate_format(char fmt) {
+ #if MICROPY_NONSTANDARD_TYPECODES
+ if (fmt == 'S' || fmt == 'O') {
+ mp_raise_RuntimeError(translate("'S' and 'O' are not supported format types"));
+ }
+ #endif
+}
+
+STATIC char get_fmt_type(const char **fmt) {
+ char t = **fmt;
+ switch (t) {
+ case '!':
+ t = '>';
+ break;
+ case '@':
+ case '=':
+ case '<':
+ case '>':
+ break;
+ default:
+ return '@';
+ }
+ // Skip type char
+ (*fmt)++;
+ return t;
+}
+
+STATIC mp_uint_t get_fmt_num(const char **p) {
+ const char *num = *p;
+ uint len = 1;
+ while (unichar_isdigit(*++num)) {
+ len++;
+ }
+ mp_uint_t val = (mp_uint_t)MP_OBJ_SMALL_INT_VALUE(mp_parse_num_integer(*p, len, 10, NULL));
+ *p = num;
+ return val;
+}
+
+STATIC mp_uint_t calcsize_items(const char *fmt) {
+ mp_uint_t cnt = 0;
+ while (*fmt) {
+ int num = 1;
+ if (unichar_isdigit(*fmt)) {
+ num = get_fmt_num(&fmt);
+ if (*fmt == 's') {
+ num = 1;
+ }
+ }
+ // Pad bytes are skipped and don't get included in the item count.
+ if (*fmt != 'x') {
+ cnt += num;
+ }
+ fmt++;
+ }
+ return cnt;
+}
+
+mp_uint_t shared_modules_struct_calcsize(mp_obj_t fmt_in) {
+ const char *fmt = mp_obj_str_get_str(fmt_in);
+ char fmt_type = get_fmt_type(&fmt);
+
+ mp_uint_t size;
+ for (size = 0; *fmt; fmt++) {
+
+ struct_validate_format(*fmt);
+
+ mp_uint_t cnt = 1;
+ if (unichar_isdigit(*fmt)) {
+ cnt = get_fmt_num(&fmt);
+ }
+
+ if (*fmt == 's') {
+ size += cnt;
+ } else {
+ mp_uint_t align;
+ size_t sz = mp_binary_get_size(fmt_type, *fmt, &align);
+ while (cnt--) {
+ // Apply alignment
+ size = (size + align - 1) & ~(align - 1);
+ size += sz;
+ }
+ }
+ }
+ return size;
+}
+
+void shared_modules_struct_pack_into(mp_obj_t fmt_in, byte *p, byte *end_p, size_t n_args, const mp_obj_t *args) {
+ const char *fmt = mp_obj_str_get_str(fmt_in);
+ char fmt_type = get_fmt_type(&fmt);
+ const mp_uint_t total_sz = shared_modules_struct_calcsize(fmt_in);
+
+ if (p + total_sz > end_p) {
+ mp_raise_RuntimeError(translate("buffer too small"));
+ }
+
+ size_t i;
+ byte *p_base = p;
+ for (i = 0; i < n_args;) {
+ mp_uint_t sz = 1;
+ if (*fmt == '\0') {
+ // more arguments given than used by format string; CPython raises struct.error here
+ mp_raise_RuntimeError(translate("too many arguments provided with the given format"));
+ }
+ struct_validate_format(*fmt);
+
+ if (unichar_isdigit(*fmt)) {
+ sz = get_fmt_num(&fmt);
+ }
+
+ if (*fmt == 's') {
+ mp_buffer_info_t bufinfo;
+ mp_get_buffer_raise(args[i++], &bufinfo, MP_BUFFER_READ);
+ mp_uint_t to_copy = sz;
+ if (bufinfo.len < to_copy) {
+ to_copy = bufinfo.len;
+ }
+ memcpy(p, bufinfo.buf, to_copy);
+ memset(p + to_copy, 0, sz - to_copy);
+ p += sz;
+ } else {
+ while (sz--) {
+ // Pad bytes don't have a corresponding argument.
+ if (*fmt == 'x') {
+ mp_binary_set_val(fmt_type, *fmt, MP_OBJ_NEW_SMALL_INT(0), p_base, &p);
+ } else {
+ mp_binary_set_val(fmt_type, *fmt, args[i], p_base, &p);
+ i++;
+ }
+ }
+ }
+ fmt++;
+ }
+}
+
+mp_obj_tuple_t *shared_modules_struct_unpack_from(mp_obj_t fmt_in, byte *p, byte *end_p, bool exact_size) {
+
+ const char *fmt = mp_obj_str_get_str(fmt_in);
+ char fmt_type = get_fmt_type(&fmt);
+ const mp_uint_t num_items = calcsize_items(fmt);
+ const mp_uint_t total_sz = shared_modules_struct_calcsize(fmt_in);
+ mp_obj_tuple_t *res = MP_OBJ_TO_PTR(mp_obj_new_tuple(num_items, NULL));
+
+ // If exact_size, make sure the buffer is exactly the right size.
+ // Otherwise just make sure it's big enough.
+ if (exact_size) {
+ if (p + total_sz != end_p) {
+ mp_raise_RuntimeError(translate("buffer size must match format"));
+ }
+ } else {
+ if (p + total_sz > end_p) {
+ mp_raise_RuntimeError(translate("buffer too small"));
+ }
+ }
+
+ byte *p_base = p;
+ for (uint i = 0; i < num_items;) {
+ mp_uint_t sz = 1;
+
+ struct_validate_format(*fmt);
+
+ if (unichar_isdigit(*fmt)) {
+ sz = get_fmt_num(&fmt);
+ }
+ mp_obj_t item;
+ if (*fmt == 's') {
+ item = mp_obj_new_bytes(p, sz);
+ p += sz;
+ res->items[i++] = item;
+ } else {
+ while (sz--) {
+ item = mp_binary_get_val(fmt_type, *fmt, p_base, &p);
+ // Pad bytes are not stored.
+ if (*fmt != 'x') {
+ res->items[i++] = item;
+ }
+ }
+ }
+ fmt++;
+ }
+ return res;
+
+}
diff --git a/circuitpython/shared-module/struct/__init__.h b/circuitpython/shared-module/struct/__init__.h
new file mode 100644
index 0000000..637080b
--- /dev/null
+++ b/circuitpython/shared-module/struct/__init__.h
@@ -0,0 +1,34 @@
+
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2017 Scott Shawcroft 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.
+ */
+#ifndef MICROPY_INCLUDED_SHARED_MODULE_STRUCT___INIT___H
+#define MICROPY_INCLUDED_SHARED_MODULE_STRUCT___INIT___H
+
+char get_fmt_type(const char **fmt);
+mp_uint_t get_fmt_num(const char **p);
+mp_uint_t calcsize_items(const char *fmt);
+
+#endif
diff --git a/circuitpython/shared-module/synthio/MidiTrack.c b/circuitpython/shared-module/synthio/MidiTrack.c
new file mode 100644
index 0000000..b2693a0
--- /dev/null
+++ b/circuitpython/shared-module/synthio/MidiTrack.c
@@ -0,0 +1,212 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 Artyom Skrobov
+ *
+ * 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.
+ */
+
+#include "py/runtime.h"
+#include "shared-bindings/synthio/MidiTrack.h"
+
+#define LOUDNESS 0x4000 // 0.5
+#define BITS_PER_SAMPLE 16
+#define BYTES_PER_SAMPLE (BITS_PER_SAMPLE / 8)
+#define SILENCE 0x80
+
+STATIC uint8_t parse_note(const uint8_t *buffer, uint32_t len, uint32_t *pos) {
+ if (*pos + 1 >= len) {
+ mp_raise_ValueError_varg(translate("Error in MIDI stream at position %d"), *pos);
+ }
+ uint8_t note = buffer[(*pos)++];
+ if (note > 127 || buffer[(*pos)++] > 127) {
+ mp_raise_ValueError_varg(translate("Error in MIDI stream at position %d"), *pos);
+ }
+ return note;
+}
+
+STATIC void terminate_span(synthio_miditrack_obj_t *self, uint16_t *dur, uint16_t *max_dur) {
+ if (*dur) {
+ self->track[self->total_spans - 1].dur = *dur;
+ if (*dur > *max_dur) {
+ *max_dur = *dur;
+ }
+ *dur = 0;
+ } else {
+ self->total_spans--;
+ }
+}
+
+STATIC void add_span(synthio_miditrack_obj_t *self, uint8_t note1, uint8_t note2) {
+ synthio_midi_span_t span = { 0, {note1, note2} };
+ self->track = m_realloc(self->track,
+ (self->total_spans + 1) * sizeof(synthio_midi_span_t));
+ self->track[self->total_spans++] = span;
+}
+
+void common_hal_synthio_miditrack_construct(synthio_miditrack_obj_t *self,
+ const uint8_t *buffer, uint32_t len, uint32_t tempo, uint32_t sample_rate) {
+
+ synthio_midi_span_t initial = { 0, {SILENCE, SILENCE} };
+ self->sample_rate = sample_rate;
+ self->track = m_malloc(sizeof(synthio_midi_span_t), false);
+ self->next_span = 0;
+ self->total_spans = 1;
+ *self->track = initial;
+
+ uint16_t dur = 0, max_dur = 0;
+ uint32_t pos = 0;
+ while (pos < len) {
+ uint8_t c;
+ uint32_t delta = 0;
+ do {
+ c = buffer[pos++];
+ delta <<= 7;
+ delta |= c & 0x7f;
+ } while ((c & 0x80) && (pos < len));
+
+ if (c & 0x80) {
+ mp_raise_ValueError_varg(translate("Error in MIDI stream at position %d"), pos);
+ }
+
+ dur += delta * sample_rate / tempo;
+
+ switch (buffer[pos++] >> 4) {
+ case 8: { // Note Off
+ uint8_t note = parse_note(buffer, len, &pos);
+
+ // Ignore if not a note which is playing
+ synthio_midi_span_t last_span = self->track[self->total_spans - 1];
+ if (last_span.note[0] == note || last_span.note[1] == note) {
+ terminate_span(self, &dur, &max_dur);
+ if (last_span.note[0] == note) {
+ add_span(self, last_span.note[1], SILENCE);
+ } else {
+ add_span(self, last_span.note[0], SILENCE);
+ }
+ }
+ break;
+ }
+ case 9: { // Note On
+ uint8_t note = parse_note(buffer, len, &pos);
+
+ // Ignore if two notes are already playing
+ synthio_midi_span_t last_span = self->track[self->total_spans - 1];
+ if (last_span.note[1] == SILENCE) {
+ terminate_span(self, &dur, &max_dur);
+ if (last_span.note[0] == SILENCE) {
+ add_span(self, note, SILENCE);
+ } else {
+ add_span(self, last_span.note[0], note);
+ }
+ }
+ break;
+ }
+ case 10:
+ case 11:
+ case 14: // two data bytes to ignore
+ parse_note(buffer, len, &pos);
+ break;
+ case 12:
+ case 13: // one data byte to ignore
+ if (pos >= len || buffer[pos++] > 127) {
+ mp_raise_ValueError_varg(translate("Error in MIDI stream at position %d"), pos);
+ }
+ break;
+ case 15: // the full syntax is too complicated, just assume it's "End of Track" event
+ pos = len;
+ break;
+ default: // invalid event
+ mp_raise_ValueError_varg(translate("Error in MIDI stream at position %d"), pos);
+ }
+ }
+ terminate_span(self, &dur, &max_dur);
+
+ self->buffer_length = max_dur * BYTES_PER_SAMPLE;
+ self->buffer = m_malloc(self->buffer_length, false);
+}
+
+void common_hal_synthio_miditrack_deinit(synthio_miditrack_obj_t *self) {
+ m_free(self->buffer);
+ self->buffer = NULL;
+ m_free(self->track);
+ self->track = NULL;
+}
+bool common_hal_synthio_miditrack_deinited(synthio_miditrack_obj_t *self) {
+ return self->buffer == NULL;
+}
+
+uint32_t common_hal_synthio_miditrack_get_sample_rate(synthio_miditrack_obj_t *self) {
+ return self->sample_rate;
+}
+uint8_t common_hal_synthio_miditrack_get_bits_per_sample(synthio_miditrack_obj_t *self) {
+ return BITS_PER_SAMPLE;
+}
+uint8_t common_hal_synthio_miditrack_get_channel_count(synthio_miditrack_obj_t *self) {
+ return 1;
+}
+
+void synthio_miditrack_reset_buffer(synthio_miditrack_obj_t *self,
+ bool single_channel_output, uint8_t channel) {
+
+ self->next_span = 0;
+}
+
+STATIC const uint16_t notes[] = {8372, 8870, 9397, 9956, 10548, 11175, 11840,
+ 12544, 13290, 14080, 14917, 15804}; // 9th octave
+
+audioio_get_buffer_result_t synthio_miditrack_get_buffer(synthio_miditrack_obj_t *self,
+ bool single_channel_output, uint8_t channel, uint8_t **buffer, uint32_t *buffer_length) {
+
+ if (self->next_span >= self->total_spans) {
+ *buffer_length = 0;
+ return GET_BUFFER_DONE;
+ }
+
+ synthio_midi_span_t span = self->track[self->next_span++];
+ *buffer_length = span.dur * BYTES_PER_SAMPLE;
+ uint8_t octave1 = span.note[0] / 12; // 0..10
+ uint8_t octave2 = span.note[1] / 12; // 0..10
+ int32_t base_freq1 = notes[span.note[0] % 12];
+ int32_t base_freq2 = notes[span.note[1] % 12];
+ int32_t sample_rate = self->sample_rate;
+
+ for (uint16_t i = 0; i < span.dur; i++) {
+ int16_t semiperiod1 = span.note[0] == SILENCE ? 0 :
+ ((base_freq1 * i * 2) / sample_rate) >> (10 - octave1);
+ int16_t semiperiod2 = span.note[1] == SILENCE ? semiperiod1 :
+ ((base_freq2 * i * 2) / sample_rate) >> (10 - octave2);
+ self->buffer[i] = ((semiperiod1 % 2 + semiperiod2 % 2) - 1) * LOUDNESS;
+ }
+ *buffer = (uint8_t *)self->buffer;
+
+ return self->next_span >= self->total_spans ?
+ GET_BUFFER_DONE : GET_BUFFER_MORE_DATA;
+}
+
+void synthio_miditrack_get_buffer_structure(synthio_miditrack_obj_t *self, bool single_channel_output,
+ bool *single_buffer, bool *samples_signed, uint32_t *max_buffer_length, uint8_t *spacing) {
+
+ *single_buffer = true;
+ *samples_signed = true;
+ *max_buffer_length = self->buffer_length;
+ *spacing = 1;
+}
diff --git a/circuitpython/shared-module/synthio/MidiTrack.h b/circuitpython/shared-module/synthio/MidiTrack.h
new file mode 100644
index 0000000..0af174e
--- /dev/null
+++ b/circuitpython/shared-module/synthio/MidiTrack.h
@@ -0,0 +1,65 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 Artyom Skrobov
+ *
+ * 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.
+ */
+
+#ifndef MICROPY_INCLUDED_SHARED_MODULE_SYNTHIO_MIDITRACK_H
+#define MICROPY_INCLUDED_SHARED_MODULE_SYNTHIO_MIDITRACK_H
+
+#include "py/obj.h"
+
+#include "shared-module/synthio/__init__.h"
+
+typedef struct {
+ uint16_t dur;
+ uint8_t note[2];
+} synthio_midi_span_t;
+
+typedef struct {
+ mp_obj_base_t base;
+ uint32_t sample_rate;
+ uint16_t *buffer;
+ uint16_t buffer_length;
+ synthio_midi_span_t *track;
+ uint16_t next_span;
+ uint16_t total_spans;
+} synthio_miditrack_obj_t;
+
+
+// These are not available from Python because it may be called in an interrupt.
+void synthio_miditrack_reset_buffer(synthio_miditrack_obj_t *self,
+ bool single_channel_output,
+ uint8_t channel);
+
+audioio_get_buffer_result_t synthio_miditrack_get_buffer(synthio_miditrack_obj_t *self,
+ bool single_channel_output,
+ uint8_t channel,
+ uint8_t **buffer,
+ uint32_t *buffer_length); // length in bytes
+
+void synthio_miditrack_get_buffer_structure(synthio_miditrack_obj_t *self, bool single_channel_output,
+ bool *single_buffer, bool *samples_signed,
+ uint32_t *max_buffer_length, uint8_t *spacing);
+
+#endif // MICROPY_INCLUDED_SHARED_MODULE_SYNTHIO_MIDITRACK_H
diff --git a/circuitpython/shared-module/synthio/__init__.c b/circuitpython/shared-module/synthio/__init__.c
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/circuitpython/shared-module/synthio/__init__.c
diff --git a/circuitpython/shared-module/synthio/__init__.h b/circuitpython/shared-module/synthio/__init__.h
new file mode 100644
index 0000000..d9d98a5
--- /dev/null
+++ b/circuitpython/shared-module/synthio/__init__.h
@@ -0,0 +1,32 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 Artyom Skrobov
+ *
+ * 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.
+ */
+
+#ifndef MICROPY_INCLUDED_SHARED_MODULE_SYNTHIO__INIT__H
+#define MICROPY_INCLUDED_SHARED_MODULE_SYNTHIO__INIT__H
+
+#include "shared-module/audiocore/__init__.h"
+
+#endif // MICROPY_INCLUDED_SHARED_MODULE_SYNTHIO__INIT__H
diff --git a/circuitpython/shared-module/terminalio/Terminal.c b/circuitpython/shared-module/terminalio/Terminal.c
new file mode 100644
index 0000000..fc33533
--- /dev/null
+++ b/circuitpython/shared-module/terminalio/Terminal.c
@@ -0,0 +1,177 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Scott Shawcroft 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.
+ */
+
+#include "shared-module/terminalio/Terminal.h"
+
+#include "shared-module/fontio/BuiltinFont.h"
+#include "shared-bindings/displayio/TileGrid.h"
+#include "shared-bindings/terminalio/Terminal.h"
+
+void common_hal_terminalio_terminal_construct(terminalio_terminal_obj_t *self, displayio_tilegrid_t *tilegrid, const fontio_builtinfont_t *font) {
+ self->cursor_x = 0;
+ self->cursor_y = 0;
+ self->font = font;
+ self->tilegrid = tilegrid;
+ self->first_row = 0;
+ for (uint16_t x = 0; x < self->tilegrid->width_in_tiles; x++) {
+ for (uint16_t y = 0; y < self->tilegrid->height_in_tiles; y++) {
+ common_hal_displayio_tilegrid_set_tile(self->tilegrid, x, y, 0);
+ }
+ }
+
+ common_hal_displayio_tilegrid_set_top_left(self->tilegrid, 0, 1);
+}
+
+size_t common_hal_terminalio_terminal_write(terminalio_terminal_obj_t *self, const byte *data, size_t len, int *errcode) {
+ // Make sure the terminal is initialized before we do anything with it.
+ if (self->tilegrid == NULL) {
+ return len;
+ }
+ const byte *i = data;
+ uint16_t start_y = self->cursor_y;
+ while (i < data + len) {
+ unichar c = utf8_get_char(i);
+ i = utf8_next_char(i);
+ // Always handle ASCII.
+ if (c < 128) {
+ if (c >= 0x20 && c <= 0x7e) {
+ uint8_t tile_index = fontio_builtinfont_get_glyph_index(self->font, c);
+ common_hal_displayio_tilegrid_set_tile(self->tilegrid, self->cursor_x, self->cursor_y, tile_index);
+ self->cursor_x++;
+ } else if (c == '\r') {
+ self->cursor_x = 0;
+ } else if (c == '\n') {
+ self->cursor_y++;
+ // Commands below are used by MicroPython in the REPL
+ } else if (c == '\b') {
+ if (self->cursor_x > 0) {
+ self->cursor_x--;
+ }
+ } else if (c == 0x1b) {
+ if (i[0] == '[') {
+ if (i[1] == 'K') {
+ // Clear the rest of the line.
+ for (uint16_t j = self->cursor_x; j < self->tilegrid->width_in_tiles; j++) {
+ common_hal_displayio_tilegrid_set_tile(self->tilegrid, j, self->cursor_y, 0);
+ }
+ i += 2;
+ } else {
+ // Handle commands of the form \x1b[####D
+ uint16_t n = 0;
+ uint8_t j = 1;
+ for (; j < 6; j++) {
+ if ('0' <= i[j] && i[j] <= '9') {
+ n = n * 10 + (i[j] - '0');
+ } else {
+ c = i[j];
+ break;
+ }
+ }
+ if (c == 'D') {
+ if (n > self->cursor_x) {
+ self->cursor_x = 0;
+ } else {
+ self->cursor_x -= n;
+ }
+ }
+ if (c == 'J') {
+ if (n == 2) {
+ common_hal_displayio_tilegrid_set_top_left(self->tilegrid, 0, 0);
+ self->cursor_x = self->cursor_y = start_y = 0;
+ n = 0;
+ for (uint16_t x = 0; x < self->tilegrid->width_in_tiles; x++) {
+ for (uint16_t y = 0; y < self->tilegrid->height_in_tiles; y++) {
+ common_hal_displayio_tilegrid_set_tile(self->tilegrid, x, y, 0);
+ }
+ }
+ }
+ }
+ if (c == ';') {
+ uint16_t m = 0;
+ for (++j; j < 9; j++) {
+ if ('0' <= i[j] && i[j] <= '9') {
+ m = m * 10 + (i[j] - '0');
+ } else {
+ c = i[j];
+ break;
+ }
+ }
+ if (c == 'H') {
+ if (n > 0) {
+ n--;
+ }
+ if (m > 0) {
+ m--;
+ }
+ if (n >= self->tilegrid->height_in_tiles) {
+ n = self->tilegrid->height_in_tiles - 1;
+ }
+ if (m >= self->tilegrid->width_in_tiles) {
+ m = self->tilegrid->width_in_tiles - 1;
+ }
+ n = (n + self->tilegrid->top_left_y) % self->tilegrid->height_in_tiles;
+ self->cursor_x = m;
+ self->cursor_y = n;
+ start_y = self->cursor_y;
+ }
+ }
+ i += j + 1;
+ continue;
+ }
+ }
+ }
+ } else {
+ uint8_t tile_index = fontio_builtinfont_get_glyph_index(self->font, c);
+ if (tile_index != 0xff) {
+ common_hal_displayio_tilegrid_set_tile(self->tilegrid, self->cursor_x, self->cursor_y, tile_index);
+ self->cursor_x++;
+
+ }
+ }
+ if (self->cursor_x >= self->tilegrid->width_in_tiles) {
+ self->cursor_y++;
+ self->cursor_x %= self->tilegrid->width_in_tiles;
+ }
+ if (self->cursor_y >= self->tilegrid->height_in_tiles) {
+ self->cursor_y %= self->tilegrid->height_in_tiles;
+ }
+ if (self->cursor_y != start_y) {
+ // clear the new row in case of scroll up
+ if (self->cursor_y == self->tilegrid->top_left_y) {
+ for (uint16_t j = 0; j < self->tilegrid->width_in_tiles; j++) {
+ common_hal_displayio_tilegrid_set_tile(self->tilegrid, j, self->cursor_y, 0);
+ }
+ common_hal_displayio_tilegrid_set_top_left(self->tilegrid, 0, (self->cursor_y + self->tilegrid->height_in_tiles + 1) % self->tilegrid->height_in_tiles);
+ }
+ start_y = self->cursor_y;
+ }
+ }
+ return i - data;
+}
+
+bool common_hal_terminalio_terminal_ready_to_tx(terminalio_terminal_obj_t *self) {
+ return self->tilegrid != NULL;
+}
diff --git a/circuitpython/shared-module/terminalio/Terminal.h b/circuitpython/shared-module/terminalio/Terminal.h
new file mode 100644
index 0000000..2ba7e21
--- /dev/null
+++ b/circuitpython/shared-module/terminalio/Terminal.h
@@ -0,0 +1,46 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Scott Shawcroft 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.
+ */
+
+#ifndef SHARED_MODULE_TERMINALIO_TERMINAL_H
+#define SHARED_MODULE_TERMINALIO_TERMINAL_H
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "py/obj.h"
+#include "shared-module/fontio/BuiltinFont.h"
+#include "shared-module/displayio/TileGrid.h"
+
+typedef struct {
+ mp_obj_base_t base;
+ const fontio_builtinfont_t *font;
+ uint16_t cursor_x;
+ uint16_t cursor_y;
+ displayio_tilegrid_t *tilegrid;
+ uint16_t first_row;
+} terminalio_terminal_obj_t;
+
+#endif /* SHARED_MODULE_TERMINALIO_TERMINAL_H */
diff --git a/circuitpython/shared-module/terminalio/__init__.c b/circuitpython/shared-module/terminalio/__init__.c
new file mode 100644
index 0000000..3f61f48
--- /dev/null
+++ b/circuitpython/shared-module/terminalio/__init__.c
@@ -0,0 +1,27 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 hathach 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.
+ */
+
+#include "shared-bindings/terminalio/__init__.h"
diff --git a/circuitpython/shared-module/terminalio/__init__.h b/circuitpython/shared-module/terminalio/__init__.h
new file mode 100644
index 0000000..e925dd4
--- /dev/null
+++ b/circuitpython/shared-module/terminalio/__init__.h
@@ -0,0 +1,30 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Scott Shawcroft 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.
+ */
+
+#ifndef SHARED_MODULE_TERMINALIO___INIT___H
+#define SHARED_MODULE_TERMINALIO___INIT___H
+
+#endif /* SHARED_MODULE_TERMINALIO___INIT___H */
diff --git a/circuitpython/shared-module/time/__init__.c b/circuitpython/shared-module/time/__init__.c
new file mode 100644
index 0000000..b4575b1
--- /dev/null
+++ b/circuitpython/shared-module/time/__init__.c
@@ -0,0 +1,46 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Scott Shawcroft 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.
+ */
+
+#include "py/mphal.h"
+#include "supervisor/port.h"
+#include "supervisor/shared/tick.h"
+#include "shared-bindings/time/__init__.h"
+
+uint64_t common_hal_time_monotonic_ms(void) {
+ return supervisor_ticks_ms64();
+}
+
+uint64_t common_hal_time_monotonic_ns(void) {
+ uint8_t subticks = 0;
+ uint64_t ticks = port_get_raw_ticks(&subticks);
+ // A tick is 976562.5 nanoseconds so multiply it by the base and add half instead of doing float
+ // math.
+ return 976562 * ticks + ticks / 2 + 30518 * subticks;
+}
+
+void common_hal_time_delay_ms(uint32_t delay) {
+ mp_hal_delay_ms(delay);
+}
diff --git a/circuitpython/shared-module/touchio/TouchIn.c b/circuitpython/shared-module/touchio/TouchIn.c
new file mode 100644
index 0000000..840c145
--- /dev/null
+++ b/circuitpython/shared-module/touchio/TouchIn.c
@@ -0,0 +1,118 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Scott Shawcroft for Adafruit Industries
+ * Copyright (c) 2018 Nick Moore 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.
+ */
+
+#include <string.h>
+#include <stdio.h>
+
+#include "py/runtime.h"
+#include "py/mphal.h"
+#include "shared-bindings/touchio/TouchIn.h"
+#include "shared-bindings/microcontroller/Pin.h"
+
+// This is a capacitive touch sensing routine using a single digital
+// pin. The pin should be connected to the sensing pad, and to ground
+// via a 1Mohm or thereabout drain resistor. When a reading is taken,
+// the pin's capacitance is charged by setting it to a digital output
+// 'high' for a few microseconds, and then it is changed to a high
+// impedance input. We measure how long it takes to discharge through
+// the resistor (around 50us), using a busy-waiting loop, and average
+// over N_SAMPLES cycles to reduce the effects of noise.
+
+#define N_SAMPLES 10
+#define TIMEOUT_TICKS 10000
+
+static uint16_t get_raw_reading(touchio_touchin_obj_t *self) {
+
+ uint16_t ticks = 0;
+
+ for (uint16_t i = 0; i < N_SAMPLES; i++) {
+ // set pad to digital output high for 10us to charge it
+
+ common_hal_digitalio_digitalinout_switch_to_output(self->digitalinout, true, DRIVE_MODE_PUSH_PULL);
+ mp_hal_delay_us(10);
+
+ // set pad back to an input and take some samples
+
+ common_hal_digitalio_digitalinout_switch_to_input(self->digitalinout, PULL_NONE);
+
+ while (common_hal_digitalio_digitalinout_get_value(self->digitalinout)) {
+ if (ticks >= TIMEOUT_TICKS) {
+ return TIMEOUT_TICKS;
+ }
+ ticks++;
+ }
+ }
+ return ticks;
+}
+
+void common_hal_touchio_touchin_construct(touchio_touchin_obj_t *self, const mcu_pin_obj_t *pin) {
+ common_hal_mcu_pin_claim(pin);
+ self->digitalinout = m_new_obj(digitalio_digitalinout_obj_t);
+ self->digitalinout->base.type = &digitalio_digitalinout_type;
+
+ common_hal_digitalio_digitalinout_construct(self->digitalinout, pin);
+
+ uint16_t raw_reading = get_raw_reading(self);
+ if (raw_reading == TIMEOUT_TICKS) {
+ mp_raise_ValueError(translate("No pulldown on pin; 1Mohm recommended"));
+ }
+ self->threshold = raw_reading * 1.05 + 100;
+}
+
+bool common_hal_touchio_touchin_deinited(touchio_touchin_obj_t *self) {
+ return self->digitalinout == MP_OBJ_NULL;
+}
+
+void common_hal_touchio_touchin_deinit(touchio_touchin_obj_t *self) {
+ if (common_hal_touchio_touchin_deinited(self)) {
+ return;
+ }
+
+ common_hal_digitalio_digitalinout_deinit(self->digitalinout);
+ self->digitalinout = MP_OBJ_NULL;
+}
+
+void touchin_reset() {
+}
+
+bool common_hal_touchio_touchin_get_value(touchio_touchin_obj_t *self) {
+ uint16_t reading = get_raw_reading(self);
+ return reading > self->threshold;
+}
+
+uint16_t common_hal_touchio_touchin_get_raw_value(touchio_touchin_obj_t *self) {
+ return get_raw_reading(self);
+}
+
+uint16_t common_hal_touchio_touchin_get_threshold(touchio_touchin_obj_t *self) {
+ return self->threshold;
+}
+
+void common_hal_touchio_touchin_set_threshold(touchio_touchin_obj_t *self,
+ uint16_t new_threshold) {
+ self->threshold = new_threshold;
+}
diff --git a/circuitpython/shared-module/touchio/TouchIn.h b/circuitpython/shared-module/touchio/TouchIn.h
new file mode 100644
index 0000000..8ff6fda
--- /dev/null
+++ b/circuitpython/shared-module/touchio/TouchIn.h
@@ -0,0 +1,44 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Scott Shawcroft
+ * Copyright (c) 2018 Nick Moore 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.
+ */
+
+#ifndef MICROPY_INCLUDED_SHARED_MODULE_TOUCHIO_TOUCHIN_H
+#define MICROPY_INCLUDED_SHARED_MODULE_TOUCHIO_TOUCHIN_H
+
+#include "common-hal/microcontroller/Pin.h"
+#include "shared-bindings/digitalio/DigitalInOut.h"
+
+#include "py/obj.h"
+
+typedef struct {
+ mp_obj_base_t base;
+ digitalio_digitalinout_obj_t *digitalinout;
+ uint16_t threshold;
+} touchio_touchin_obj_t;
+
+void touchin_reset(void);
+
+#endif // MICROPY_INCLUDED_SHARED_MODULE_TOUCHIO_TOUCHIN_H
diff --git a/circuitpython/shared-module/touchio/__init__.c b/circuitpython/shared-module/touchio/__init__.c
new file mode 100644
index 0000000..d229044
--- /dev/null
+++ b/circuitpython/shared-module/touchio/__init__.c
@@ -0,0 +1 @@
+// No touchio module functions.
diff --git a/circuitpython/shared-module/traceback/__init__.c b/circuitpython/shared-module/traceback/__init__.c
new file mode 100644
index 0000000..a29bd57
--- /dev/null
+++ b/circuitpython/shared-module/traceback/__init__.c
@@ -0,0 +1,31 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 microDev
+ *
+ * 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.
+ */
+
+#include "shared-module/traceback/__init__.h"
+
+void shared_module_traceback_print_exception(mp_obj_exception_t *exc, mp_print_t *print, mp_int_t limit) {
+ mp_obj_print_exception_with_limit(print, exc, limit);
+}
diff --git a/circuitpython/shared-module/traceback/__init__.h b/circuitpython/shared-module/traceback/__init__.h
new file mode 100644
index 0000000..73edd07
--- /dev/null
+++ b/circuitpython/shared-module/traceback/__init__.h
@@ -0,0 +1,36 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 microDev
+ *
+ * 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.
+ */
+
+#ifndef MICROPY_INCLUDED_SHARED_MODULE_TRACEBACK___INIT___H
+#define MICROPY_INCLUDED_SHARED_MODULE_TRACEBACK___INIT___H
+
+#include "py/objexcept.h"
+#include "py/objtraceback.h"
+
+extern void shared_module_traceback_print_exception(mp_obj_exception_t *exc,
+ mp_print_t *print, mp_int_t limit);
+
+#endif // MICROPY_INCLUDED_SHARED_MODULE_TRACEBACK___INIT___H
diff --git a/circuitpython/shared-module/uheap/__init__.c b/circuitpython/shared-module/uheap/__init__.c
new file mode 100644
index 0000000..bff571a
--- /dev/null
+++ b/circuitpython/shared-module/uheap/__init__.c
@@ -0,0 +1,292 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Scott Shawcroft
+ *
+ * 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.
+ */
+
+#include <stdint.h>
+
+#include "py/bc.h"
+#include "py/binary.h"
+#include "py/gc.h"
+#include "py/obj.h"
+#include "py/objarray.h"
+#include "py/objfun.h"
+#include "py/objint.h"
+#include "py/objstr.h"
+#include "py/objtype.h"
+#include "py/runtime.h"
+
+#include "shared-bindings/uheap/__init__.h"
+
+#define VERIFY_PTR(ptr) ( \
+ (void *)ptr >= (void *)MP_STATE_MEM(gc_pool_start) /* must be above start of pool */ \
+ && (void *)ptr < (void *)MP_STATE_MEM(gc_pool_end) /* must be below end of pool */ \
+ )
+
+static void indent(uint8_t levels) {
+ for (int i = 0; i < levels; i++) {
+ mp_printf(&mp_plat_print, " ");
+ }
+}
+
+static uint32_t object_size(uint8_t indent_level, mp_obj_t obj);
+
+static uint32_t int_size(uint8_t indent_level, mp_obj_t obj) {
+ if (mp_obj_is_small_int(obj)) {
+ return 0;
+ }
+ if (!VERIFY_PTR(obj)) {
+ return 0;
+ }
+ #if MICROPY_LONGINT_IMPL == MICROPY_LONGINT_IMPL_MPZ
+ mp_obj_int_t *i = MP_OBJ_TO_PTR(obj);
+ return gc_nbytes(obj) + gc_nbytes(i->mpz.dig);
+ #else
+ return gc_nbytes(obj);
+ #endif
+}
+
+static uint32_t string_size(uint8_t indent_level, mp_obj_t obj) {
+ if (mp_obj_is_qstr(obj)) {
+ qstr qs = MP_OBJ_QSTR_VALUE(obj);
+ const char *s = qstr_str(qs);
+ if (!VERIFY_PTR(s)) {
+ return 0;
+ }
+ indent(indent_level);
+ mp_printf(&mp_plat_print, "%s\n", s);
+ return 0;
+ } else { // mp_obj_is_type(o, &mp_type_str)
+ mp_obj_str_t *s = MP_OBJ_TO_PTR(obj);
+ return gc_nbytes(s) + gc_nbytes(s->data);
+ }
+}
+
+static uint32_t map_size(uint8_t indent_level, const mp_map_t *map) {
+ uint32_t total_size = gc_nbytes(map->table);
+ for (int i = 0; i < map->used; i++) {
+ uint32_t this_size = 0;
+ indent(indent_level);
+ if (map->table[i].key != NULL) {
+ mp_print_str(&mp_plat_print, "key: ");
+ mp_obj_print_helper(&mp_plat_print, map->table[i].key, PRINT_STR);
+ mp_print_str(&mp_plat_print, "\n");
+ } else {
+ mp_print_str(&mp_plat_print, "null key\n");
+ }
+ this_size += object_size(indent_level + 1, map->table[i].key);
+ this_size += object_size(indent_level + 1, map->table[i].value);
+
+ indent(indent_level);
+ mp_printf(&mp_plat_print, "Entry size: %u\n\n", this_size);
+ total_size += this_size;
+ }
+
+ return total_size;
+}
+
+static uint32_t dict_size(uint8_t indent_level, mp_obj_dict_t *dict) {
+ uint32_t total_size = gc_nbytes(dict);
+
+ indent(indent_level);
+ mp_printf(&mp_plat_print, "Dictionary @%x\n", dict);
+
+ total_size += map_size(indent_level, &dict->map);
+
+ return total_size;
+}
+
+static uint32_t function_size(uint8_t indent_level, mp_obj_t obj) {
+ // indent(indent_level);
+ // mp_print_str(&mp_plat_print, "function\n");
+ if (mp_obj_is_type(obj, &mp_type_fun_builtin_0)) {
+ return 0;
+ } else if (mp_obj_is_type(obj, &mp_type_fun_builtin_1)) {
+ return 0;
+ } else if (mp_obj_is_type(obj, &mp_type_fun_builtin_2)) {
+ return 0;
+ } else if (mp_obj_is_type(obj, &mp_type_fun_builtin_3)) {
+ return 0;
+ } else if (mp_obj_is_type(obj, &mp_type_fun_builtin_var)) {
+ return 0;
+ } else if (mp_obj_is_type(obj, &mp_type_fun_bc)) {
+ mp_obj_fun_bc_t *fn = MP_OBJ_TO_PTR(obj);
+ uint32_t total_size = gc_nbytes(fn) + gc_nbytes(fn->bytecode) + gc_nbytes(fn->const_table);
+ #if MICROPY_DEBUG_PRINTERS
+ mp_printf(&mp_plat_print, "BYTECODE START\n");
+ mp_bytecode_print(fn, fn->bytecode, gc_nbytes(fn->bytecode), fn->const_table);
+ mp_printf(&mp_plat_print, "BYTECODE END\n");
+ #endif
+ return total_size;
+ #if MICROPY_EMIT_NATIVE
+ } else if (mp_obj_is_type(obj, &mp_type_fun_native)) {
+ return 0;
+ #endif
+ #if MICROPY_EMIT_NATIVE
+ } else if (mp_obj_is_type(obj, &mp_obj_fun_viper_t)) {
+ return 0;
+ #endif
+ #if MICROPY_EMIT_THUMB
+ } else if (mp_obj_is_type(obj, &mp_type_fun_asm)) {
+ return 0;
+ #endif
+ }
+ return 0;
+}
+
+static uint32_t array_size(uint8_t indent_level, mp_obj_array_t *array) {
+ uint32_t total_size = gc_nbytes(array);
+
+ uint32_t item_size = gc_nbytes(array->items);
+ total_size += item_size;
+ indent(indent_level);
+ mp_printf(&mp_plat_print, "Array of size: %u\n\n", item_size);
+
+ return total_size;
+}
+
+static uint32_t memoryview_size(uint8_t indent_level, mp_obj_array_t *array) {
+ uint32_t total_size = gc_nbytes(array);
+
+ indent(indent_level);
+ mp_printf(&mp_plat_print, "memoryview\n");
+
+ return total_size;
+}
+
+static uint32_t type_size(uint8_t indent_level, mp_obj_type_t *type) {
+ uint32_t total_size = gc_nbytes(type);
+
+ // mp_obj_base_t base;
+ // qstr name;
+ // total_size += string_size(indent_level, MP_OBJ_TO_PTR(type->name));
+ // mp_print_fun_t print;
+ // mp_make_new_fun_t make_new; // to make an instance of the type
+ //
+ // mp_call_fun_t call;
+ // mp_unary_op_fun_t unary_op; // can return MP_OBJ_NULL if op not supported
+ // mp_binary_op_fun_t binary_op; // can return MP_OBJ_NULL if op not supported
+ //
+ // // implements load, store and delete attribute
+ // //
+ // // dest[0] = MP_OBJ_NULL means load
+ // // return: for fail, do nothing
+ // // for attr, dest[0] = value
+ // // for method, dest[0] = method, dest[1] = self
+ // //
+ // // dest[0,1] = {MP_OBJ_SENTINEL, MP_OBJ_NULL} means delete
+ // // dest[0,1] = {MP_OBJ_SENTINEL, object} means store
+ // // return: for fail, do nothing
+ // // for success set dest[0] = MP_OBJ_NULL
+ // mp_attr_fun_t attr;
+ //
+ // mp_subscr_fun_t subscr; // implements load, store, delete subscripting
+ // // value=MP_OBJ_NULL means delete, value=MP_OBJ_SENTINEL means load, else store
+ // // can return MP_OBJ_NULL if op not supported
+ //
+ // mp_fun_1_t getiter; // corresponds to __iter__ special method
+ // mp_fun_1_t iternext; // may return MP_OBJ_STOP_ITERATION as an optimisation instead of raising StopIteration() (with no args)
+ //
+ // mp_buffer_p_t buffer_p;
+ // // One of disjoint protocols (interfaces), like mp_stream_p_t, etc.
+ // const void *protocol;
+ //
+ // // these are for dynamically created types (classes)
+ // struct _mp_obj_tuple_t *bases_tuple;
+ // struct _mp_obj_dict_t *locals_dict;
+ if (type->locals_dict != NULL) {
+ total_size += dict_size(indent_level, type->locals_dict);
+ }
+
+ indent(indent_level);
+ mp_printf(&mp_plat_print, "TYPE\n");
+ return total_size;
+}
+
+
+static uint32_t instance_size(uint8_t indent_level, mp_obj_instance_t *instance) {
+ uint32_t total_size = gc_nbytes(instance);
+
+ total_size += map_size(indent_level, &instance->members);
+
+ return total_size;
+}
+
+static uint32_t module_size(uint8_t indent_level, mp_obj_module_t *module) {
+ uint32_t total_size = gc_nbytes(module);
+
+ indent(indent_level);
+ mp_printf(&mp_plat_print, ".globals\n");
+
+ total_size += dict_size(indent_level + 1, module->globals);
+
+ indent(indent_level);
+ mp_printf(&mp_plat_print, "Module size: %u\n", total_size);
+ return total_size;
+}
+
+static uint32_t object_size(uint8_t indent_level, mp_obj_t obj) {
+ if (obj == NULL) {
+ return 0;
+ }
+ if (mp_obj_is_int(obj)) {
+ return int_size(indent_level, MP_OBJ_TO_PTR(obj));
+ } else if (mp_obj_is_str(obj)) {
+ return string_size(indent_level, MP_OBJ_TO_PTR(obj));
+ } else if (mp_obj_is_fun(obj)) {
+ return function_size(indent_level, MP_OBJ_TO_PTR(obj));
+ }
+ if (!VERIFY_PTR(obj)) {
+ // indent(indent_level);
+ // mp_printf(&mp_plat_print, "In ROM\n");
+ return 0;
+ }
+ mp_obj_t type = MP_OBJ_FROM_PTR(mp_obj_get_type(obj));
+
+ if (type == &mp_type_module) {
+ return module_size(indent_level, MP_OBJ_TO_PTR(obj));
+ } else if (type == &mp_type_dict) {
+ return dict_size(indent_level, MP_OBJ_TO_PTR(obj));
+ } else if (type == &mp_type_type) {
+ return type_size(indent_level, MP_OBJ_TO_PTR(obj));
+ } else if (type == &mp_type_bytearray || type == &mp_type_array) {
+ return array_size(indent_level, MP_OBJ_TO_PTR(obj));
+ } else if (type == &mp_type_memoryview) {
+ return memoryview_size(indent_level, MP_OBJ_TO_PTR(obj));
+ } else if (mp_obj_is_obj(obj) && VERIFY_PTR(type)) {
+ return instance_size(indent_level, MP_OBJ_TO_PTR(obj));
+ }
+
+ indent(indent_level);
+ mp_printf(&mp_plat_print, "unknown type %x\n", type);
+ return 0;
+}
+
+uint32_t shared_module_uheap_info(mp_obj_t obj) {
+ if (!VERIFY_PTR(obj)) {
+ mp_printf(&mp_plat_print, "Object not on heap.\n");
+ return 0;
+ }
+ return object_size(0, obj);
+}
diff --git a/circuitpython/shared-module/usb/__init__.c b/circuitpython/shared-module/usb/__init__.c
new file mode 100644
index 0000000..2b3a4c5
--- /dev/null
+++ b/circuitpython/shared-module/usb/__init__.c
@@ -0,0 +1,27 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2022 Scott Shawcroft 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.
+ */
+
+// Nothing here
diff --git a/circuitpython/shared-module/usb/core/Device.c b/circuitpython/shared-module/usb/core/Device.c
new file mode 100644
index 0000000..81431d5
--- /dev/null
+++ b/circuitpython/shared-module/usb/core/Device.c
@@ -0,0 +1,185 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2022 Scott Shawcroft 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.
+ */
+
+#include "shared-bindings/usb/core/Device.h"
+
+#include "tusb_config.h"
+
+#include "lib/tinyusb/src/host/usbh.h"
+#include "py/runtime.h"
+#include "shared/runtime/interrupt_char.h"
+#include "shared-bindings/usb/core/__init__.h"
+#include "shared-module/usb/utf16le.h"
+#include "supervisor/shared/tick.h"
+
+bool common_hal_usb_core_device_construct(usb_core_device_obj_t *self, uint8_t device_number) {
+ if (device_number == 0 || device_number > CFG_TUH_DEVICE_MAX + CFG_TUH_HUB) {
+ return false;
+ }
+ if (!tuh_ready(device_number)) {
+ return false;
+ }
+ self->device_number = device_number;
+ return true;
+}
+
+uint16_t common_hal_usb_core_device_get_idVendor(usb_core_device_obj_t *self) {
+ uint16_t vid;
+ uint16_t pid;
+ tuh_vid_pid_get(self->device_number, &vid, &pid);
+ return vid;
+}
+
+uint16_t common_hal_usb_core_device_get_idProduct(usb_core_device_obj_t *self) {
+ uint16_t vid;
+ uint16_t pid;
+ tuh_vid_pid_get(self->device_number, &vid, &pid);
+ return pid;
+}
+
+STATIC xfer_result_t _get_string_result;
+STATIC bool _transfer_done_cb(uint8_t daddr, tusb_control_request_t const *request, xfer_result_t result) {
+ // Store the result so we stop waiting for the transfer. We don't need the other data for now.
+ (void)daddr;
+ (void)request;
+ _get_string_result = result;
+ return true;
+}
+
+STATIC void _wait_for_callback(void) {
+ while (!mp_hal_is_interrupted() &&
+ _get_string_result == 0xff) {
+ // The background tasks include TinyUSB which will call the function
+ // we provided above. In other words, the callback isn't in an interrupt.
+ RUN_BACKGROUND_TASKS;
+ }
+}
+
+STATIC mp_obj_t _get_string(const uint16_t *temp_buf) {
+ size_t utf16_len = ((temp_buf[0] & 0xff) - 2) / sizeof(uint16_t);
+ if (utf16_len == 0) {
+ return mp_const_none;
+ }
+ return utf16le_to_string(temp_buf + 1, utf16_len);
+}
+
+mp_obj_t common_hal_usb_core_device_get_serial_number(usb_core_device_obj_t *self) {
+ _get_string_result = 0xff;
+ uint16_t temp_buf[127];
+ if (!tuh_descriptor_string_serial_get(self->device_number, 0, temp_buf, MP_ARRAY_SIZE(temp_buf), _transfer_done_cb)) {
+ return mp_const_none;
+ }
+ _wait_for_callback();
+ return _get_string(temp_buf);
+}
+
+mp_obj_t common_hal_usb_core_device_get_product(usb_core_device_obj_t *self) {
+ _get_string_result = 0xff;
+ uint16_t temp_buf[127];
+ if (!tuh_descriptor_string_product_get(self->device_number, 0, temp_buf, MP_ARRAY_SIZE(temp_buf), _transfer_done_cb)) {
+ return mp_const_none;
+ }
+ _wait_for_callback();
+ return _get_string(temp_buf);
+}
+
+mp_obj_t common_hal_usb_core_device_get_manufacturer(usb_core_device_obj_t *self) {
+ _get_string_result = 0xff;
+ uint16_t temp_buf[127];
+ if (!tuh_descriptor_string_manufacturer_get(self->device_number, 0, temp_buf, MP_ARRAY_SIZE(temp_buf), _transfer_done_cb)) {
+ return mp_const_none;
+ }
+ _wait_for_callback();
+ return _get_string(temp_buf);
+}
+
+mp_obj_t common_hal_usb_core_device_write(usb_core_device_obj_t *self, mp_int_t endpoint, const uint8_t *buffer, mp_int_t len, mp_int_t timeout) {
+ return mp_const_none;
+}
+
+mp_obj_t common_hal_usb_core_device_read(usb_core_device_obj_t *self, mp_int_t endpoint, uint8_t *buffer, mp_int_t len, mp_int_t timeout) {
+ return mp_const_none;
+}
+
+xfer_result_t control_result;
+STATIC bool _control_complete_cb(uint8_t dev_addr, tusb_control_request_t const *request, xfer_result_t result) {
+ (void)dev_addr;
+ (void)request;
+ control_result = result;
+ return true;
+}
+
+mp_int_t common_hal_usb_core_device_ctrl_transfer(usb_core_device_obj_t *self,
+ mp_int_t bmRequestType, mp_int_t bRequest,
+ mp_int_t wValue, mp_int_t wIndex,
+ uint8_t *buffer, mp_int_t len, mp_int_t timeout) {
+ // Timeout is in ms.
+
+ tusb_control_request_t request = {
+ .bmRequestType = bmRequestType,
+ .bRequest = bRequest,
+ .wValue = wValue,
+ .wIndex = wIndex,
+ .wLength = len
+ };
+ control_result = XFER_RESULT_STALLED;
+ bool result = tuh_control_xfer(self->device_number,
+ &request,
+ buffer,
+ _control_complete_cb);
+ if (!result) {
+ mp_raise_usb_core_USBError(NULL);
+ }
+ uint32_t start_time = supervisor_ticks_ms32();
+ while (supervisor_ticks_ms32() - start_time < (uint32_t)timeout &&
+ !mp_hal_is_interrupted() &&
+ control_result == XFER_RESULT_STALLED) {
+ // The background tasks include TinyUSB which will call the function
+ // we provided above. In other words, the callback isn't in an interrupt.
+ RUN_BACKGROUND_TASKS;
+ }
+ if (control_result == XFER_RESULT_STALLED) {
+ mp_raise_usb_core_USBTimeoutError();
+ }
+ if (control_result == XFER_RESULT_SUCCESS) {
+ return len;
+ }
+
+ return 0;
+}
+
+bool common_hal_usb_core_device_is_kernel_driver_active(usb_core_device_obj_t *self, mp_int_t interface) {
+ // TODO: Implement this when CP natively uses a keyboard.
+ return false;
+}
+
+void common_hal_usb_core_device_detach_kernel_driver(usb_core_device_obj_t *self, mp_int_t interface) {
+ // TODO: Implement this when CP natively uses a keyboard.
+}
+
+void common_hal_usb_core_device_attach_kernel_driver(usb_core_device_obj_t *self, mp_int_t interface) {
+ // TODO: Implement this when CP natively uses a keyboard.
+}
diff --git a/circuitpython/shared-module/usb/core/Device.h b/circuitpython/shared-module/usb/core/Device.h
new file mode 100644
index 0000000..5959639
--- /dev/null
+++ b/circuitpython/shared-module/usb/core/Device.h
@@ -0,0 +1,37 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2022 Scott Shawcroft 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.
+ */
+
+#ifndef MICROPY_INCLUDED_SHARED_MODULE_USB_CORE_DEVICE_H
+#define MICROPY_INCLUDED_SHARED_MODULE_USB_CORE_DEVICE_H
+
+#include "py/obj.h"
+
+typedef struct {
+ mp_obj_base_t base;
+ uint8_t device_number;
+} usb_core_device_obj_t;
+
+#endif // MICROPY_INCLUDED_SHARED_MODULE_USB_CORE_DEVICE_H
diff --git a/circuitpython/shared-module/usb/core/__init__.c b/circuitpython/shared-module/usb/core/__init__.c
new file mode 100644
index 0000000..a108f5f
--- /dev/null
+++ b/circuitpython/shared-module/usb/core/__init__.c
@@ -0,0 +1,27 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2022 Scott Shawcroft 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.
+ */
+
+// Nothing implementation specific.
diff --git a/circuitpython/shared-module/usb/utf16le.c b/circuitpython/shared-module/usb/utf16le.c
new file mode 100644
index 0000000..24ccd09
--- /dev/null
+++ b/circuitpython/shared-module/usb/utf16le.c
@@ -0,0 +1,85 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2022 Scott Shawcroft 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.
+ */
+
+#include "shared-module/usb/utf16le.h"
+
+STATIC void _convert_utf16le_to_utf8(const uint16_t *utf16, size_t utf16_len, uint8_t *utf8, size_t utf8_len) {
+ // TODO: Check for runover.
+ (void)utf8_len;
+
+ for (size_t i = 0; i < utf16_len; i++) {
+ uint16_t chr = utf16[i];
+ if (chr < 0x80) {
+ *utf8++ = chr & 0xff;
+ } else if (chr < 0x800) {
+ *utf8++ = (uint8_t)(0xC0 | (chr >> 6 & 0x1F));
+ *utf8++ = (uint8_t)(0x80 | (chr >> 0 & 0x3F));
+ } else if (chr < 0x10000) {
+ // TODO: Verify surrogate.
+ *utf8++ = (uint8_t)(0xE0 | (chr >> 12 & 0x0F));
+ *utf8++ = (uint8_t)(0x80 | (chr >> 6 & 0x3F));
+ *utf8++ = (uint8_t)(0x80 | (chr >> 0 & 0x3F));
+ } else {
+ // TODO: Handle UTF-16 code points that take two entries.
+ uint32_t hc = ((chr & 0xFFFF0000) - 0xD8000000) >> 6; /* Get high 10 bits */
+ chr = (chr & 0xFFFF) - 0xDC00; /* Get low 10 bits */
+ chr = (hc | chr) + 0x10000;
+ *utf8++ = (uint8_t)(0xF0 | (chr >> 18 & 0x07));
+ *utf8++ = (uint8_t)(0x80 | (chr >> 12 & 0x3F));
+ *utf8++ = (uint8_t)(0x80 | (chr >> 6 & 0x3F));
+ *utf8++ = (uint8_t)(0x80 | (chr >> 0 & 0x3F));
+ }
+ }
+}
+
+// Count how many bytes a utf-16-le encoded string will take in utf-8.
+STATIC mp_int_t _count_utf8_bytes(const uint16_t *buf, size_t len) {
+ size_t total_bytes = 0;
+ for (size_t i = 0; i < len; i++) {
+ uint16_t chr = buf[i];
+ if (chr < 0x80) {
+ total_bytes += 1;
+ } else if (chr < 0x800) {
+ total_bytes += 2;
+ } else if (chr < 0x10000) {
+ total_bytes += 3;
+ } else {
+ total_bytes += 4;
+ }
+ }
+ return total_bytes;
+}
+
+mp_obj_t utf16le_to_string(const uint16_t *buf, size_t utf16_len) {
+ size_t size = _count_utf8_bytes(buf, utf16_len);
+ vstr_t vstr;
+ vstr_init_len(&vstr, size + 1);
+ byte *p = (byte *)vstr.buf;
+ // Null terminate.
+ p[size] = '\0';
+ _convert_utf16le_to_utf8(buf, utf16_len, p, size);
+ return mp_obj_new_str_from_vstr(&mp_type_str, &vstr);
+}
diff --git a/circuitpython/shared-module/usb/utf16le.h b/circuitpython/shared-module/usb/utf16le.h
new file mode 100644
index 0000000..7305a1a
--- /dev/null
+++ b/circuitpython/shared-module/usb/utf16le.h
@@ -0,0 +1,34 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2022 Scott Shawcroft 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.
+ */
+
+#ifndef MICROPY_INCLUDED_SHARED_MODULE_USB_UTF16LE_H
+#define MICROPY_INCLUDED_SHARED_MODULE_USB_UTF16LE_H
+
+#include "py/obj.h"
+
+mp_obj_t utf16le_to_string(const uint16_t *buf, size_t utf16_len);
+
+#endif // MICROPY_INCLUDED_SHARED_MODULE_USB_UTF16LE_H
diff --git a/circuitpython/shared-module/usb_cdc/Serial.c b/circuitpython/shared-module/usb_cdc/Serial.c
new file mode 100644
index 0000000..7f1bc75
--- /dev/null
+++ b/circuitpython/shared-module/usb_cdc/Serial.c
@@ -0,0 +1,158 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 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.
+ */
+
+#include "shared/runtime/interrupt_char.h"
+#include "shared-bindings/usb_cdc/Serial.h"
+#include "shared-module/usb_cdc/Serial.h"
+#include "supervisor/shared/tick.h"
+
+#include "tusb.h"
+
+size_t common_hal_usb_cdc_serial_read(usb_cdc_serial_obj_t *self, uint8_t *data, size_t len, int *errcode) {
+
+ const bool wait_forever = self->timeout < 0.0f;
+ const bool wait_for_timeout = self->timeout > 0.0f;
+
+ // Read up to len bytes immediately.
+ // The number of bytes read will not be larger than what is already in the TinyUSB FIFO.
+ uint32_t total_num_read = tud_cdc_n_read(self->idx, data, len);
+
+ if (wait_forever || wait_for_timeout) {
+ // Continue filling the buffer past what we already read.
+ len -= total_num_read;
+ data += total_num_read;
+
+ // Read more if we have time.
+ // Use special routine to avoid pulling in uint64-float-compatible math routines.
+ uint64_t timeout_ms = float_to_uint64(self->timeout * 1000); // Junk value if timeout < 0.
+ uint64_t start_ticks = supervisor_ticks_ms64();
+
+ uint32_t num_read = 0;
+ while (total_num_read < len &&
+ (wait_forever || supervisor_ticks_ms64() - start_ticks <= timeout_ms)) {
+
+ // Wait for a bit, and check for ctrl-C.
+ RUN_BACKGROUND_TASKS;
+ if (mp_hal_is_interrupted()) {
+ return 0;
+ }
+
+ // Advance buffer pointer and reduce number of bytes that need to be read.
+ len -= num_read;
+ data += num_read;
+
+ // Try to read another batch of bytes.
+ num_read = tud_cdc_n_read(self->idx, data, len);
+ total_num_read += num_read;
+ }
+ }
+
+ return total_num_read;
+}
+
+size_t common_hal_usb_cdc_serial_write(usb_cdc_serial_obj_t *self, const uint8_t *data, size_t len, int *errcode) {
+ const bool wait_forever = self->write_timeout < 0.0f;
+ const bool wait_for_timeout = self->write_timeout > 0.0f;
+
+ // Write as many bytes as possible immediately.
+ // The number of bytes written at once will not be larger than what can fit in the TinyUSB FIFO.
+ uint32_t total_num_written = tud_cdc_n_write(self->idx, data, len);
+ tud_cdc_n_write_flush(self->idx);
+
+ if (wait_forever || wait_for_timeout) {
+ // Continue writing the rest of the buffer.
+ len -= total_num_written;
+ data += total_num_written;
+
+ // Write more if we have time.
+ // Use special routine to avoid pulling in uint64-float-compatible math routines.
+ uint64_t timeout_ms = float_to_uint64(self->write_timeout * 1000); // Junk value if write_timeout < 0.
+ uint64_t start_ticks = supervisor_ticks_ms64();
+
+ uint32_t num_written = 0;
+ while (total_num_written < len &&
+ (wait_forever || supervisor_ticks_ms64() - start_ticks <= timeout_ms)) {
+
+ // Wait for a bit, and check for ctrl-C.
+ RUN_BACKGROUND_TASKS;
+ if (mp_hal_is_interrupted()) {
+ return 0;
+ }
+
+ // Advance buffer pointer and reduce number of bytes that need to be written.
+ len -= num_written;
+ data += num_written;
+
+ // Try to write another batch of bytes.
+ num_written = tud_cdc_n_write(self->idx, data, len);
+ tud_cdc_n_write_flush(self->idx);
+ total_num_written += num_written;
+ }
+ }
+
+ return total_num_written;
+}
+
+uint32_t common_hal_usb_cdc_serial_get_in_waiting(usb_cdc_serial_obj_t *self) {
+ return tud_cdc_n_available(self->idx);
+}
+
+uint32_t common_hal_usb_cdc_serial_get_out_waiting(usb_cdc_serial_obj_t *self) {
+ // Return number of FIFO bytes currently occupied.
+ return CFG_TUD_CDC_TX_BUFSIZE - tud_cdc_n_write_available(self->idx);
+}
+
+void common_hal_usb_cdc_serial_reset_input_buffer(usb_cdc_serial_obj_t *self) {
+ tud_cdc_n_read_flush(self->idx);
+}
+
+uint32_t common_hal_usb_cdc_serial_reset_output_buffer(usb_cdc_serial_obj_t *self) {
+ return tud_cdc_n_write_clear(self->idx);
+}
+
+uint32_t common_hal_usb_cdc_serial_flush(usb_cdc_serial_obj_t *self) {
+ return tud_cdc_n_write_flush(self->idx);
+}
+
+bool common_hal_usb_cdc_serial_get_connected(usb_cdc_serial_obj_t *self) {
+ return tud_cdc_n_connected(self->idx);
+}
+
+mp_float_t common_hal_usb_cdc_serial_get_timeout(usb_cdc_serial_obj_t *self) {
+ return self->timeout;
+}
+
+void common_hal_usb_cdc_serial_set_timeout(usb_cdc_serial_obj_t *self, mp_float_t timeout) {
+ self->timeout = timeout;
+}
+
+mp_float_t common_hal_usb_cdc_serial_get_write_timeout(usb_cdc_serial_obj_t *self) {
+ return self->write_timeout;
+}
+
+void common_hal_usb_cdc_serial_set_write_timeout(usb_cdc_serial_obj_t *self, mp_float_t write_timeout) {
+ self->write_timeout = write_timeout;
+}
diff --git a/circuitpython/shared-module/usb_cdc/Serial.h b/circuitpython/shared-module/usb_cdc/Serial.h
new file mode 100644
index 0000000..ddf78ee
--- /dev/null
+++ b/circuitpython/shared-module/usb_cdc/Serial.h
@@ -0,0 +1,39 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 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.
+ */
+
+#ifndef SHARED_MODULE_USB_CDC_SERIAL_H
+#define SHARED_MODULE_USB_CDC_SERIAL_H
+
+#include "py/obj.h"
+
+typedef struct {
+ mp_obj_base_t base;
+ mp_float_t timeout; // if negative, wait forever.
+ mp_float_t write_timeout; // if negative, wait forever.
+ uint8_t idx; // which CDC device?
+} usb_cdc_serial_obj_t;
+
+#endif // SHARED_MODULE_USB_CDC_SERIAL_H
diff --git a/circuitpython/shared-module/usb_cdc/__init__.c b/circuitpython/shared-module/usb_cdc/__init__.c
new file mode 100644
index 0000000..9961d6d
--- /dev/null
+++ b/circuitpython/shared-module/usb_cdc/__init__.c
@@ -0,0 +1,417 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 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.
+ */
+
+#include "py/gc.h"
+#include "py/obj.h"
+#include "py/mphal.h"
+#include "py/runtime.h"
+#include "py/objtuple.h"
+#include "shared-bindings/usb_cdc/__init__.h"
+#include "shared-bindings/usb_cdc/Serial.h"
+#include "supervisor/usb.h"
+
+#include "tusb.h"
+
+#if CIRCUITPY_USB_VENDOR
+// todo - this doesn't feel like it should be here.
+#include "supervisor/memory.h"
+#endif
+
+#if CFG_TUD_CDC != 2
+#error CFG_TUD_CDC must be exactly 2
+#endif
+
+static const uint8_t usb_cdc_descriptor_template[] = {
+ // CDC IAD Descriptor
+ 0x08, // 0 bLength
+ 0x0B, // 1 bDescriptorType: IAD Descriptor
+ 0xFF, // 2 bFirstInterface [SET AT RUNTIME]
+#define CDC_FIRST_INTERFACE_INDEX 2
+ 0x02, // 3 bInterfaceCount: 2
+ 0x02, // 4 bFunctionClass: COMM
+ 0x02, // 5 bFunctionSubclass: ACM
+ 0x00, // 6 bFunctionProtocol: NONE
+ 0x00, // 7 iFunction
+
+ // CDC Comm Interface Descriptor
+ 0x09, // 8 bLength
+ 0x04, // 9 bDescriptorType (Interface)
+ 0xFF, // 10 bInterfaceNumber [SET AT RUNTIME]
+#define CDC_COMM_INTERFACE_INDEX 10
+ 0x00, // 11 bAlternateSetting
+ 0x01, // 12 bNumEndpoints 1
+ 0x02, // 13 bInterfaceClass: COMM
+ 0x02, // 14 bInterfaceSubClass: ACM
+ 0x00, // 15 bInterfaceProtocol: NONE
+ 0xFF, // 16 iInterface (String Index)
+#define CDC_COMM_INTERFACE_STRING_INDEX 16
+
+ // CDC Header Descriptor
+ 0x05, // 17 bLength
+ 0x24, // 18 bDescriptorType: CLASS SPECIFIC INTERFACE
+ 0x00, // 19 bDescriptorSubtype: NONE
+ 0x10, 0x01, // 20,21 bcdCDC: 1.10
+
+ // CDC Call Management Descriptor
+ 0x05, // 22 bLength
+ 0x24, // 23 bDescriptorType: CLASS SPECIFIC INTERFACE
+ 0x01, // 24 bDescriptorSubtype: CALL MANAGEMENT
+ 0x01, // 25 bmCapabilities
+ 0xFF, // 26 bDataInterface [SET AT RUNTIME]
+#define CDC_CALL_MANAGEMENT_DATA_INTERFACE_INDEX 26
+
+ // CDC Abstract Control Management Descriptor
+ 0x04, // 27 bLength
+ 0x24, // 28 bDescriptorType: CLASS SPECIFIC INTERFACE
+ 0x02, // 29 bDescriptorSubtype: ABSTRACT CONTROL MANAGEMENT
+ 0x02, // 30 bmCapabilities
+
+ // CDC Union Descriptor
+ 0x05, // 31 bLength
+ 0x24, // 32 bDescriptorType: CLASS SPECIFIC INTERFACE
+ 0x06, // 33 bDescriptorSubtype: CDC
+ 0xFF, // 34 bMasterInterface [SET AT RUNTIME]
+#define CDC_UNION_MASTER_INTERFACE_INDEX 34
+ 0xFF, // 35 bSlaveInterface_list (1 item)
+#define CDC_UNION_SLAVE_INTERFACE_INDEX 35
+
+ // CDC Control IN Endpoint Descriptor
+ 0x07, // 36 bLength
+ 0x05, // 37 bDescriptorType (Endpoint)
+ 0xFF, // 38 bEndpointAddress (IN/D2H) [SET AT RUNTIME: 0x80 | number]
+#define CDC_CONTROL_IN_ENDPOINT_INDEX 38
+ 0x03, // 39 bmAttributes (Interrupt)
+ 0x40, 0x00, // 40, 41 wMaxPacketSize 64
+ 0x10, // 42 bInterval 16 (unit depends on device speed)
+
+ // CDC Data Interface
+ 0x09, // 43 bLength
+ 0x04, // 44 bDescriptorType (Interface)
+ 0xFF, // 45 bInterfaceNumber [SET AT RUNTIME]
+#define CDC_DATA_INTERFACE_INDEX 45
+ 0x00, // 46 bAlternateSetting
+ 0x02, // 47 bNumEndpoints 2
+ 0x0A, // 48 bInterfaceClass: DATA
+ 0x00, // 49 bInterfaceSubClass: NONE
+ 0x00, // 50 bInterfaceProtocol
+ 0x05, // 51 iInterface (String Index)
+#define CDC_DATA_INTERFACE_STRING_INDEX 51
+
+ // CDC Data OUT Endpoint Descriptor
+ 0x07, // 52 bLength
+ 0x05, // 53 bDescriptorType (Endpoint)
+ 0xFF, // 54 bEndpointAddress (OUT/H2D) [SET AT RUNTIME]
+#define CDC_DATA_OUT_ENDPOINT_INDEX 54
+ 0x02, // 55 bmAttributes (Bulk)
+ #if USB_HIGHSPEED
+ 0x00, 0x02, // 56,57 wMaxPacketSize 512
+ #else
+ 0x40, 0x00, // 56,57 wMaxPacketSize 64
+ #endif
+ 0x00, // 58 bInterval 0 (unit depends on device speed)
+
+ // CDC Data IN Endpoint Descriptor
+ 0x07, // 59 bLength
+ 0x05, // 60 bDescriptorType (Endpoint)
+ 0xFF, // 61 bEndpointAddress (IN/D2H) [SET AT RUNTIME: 0x80 | number]
+#define CDC_DATA_IN_ENDPOINT_INDEX 61
+ 0x02, // 62 bmAttributes (Bulk)
+ #if USB_HIGHSPEED
+ 0x00, 0x02, // 63,64 wMaxPacketSize 512
+ #else
+ 0x40, 0x00, // 63,64 wMaxPacketSize 64
+ #endif
+ 0x00, // 65 bInterval 0 (unit depends on device speed)
+};
+
+static const char console_cdc_comm_interface_name[] = USB_INTERFACE_NAME " CDC control";
+static const char data_cdc_comm_interface_name[] = USB_INTERFACE_NAME " CDC2 control";
+static const char console_cdc_data_interface_name[] = USB_INTERFACE_NAME " CDC data";
+static const char data_cdc_data_interface_name[] = USB_INTERFACE_NAME " CDC2 data";
+
+// .idx is set later.
+
+static usb_cdc_serial_obj_t usb_cdc_console_obj = {
+ .base.type = &usb_cdc_serial_type,
+ .timeout = -1.0f,
+ .write_timeout = -1.0f,
+};
+
+static usb_cdc_serial_obj_t usb_cdc_data_obj = {
+ .base.type = &usb_cdc_serial_type,
+ .timeout = -1.0f,
+ .write_timeout = -1.0f,
+};
+
+static bool usb_cdc_console_is_enabled;
+static bool usb_cdc_data_is_enabled;
+
+void usb_cdc_set_defaults(void) {
+ common_hal_usb_cdc_enable(CIRCUITPY_USB_CDC_CONSOLE_ENABLED_DEFAULT,
+ CIRCUITPY_USB_CDC_DATA_ENABLED_DEFAULT);
+}
+
+bool usb_cdc_console_enabled(void) {
+ return usb_cdc_console_is_enabled;
+}
+
+bool usb_cdc_data_enabled(void) {
+ return usb_cdc_data_is_enabled;
+}
+
+size_t usb_cdc_descriptor_length(void) {
+ return sizeof(usb_cdc_descriptor_template);
+}
+
+size_t usb_cdc_add_descriptor(uint8_t *descriptor_buf, descriptor_counts_t *descriptor_counts, uint8_t *current_interface_string, bool console) {
+ memcpy(descriptor_buf, usb_cdc_descriptor_template, sizeof(usb_cdc_descriptor_template));
+
+ // Store comm interface number.
+ descriptor_buf[CDC_FIRST_INTERFACE_INDEX] = descriptor_counts->current_interface;
+ descriptor_buf[CDC_COMM_INTERFACE_INDEX] = descriptor_counts->current_interface;
+ descriptor_buf[CDC_UNION_MASTER_INTERFACE_INDEX] = descriptor_counts->current_interface;
+ descriptor_counts->current_interface++;
+
+ // Now store data interface number.
+ descriptor_buf[CDC_CALL_MANAGEMENT_DATA_INTERFACE_INDEX] = descriptor_counts->current_interface;
+ descriptor_buf[CDC_UNION_SLAVE_INTERFACE_INDEX] = descriptor_counts->current_interface;
+ descriptor_buf[CDC_DATA_INTERFACE_INDEX] = descriptor_counts->current_interface;
+ descriptor_counts->current_interface++;
+
+ descriptor_buf[CDC_CONTROL_IN_ENDPOINT_INDEX] = 0x80 | (
+ console
+ ? (USB_CDC_EP_NUM_NOTIFICATION ? USB_CDC_EP_NUM_NOTIFICATION : descriptor_counts->current_endpoint)
+ : (USB_CDC2_EP_NUM_NOTIFICATION ? USB_CDC2_EP_NUM_NOTIFICATION : descriptor_counts->current_endpoint));
+ descriptor_counts->num_in_endpoints++;
+ descriptor_counts->current_endpoint++;
+
+ descriptor_buf[CDC_DATA_IN_ENDPOINT_INDEX] = 0x80 | (
+ console
+ ? (USB_CDC_EP_NUM_DATA_IN ? USB_CDC_EP_NUM_DATA_IN : descriptor_counts->current_endpoint)
+ : (USB_CDC2_EP_NUM_DATA_IN ? USB_CDC2_EP_NUM_DATA_IN : descriptor_counts->current_endpoint));
+ descriptor_counts->num_in_endpoints++;
+ descriptor_buf[CDC_DATA_OUT_ENDPOINT_INDEX] =
+ console
+ ? (USB_CDC_EP_NUM_DATA_OUT ? USB_CDC_EP_NUM_DATA_OUT : descriptor_counts->current_endpoint)
+ : (USB_CDC2_EP_NUM_DATA_OUT ? USB_CDC2_EP_NUM_DATA_OUT : descriptor_counts->current_endpoint);
+ descriptor_counts->num_out_endpoints++;
+ descriptor_counts->current_endpoint++;
+
+ usb_add_interface_string(*current_interface_string,
+ console ? console_cdc_comm_interface_name : data_cdc_comm_interface_name);
+ descriptor_buf[CDC_COMM_INTERFACE_STRING_INDEX] = *current_interface_string;
+ (*current_interface_string)++;
+
+ usb_add_interface_string(*current_interface_string,
+ console ? console_cdc_data_interface_name : data_cdc_data_interface_name);
+ descriptor_buf[CDC_DATA_INTERFACE_STRING_INDEX] = *current_interface_string;
+ (*current_interface_string)++;
+
+ return sizeof(usb_cdc_descriptor_template);
+}
+
+bool common_hal_usb_cdc_disable(void) {
+ return common_hal_usb_cdc_enable(false, false);
+}
+
+bool common_hal_usb_cdc_enable(bool console, bool data) {
+ // We can't change the descriptors once we're connected.
+ if (tud_connected()) {
+ return false;
+ }
+
+ // Right now these objects contain no heap objects, but if that changes,
+ // they will need to be protected against gc.
+
+ // Assign only as many idx values as necessary. They must start at 0.
+ uint8_t idx = 0;
+ usb_cdc_console_is_enabled = console;
+ usb_cdc_set_console(console ? MP_OBJ_FROM_PTR(&usb_cdc_console_obj) : mp_const_none);
+ if (console) {
+ usb_cdc_console_obj.idx = idx;
+ idx++;
+ }
+
+ usb_cdc_data_is_enabled = data;
+ usb_cdc_set_data(data ? MP_OBJ_FROM_PTR(&usb_cdc_data_obj) : mp_const_none);
+ if (data) {
+ usb_cdc_data_obj.idx = idx;
+ }
+
+
+ return true;
+}
+
+#if CIRCUITPY_USB_VENDOR
+#include "usb_vendor_descriptors.h"
+
+#define BOS_TOTAL_LEN (TUD_BOS_DESC_LEN + TUD_BOS_WEBUSB_DESC_LEN + TUD_BOS_MICROSOFT_OS_DESC_LEN)
+
+#define MS_OS_20_DESC_LEN 0xB2
+
+// BOS Descriptor is required for webUSB
+uint8_t const desc_bos[] =
+{
+ // total length, number of device caps
+ TUD_BOS_DESCRIPTOR(BOS_TOTAL_LEN, 2),
+
+ // Vendor Code, iLandingPage
+ TUD_BOS_WEBUSB_DESCRIPTOR(VENDOR_REQUEST_WEBUSB, 1),
+
+ // Microsoft OS 2.0 descriptor
+ TUD_BOS_MS_OS_20_DESCRIPTOR(MS_OS_20_DESC_LEN, VENDOR_REQUEST_MICROSOFT)
+};
+
+uint8_t const *tud_descriptor_bos_cb(void) {
+ return desc_bos;
+}
+
+#define MS_OS_20_ITF_NUM_MAGIC 0x5b
+#define MS_OS_20_ITF_NUM_OFFSET 22
+
+const uint8_t ms_os_20_descriptor_template[] =
+{
+ // 10 Set header: length, type, windows version, total length
+ U16_TO_U8S_LE(0x000A), U16_TO_U8S_LE(MS_OS_20_SET_HEADER_DESCRIPTOR), U32_TO_U8S_LE(0x06030000), U16_TO_U8S_LE(MS_OS_20_DESC_LEN),
+
+ // 8 Configuration subset header: length, type, configuration index, reserved, configuration total length
+ U16_TO_U8S_LE(0x0008), U16_TO_U8S_LE(MS_OS_20_SUBSET_HEADER_CONFIGURATION), 0, 0, U16_TO_U8S_LE(MS_OS_20_DESC_LEN - 0x0A),
+
+ // 8 Function Subset header: length, type, first interface, reserved, subset length
+ U16_TO_U8S_LE(0x0008), U16_TO_U8S_LE(MS_OS_20_SUBSET_HEADER_FUNCTION), /* 22 */ MS_OS_20_ITF_NUM_MAGIC, 0, U16_TO_U8S_LE(MS_OS_20_DESC_LEN - 0x0A - 0x08),
+
+ // 20 MS OS 2.0 Compatible ID descriptor: length, type, compatible ID, sub compatible ID
+ U16_TO_U8S_LE(0x0014), U16_TO_U8S_LE(MS_OS_20_FEATURE_COMPATBLE_ID), 'W', 'I', 'N', 'U', 'S', 'B', 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // sub-compatible
+
+ // MS OS 2.0 Registry property descriptor: length, type
+ U16_TO_U8S_LE(MS_OS_20_DESC_LEN - 0x0A - 0x08 - 0x08 - 0x14), U16_TO_U8S_LE(MS_OS_20_FEATURE_REG_PROPERTY),
+ U16_TO_U8S_LE(0x0007), U16_TO_U8S_LE(0x002A), // wPropertyDataType, wPropertyNameLength and PropertyName "DeviceInterfaceGUIDs\0" in UTF-16
+ 'D', 0x00, 'e', 0x00, 'v', 0x00, 'i', 0x00, 'c', 0x00, 'e', 0x00, 'I', 0x00, 'n', 0x00, 't', 0x00, 'e', 0x00,
+ 'r', 0x00, 'f', 0x00, 'a', 0x00, 'c', 0x00, 'e', 0x00, 'G', 0x00, 'U', 0x00, 'I', 0x00, 'D', 0x00, 's', 0x00, 0x00, 0x00,
+ U16_TO_U8S_LE(0x0050), // wPropertyDataLength
+ // bPropertyData: “{975F44D9-0D08-43FD-8B3E-127CA8AFFF9D}”.
+ '{', 0x00, '9', 0x00, '7', 0x00, '5', 0x00, 'F', 0x00, '4', 0x00, '4', 0x00, 'D', 0x00, '9', 0x00, '-', 0x00,
+ '0', 0x00, 'D', 0x00, '0', 0x00, '8', 0x00, '-', 0x00, '4', 0x00, '3', 0x00, 'F', 0x00, 'D', 0x00, '-', 0x00,
+ '8', 0x00, 'B', 0x00, '3', 0x00, 'E', 0x00, '-', 0x00, '1', 0x00, '2', 0x00, '7', 0x00, 'C', 0x00, 'A', 0x00,
+ '8', 0x00, 'A', 0x00, 'F', 0x00, 'F', 0x00, 'F', 0x00, '9', 0x00, 'D', 0x00, '}', 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+TU_VERIFY_STATIC(sizeof(ms_os_20_descriptor_template) == MS_OS_20_DESC_LEN, "Incorrect size");
+
+static const uint8_t usb_vendor_descriptor_template[] = {
+ // Vendor Descriptor
+ 0x09, // 0 bLength
+ 0x04, // 1 bDescriptorType (Interface)
+ 0xFF, // 2 bInterfaceNumber [SET AT RUNTIME]
+#define VENDOR_INTERFACE_INDEX 2
+ 0x00, // 3 bAlternateSetting
+ 0x02, // 4 bNumEndpoints 2
+ 0xFF, // 5 bInterfaceClass: Vendor Specific
+ 0x00, // 6 bInterfaceSubClass: NONE
+ 0x00, // 7 bInterfaceProtocol: NONE
+ 0xFF, // 8 iInterface (String Index)
+#define VENDOR_INTERFACE_STRING_INDEX 8
+
+ // Vendor OUT Endpoint Descriptor
+ 0x07, // 9 bLength
+ 0x05, // 10 bDescriptorType (Endpoint)
+ 0xFF, // 11 bEndpointAddress (IN/D2H) [SET AT RUNTIME: number]
+#define VENDOR_OUT_ENDPOINT_INDEX 11
+ 0x02, // 12 bmAttributes (Bulk)
+ #if USB_HIGHSPEED
+ 0x00, 0x02, // 13,14 wMaxPacketSize 512
+ #else
+ 0x40, 0x00, // 13,14 wMaxPacketSize 64
+ #endif
+ 0x0, // 15 bInterval 0
+
+ // Vendor IN Endpoint Descriptor
+ 0x07, // 16 bLength
+ 0x05, // 17 bDescriptorType (Endpoint)
+ 0xFF, // 18 bEndpointAddress (IN/D2H) [SET AT RUNTIME: 0x80 | number]
+#define VENDOR_IN_ENDPOINT_INDEX 18
+ 0x02, // 19 bmAttributes (Bulk)
+ 0x40, 0x00, // 20, 21 wMaxPacketSize 64
+ 0x0 // 22 bInterval 0
+};
+
+static const char vendor_interface_name[] = USB_INTERFACE_NAME " WebUSB";
+
+
+bool usb_vendor_enabled(void) {
+ return usb_cdc_console_enabled();
+}
+
+size_t usb_vendor_descriptor_length(void) {
+ return sizeof(usb_vendor_descriptor_template);
+}
+
+static supervisor_allocation *ms_os_20_descriptor_allocation;
+
+size_t vendor_ms_os_20_descriptor_length() {
+ return sizeof(ms_os_20_descriptor_template);
+}
+uint8_t const *vendor_ms_os_20_descriptor() {
+ return (uint8_t *)ms_os_20_descriptor_allocation->ptr;
+}
+
+
+size_t usb_vendor_add_descriptor(uint8_t *descriptor_buf, descriptor_counts_t *descriptor_counts, uint8_t *current_interface_string) {
+
+ if (ms_os_20_descriptor_template[MS_OS_20_ITF_NUM_OFFSET] == MS_OS_20_ITF_NUM_MAGIC) {
+ ms_os_20_descriptor_allocation =
+ allocate_memory(align32_size(sizeof(ms_os_20_descriptor_template)),
+ /*high_address*/ false, /*movable*/ false);
+ uint8_t *ms_os_20_descriptor_buf = (uint8_t *)ms_os_20_descriptor_allocation->ptr;
+ memcpy(ms_os_20_descriptor_buf, ms_os_20_descriptor_template, sizeof(ms_os_20_descriptor_template));
+ ms_os_20_descriptor_buf[MS_OS_20_ITF_NUM_OFFSET] = descriptor_counts->current_interface;
+ ms_os_20_descriptor_buf[VENDOR_IN_ENDPOINT_INDEX] = 0x80 | descriptor_counts->current_endpoint;
+ ms_os_20_descriptor_buf[VENDOR_OUT_ENDPOINT_INDEX] = descriptor_counts->current_endpoint;
+ }
+
+ memcpy(descriptor_buf, usb_vendor_descriptor_template, sizeof(usb_vendor_descriptor_template));
+
+ descriptor_buf[VENDOR_INTERFACE_INDEX] = descriptor_counts->current_interface;
+ descriptor_counts->current_interface++;
+
+ descriptor_buf[VENDOR_IN_ENDPOINT_INDEX] = 0x80 | descriptor_counts->current_endpoint;
+ descriptor_counts->num_in_endpoints++;
+ descriptor_buf[VENDOR_OUT_ENDPOINT_INDEX] = descriptor_counts->current_endpoint;
+ descriptor_counts->num_out_endpoints++;
+ descriptor_counts->current_endpoint++;
+
+ usb_add_interface_string(*current_interface_string, vendor_interface_name);
+ descriptor_buf[VENDOR_INTERFACE_STRING_INDEX] = *current_interface_string;
+ (*current_interface_string)++;
+
+ return sizeof(usb_vendor_descriptor_template);
+}
+
+
+
+
+#endif
diff --git a/circuitpython/shared-module/usb_cdc/__init__.h b/circuitpython/shared-module/usb_cdc/__init__.h
new file mode 100644
index 0000000..87dabf0
--- /dev/null
+++ b/circuitpython/shared-module/usb_cdc/__init__.h
@@ -0,0 +1,51 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 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.
+ */
+
+#ifndef SHARED_MODULE_USB_CDC___INIT___H
+#define SHARED_MODULE_USB_CDC___INIT___H
+
+#include "py/mpconfig.h"
+#include "py/objtuple.h"
+#include "supervisor/usb.h"
+
+bool usb_cdc_console_enabled(void);
+bool usb_cdc_data_enabled(void);
+
+void usb_cdc_set_defaults(void);
+
+size_t usb_cdc_descriptor_length(void);
+size_t usb_cdc_add_descriptor(uint8_t *descriptor_buf, descriptor_counts_t *descriptor_counts, uint8_t *current_interface_string, bool console);
+
+#if CIRCUITPY_USB_VENDOR
+bool usb_vendor_enabled(void);
+size_t usb_vendor_descriptor_length(void);
+size_t usb_vendor_add_descriptor(uint8_t *descriptor_buf, descriptor_counts_t *descriptor_counts, uint8_t *current_interface_string);
+
+size_t vendor_ms_os_20_descriptor_length(void);
+uint8_t const *vendor_ms_os_20_descriptor(void);
+#endif
+
+#endif /* SHARED_MODULE_USB_CDC___INIT___H */
diff --git a/circuitpython/shared-module/usb_hid/Device.c b/circuitpython/shared-module/usb_hid/Device.c
new file mode 100644
index 0000000..8a0c429
--- /dev/null
+++ b/circuitpython/shared-module/usb_hid/Device.c
@@ -0,0 +1,314 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 hathach 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.
+ */
+
+#include <string.h>
+
+#include "py/gc.h"
+#include "py/runtime.h"
+#include "shared-bindings/usb_hid/Device.h"
+#include "shared-module/usb_hid/__init__.h"
+#include "shared-module/usb_hid/Device.h"
+#include "supervisor/shared/translate.h"
+#include "supervisor/shared/tick.h"
+#include "tusb.h"
+
+static const uint8_t keyboard_report_descriptor[] = {
+ 0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
+ 0x09, 0x06, // Usage (Keyboard)
+ 0xA1, 0x01, // Collection (Application)
+ 0x85, 0x01, // Report ID (1)
+ 0x05, 0x07, // Usage Page (Kbrd/Keypad)
+ 0x19, 0xE0, // Usage Minimum (0xE0)
+ 0x29, 0xE7, // Usage Maximum (0xE7)
+ 0x15, 0x00, // Logical Minimum (0)
+ 0x25, 0x01, // Logical Maximum (1)
+ 0x75, 0x01, // Report Size (1)
+ 0x95, 0x08, // Report Count (8)
+ 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ 0x95, 0x01, // Report Count (1)
+ 0x75, 0x08, // Report Size (8)
+ 0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ 0x95, 0x03, // Report Count (3)
+ 0x75, 0x01, // Report Size (1)
+ 0x05, 0x08, // Usage Page (LEDs)
+ 0x19, 0x01, // Usage Minimum (Num Lock)
+ 0x29, 0x05, // Usage Maximum (Kana)
+ 0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+ 0x95, 0x01, // Report Count (1)
+ 0x75, 0x05, // Report Size (5)
+ 0x91, 0x01, // Output (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+ 0x95, 0x06, // Report Count (6)
+ 0x75, 0x08, // Report Size (8)
+ 0x15, 0x00, // Logical Minimum (0)
+ 0x26, 0xFF, 0x00, // Logical Maximum (255)
+ 0x05, 0x07, // Usage Page (Kbrd/Keypad)
+ 0x19, 0x00, // Usage Minimum (0x00)
+ 0x2A, 0xFF, 0x00, // Usage Maximum (0xFF)
+ 0x81, 0x00, // Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ 0xC0, // End Collection
+};
+
+const usb_hid_device_obj_t usb_hid_device_keyboard_obj = {
+ .base = {
+ .type = &usb_hid_device_type,
+ },
+ .report_descriptor = keyboard_report_descriptor,
+ .report_descriptor_length = sizeof(keyboard_report_descriptor),
+ .usage_page = 0x01,
+ .usage = 0x06,
+ .num_report_ids = 1,
+ .report_ids = { 0x01, },
+ .in_report_lengths = { 8, },
+ .out_report_lengths = { 1, },
+};
+
+static const uint8_t mouse_report_descriptor[] = {
+ 0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
+ 0x09, 0x02, // Usage (Mouse)
+ 0xA1, 0x01, // Collection (Application)
+ 0x09, 0x01, // Usage (Pointer)
+ 0xA1, 0x00, // Collection (Physical)
+ 0x85, 0x02, // 10, 11 Report ID (2)
+ 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
+};
+
+const usb_hid_device_obj_t usb_hid_device_mouse_obj = {
+ .base = {
+ .type = &usb_hid_device_type,
+ },
+ .report_descriptor = mouse_report_descriptor,
+ .report_descriptor_length = sizeof(mouse_report_descriptor),
+ .usage_page = 0x01,
+ .usage = 0x02,
+ .num_report_ids = 1,
+ .report_ids = { 0x02, },
+ .in_report_lengths = { 4, },
+ .out_report_lengths = { 0, },
+};
+
+static const uint8_t consumer_control_report_descriptor[] = {
+ 0x05, 0x0C, // Usage Page (Consumer)
+ 0x09, 0x01, // Usage (Consumer Control)
+ 0xA1, 0x01, // Collection (Application)
+ 0x85, 0x03, // Report ID (3)
+ 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
+};
+
+const usb_hid_device_obj_t usb_hid_device_consumer_control_obj = {
+ .base = {
+ .type = &usb_hid_device_type,
+ },
+ .report_descriptor = consumer_control_report_descriptor,
+ .report_descriptor_length = sizeof(consumer_control_report_descriptor),
+ .usage_page = 0x0C,
+ .usage = 0x01,
+ .num_report_ids = 1,
+ .report_ids = { 0x03 },
+ .in_report_lengths = { 2, },
+ .out_report_lengths = { 0, },
+};
+
+STATIC size_t get_report_id_idx(usb_hid_device_obj_t *self, size_t report_id) {
+ for (size_t i = 0; i < self->num_report_ids; i++) {
+ if (report_id == self->report_ids[i]) {
+ return i;
+ }
+ }
+ return CIRCUITPY_USB_HID_MAX_REPORT_IDS_PER_DESCRIPTOR;
+}
+
+// See if report_id is used by this device. If it is -1, then return the sole report id used by this device,
+// which might be 0 if no report_id was supplied.
+uint8_t common_hal_usb_hid_device_validate_report_id(usb_hid_device_obj_t *self, mp_int_t report_id_arg) {
+ if (report_id_arg == -1 && self->num_report_ids == 1) {
+ return self->report_ids[0];
+ }
+ if (!(report_id_arg >= 0 &&
+ get_report_id_idx(self, (size_t)report_id_arg) < CIRCUITPY_USB_HID_MAX_REPORT_IDS_PER_DESCRIPTOR)) {
+ mp_raise_ValueError_varg(translate("Invalid %q"), MP_QSTR_report_id);
+ }
+ return (uint8_t)report_id_arg;
+}
+
+void common_hal_usb_hid_device_construct(usb_hid_device_obj_t *self, mp_obj_t report_descriptor, uint16_t usage_page, uint16_t usage, size_t num_report_ids, uint8_t *report_ids, uint8_t *in_report_lengths, uint8_t *out_report_lengths) {
+ if (num_report_ids > CIRCUITPY_USB_HID_MAX_REPORT_IDS_PER_DESCRIPTOR) {
+ mp_raise_ValueError_varg(translate("More than %d report ids not supported"),
+ CIRCUITPY_USB_HID_MAX_REPORT_IDS_PER_DESCRIPTOR);
+ }
+
+ // report buffer pointers are NULL at start, and are created when USB is initialized.
+ mp_buffer_info_t bufinfo;
+ mp_get_buffer_raise(report_descriptor, &bufinfo, MP_BUFFER_READ);
+ self->report_descriptor_length = bufinfo.len;
+
+ // Copy the raw descriptor bytes into a heap obj. We don't keep the Python descriptor object.
+
+ uint8_t *descriptor_bytes = gc_alloc(bufinfo.len, false, false);
+ memcpy(descriptor_bytes, bufinfo.buf, bufinfo.len);
+ self->report_descriptor = descriptor_bytes;
+
+ self->usage_page = usage_page;
+ self->usage = usage;
+ self->num_report_ids = num_report_ids;
+ memcpy(self->report_ids, report_ids, num_report_ids);
+ memcpy(self->in_report_lengths, in_report_lengths, num_report_ids);
+ memcpy(self->out_report_lengths, out_report_lengths, num_report_ids);
+}
+
+uint16_t common_hal_usb_hid_device_get_usage_page(usb_hid_device_obj_t *self) {
+ return self->usage_page;
+}
+
+uint16_t common_hal_usb_hid_device_get_usage(usb_hid_device_obj_t *self) {
+ return self->usage;
+}
+
+void common_hal_usb_hid_device_send_report(usb_hid_device_obj_t *self, uint8_t *report, uint8_t len, uint8_t report_id) {
+ // report_id and len have already been validated for this device.
+ size_t id_idx = get_report_id_idx(self, report_id);
+
+ if (len != self->in_report_lengths[id_idx]) {
+ mp_raise_ValueError_varg(translate("Buffer incorrect size. Should be %d bytes."),
+ self->in_report_lengths[id_idx]);
+ }
+
+ // Wait until interface is ready, timeout = 2 seconds
+ uint64_t end_ticks = supervisor_ticks_ms64() + 2000;
+ while ((supervisor_ticks_ms64() < end_ticks) && !tud_hid_ready()) {
+ RUN_BACKGROUND_TASKS;
+ }
+
+ if (!tud_hid_ready()) {
+ mp_raise_msg(&mp_type_OSError, translate("USB busy"));
+ }
+
+ if (!tud_hid_report(report_id, report, len)) {
+ mp_raise_msg(&mp_type_OSError, translate("USB error"));
+ }
+}
+
+mp_obj_t common_hal_usb_hid_device_get_last_received_report(usb_hid_device_obj_t *self, uint8_t report_id) {
+ // report_id has already been validated for this device.
+ size_t id_idx = get_report_id_idx(self, report_id);
+ return mp_obj_new_bytes(self->out_report_buffers[id_idx], self->out_report_lengths[id_idx]);
+}
+
+void usb_hid_device_create_report_buffers(usb_hid_device_obj_t *self) {
+ for (size_t i = 0; i < self->num_report_ids; i++) {
+ // The IN buffers are used only for tud_hid_get_report_cb(),
+ // which is an unusual case. Normally we can just pass the data directly with tud_hid_report().
+ self->in_report_buffers[i] =
+ self->in_report_lengths[i] > 0
+ ? gc_alloc(self->in_report_lengths[i], false, true /*long-lived*/)
+ : NULL;
+
+ self->out_report_buffers[i] =
+ self->out_report_lengths[i] > 0
+ ? gc_alloc(self->out_report_lengths[i], false, true /*long-lived*/)
+ : NULL;
+ }
+}
+
+
+// Callback invoked when we receive Get_Report request through control endpoint
+uint16_t tud_hid_get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen) {
+ (void)itf;
+ // Support Input Report and Feature Report
+ if (report_type != HID_REPORT_TYPE_INPUT && report_type != HID_REPORT_TYPE_FEATURE) {
+ return 0;
+ }
+
+ // fill buffer with current report
+
+ usb_hid_device_obj_t *hid_device;
+ size_t id_idx;
+ // Find device with this report id, and get the report id index.
+ if (usb_hid_get_device_with_report_id(report_id, &hid_device, &id_idx)) {
+ // Make sure buffer exists before trying to copy into it.
+ if (hid_device->in_report_buffers[id_idx]) {
+ memcpy(buffer, hid_device->in_report_buffers[id_idx], reqlen);
+ return reqlen;
+ }
+ }
+ return 0;
+}
+
+// Callback invoked when we receive Set_Report request through control endpoint
+void tud_hid_set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize) {
+ (void)itf;
+ if (report_type == HID_REPORT_TYPE_INVALID) {
+ report_id = buffer[0];
+ buffer++;
+ bufsize--;
+ } else if (report_type != HID_REPORT_TYPE_OUTPUT && report_type != HID_REPORT_TYPE_FEATURE) {
+ return;
+ }
+
+ usb_hid_device_obj_t *hid_device;
+ size_t id_idx;
+ // Find device with this report id, and get the report id index.
+ if (usb_hid_get_device_with_report_id(report_id, &hid_device, &id_idx)) {
+ // If a report of the correct size has been read, save it in the proper OUT report buffer.
+ if (hid_device &&
+ hid_device->out_report_buffers[id_idx] &&
+ hid_device->out_report_lengths[id_idx] >= bufsize) {
+ memcpy(hid_device->out_report_buffers[id_idx], buffer, bufsize);
+ }
+ }
+}
diff --git a/circuitpython/shared-module/usb_hid/Device.h b/circuitpython/shared-module/usb_hid/Device.h
new file mode 100644
index 0000000..f265712
--- /dev/null
+++ b/circuitpython/shared-module/usb_hid/Device.h
@@ -0,0 +1,56 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 hathach 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.
+ */
+
+#ifndef SHARED_MODULE_USB_HID_DEVICE_H
+#define SHARED_MODULE_USB_HID_DEVICE_H
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "py/obj.h"
+
+typedef struct {
+ mp_obj_base_t base;
+ // Python buffer object whose contents are the descriptor.
+ const uint8_t *report_descriptor;
+ uint8_t *in_report_buffers[CIRCUITPY_USB_HID_MAX_REPORT_IDS_PER_DESCRIPTOR];
+ uint8_t *out_report_buffers[CIRCUITPY_USB_HID_MAX_REPORT_IDS_PER_DESCRIPTOR];
+ uint16_t report_descriptor_length;
+ uint8_t report_ids[CIRCUITPY_USB_HID_MAX_REPORT_IDS_PER_DESCRIPTOR];
+ uint8_t in_report_lengths[CIRCUITPY_USB_HID_MAX_REPORT_IDS_PER_DESCRIPTOR];
+ uint8_t out_report_lengths[CIRCUITPY_USB_HID_MAX_REPORT_IDS_PER_DESCRIPTOR];
+ uint16_t usage_page;
+ uint16_t usage;
+ uint8_t num_report_ids;
+} usb_hid_device_obj_t;
+
+extern const usb_hid_device_obj_t usb_hid_device_keyboard_obj;
+extern const usb_hid_device_obj_t usb_hid_device_mouse_obj;
+extern const usb_hid_device_obj_t usb_hid_device_consumer_control_obj;
+
+void usb_hid_device_create_report_buffers(usb_hid_device_obj_t *self);
+
+#endif /* SHARED_MODULE_USB_HID_DEVICE_H */
diff --git a/circuitpython/shared-module/usb_hid/__init__.c b/circuitpython/shared-module/usb_hid/__init__.c
new file mode 100644
index 0000000..89a05c7
--- /dev/null
+++ b/circuitpython/shared-module/usb_hid/__init__.c
@@ -0,0 +1,357 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 hathach 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.
+ */
+
+#include <string.h>
+
+#include "tusb.h"
+
+#include "py/gc.h"
+#include "py/runtime.h"
+#include "shared-bindings/usb_hid/__init__.h"
+#include "shared-bindings/usb_hid/Device.h"
+#include "supervisor/memory.h"
+#include "supervisor/usb.h"
+
+static const uint8_t usb_hid_descriptor_template[] = {
+ 0x09, // 0 bLength
+ 0x04, // 1 bDescriptorType (Interface)
+ 0xFF, // 2 bInterfaceNumber 3
+#define HID_DESCRIPTOR_INTERFACE_INDEX (2)
+ 0x00, // 3 bAlternateSetting
+ 0x02, // 4 bNumEndpoints 2
+ 0x03, // 5 bInterfaceClass: HID
+ 0x00, // 6 bInterfaceSubClass: NOBOOT
+#define HID_DESCRIPTOR_SUBCLASS_INDEX (6)
+ 0x00, // 7 bInterfaceProtocol: NONE
+#define HID_DESCRIPTOR_INTERFACE_PROTOCOL_INDEX (7)
+ 0xFF, // 8 iInterface (String Index) [SET AT RUNTIME]
+#define HID_DESCRIPTOR_INTERFACE_STRING_INDEX (8)
+
+ 0x09, // 9 bLength
+ 0x21, // 10 bDescriptorType (HID)
+ 0x11, 0x01, // 11,12 bcdHID 1.11
+ 0x00, // 13 bCountryCode
+ 0x01, // 14 bNumDescriptors
+ 0x22, // 15 bDescriptorType[0] (HID)
+ 0xFF, 0xFF, // 16,17 wDescriptorLength[0] [SET AT RUNTIME: lo, hi]
+#define HID_DESCRIPTOR_LENGTH_INDEX (16)
+
+ 0x07, // 18 bLength
+ 0x05, // 19 bDescriptorType (Endpoint)
+ 0xFF, // 20 bEndpointAddress (IN/D2H) [SET AT RUNTIME: 0x80 | endpoint]
+#define HID_IN_ENDPOINT_INDEX (20)
+ 0x03, // 21 bmAttributes (Interrupt)
+ 0x40, 0x00, // 22,23 wMaxPacketSize 64
+ 0x08, // 24 bInterval 8 (unit depends on device speed)
+
+ 0x07, // 25 bLength
+ 0x05, // 26 bDescriptorType (Endpoint)
+ 0xFF, // 27 bEndpointAddress (OUT/H2D) [SET AT RUNTIME]
+#define HID_OUT_ENDPOINT_INDEX (27)
+ 0x03, // 28 bmAttributes (Interrupt)
+ 0x40, 0x00, // 29,30 wMaxPacketSize 64
+ 0x08, // 31 bInterval 8 (unit depends on device speed)
+};
+
+#define MAX_HID_DEVICES 8
+
+static supervisor_allocation *hid_report_descriptor_allocation;
+static usb_hid_device_obj_t hid_devices[MAX_HID_DEVICES];
+// If 0, USB HID is disabled.
+static mp_int_t num_hid_devices;
+
+// Which boot device is available? 0: no boot devices, 1: boot keyboard, 2: boot mouse.
+// This value is set by usb_hid.enable(), and used to build the HID interface descriptor.
+// The value is remembered here from boot.py to code.py.
+static uint8_t hid_boot_device;
+
+// Whether a boot device was requested by a SET_PROTOCOL request from the host.
+static bool hid_boot_device_requested;
+
+// This tuple is store in usb_hid.devices.
+static mp_obj_tuple_t *hid_devices_tuple;
+
+static mp_obj_tuple_t default_hid_devices_tuple = {
+ .base = {
+ .type = &mp_type_tuple,
+ },
+ .len = 3,
+ .items = {
+ MP_OBJ_FROM_PTR(&usb_hid_device_keyboard_obj),
+ MP_OBJ_FROM_PTR(&usb_hid_device_mouse_obj),
+ MP_OBJ_FROM_PTR(&usb_hid_device_consumer_control_obj),
+ },
+};
+
+// These describe the standard descriptors used for boot keyboard and mouse, which don't use report IDs.
+// When the host requests a boot device, replace whatever HID devices were enabled with a tuple
+// containing just one of these, since the host is uninterested in other devices.
+// The driver code will then use the proper report length and send_report() will not send a report ID.
+static const usb_hid_device_obj_t boot_keyboard_obj = {
+ .base = {
+ .type = &usb_hid_device_type,
+ },
+ .report_descriptor = NULL,
+ .report_descriptor_length = 0,
+ .usage_page = 0x01,
+ .usage = 0x06,
+ .num_report_ids = 1,
+ .report_ids = { 0, },
+ .in_report_lengths = { 8, },
+ .out_report_lengths = { 1, },
+};
+
+static const usb_hid_device_obj_t boot_mouse_obj = {
+ .base = {
+ .type = &usb_hid_device_type,
+ },
+ .report_descriptor = NULL,
+ .report_descriptor_length = 0,
+ .usage_page = 0x01,
+ .usage = 0x02,
+ .num_report_ids = 1,
+ .report_ids = { 0, },
+ .in_report_lengths = { 4, },
+ .out_report_lengths = { 0, },
+};
+
+bool usb_hid_enabled(void) {
+ return num_hid_devices > 0;
+}
+
+uint8_t usb_hid_boot_device(void) {
+ return hid_boot_device;
+}
+
+// Returns 1 or 2 if host requested a boot device and boot protocol was enabled in the interface descriptor.
+uint8_t common_hal_usb_hid_get_boot_device(void) {
+ return hid_boot_device_requested ? hid_boot_device : 0;
+}
+
+void usb_hid_set_defaults(void) {
+ hid_boot_device = 0;
+ hid_boot_device_requested = false;
+ common_hal_usb_hid_enable(
+ CIRCUITPY_USB_HID_ENABLED_DEFAULT ? &default_hid_devices_tuple : mp_const_empty_tuple, 0);
+}
+
+// This is the interface descriptor, not the report descriptor.
+size_t usb_hid_descriptor_length(void) {
+ return sizeof(usb_hid_descriptor_template);
+}
+
+static const char usb_hid_interface_name[] = USB_INTERFACE_NAME " HID";
+
+// This is the interface descriptor, not the report descriptor.
+size_t usb_hid_add_descriptor(uint8_t *descriptor_buf, descriptor_counts_t *descriptor_counts, uint8_t *current_interface_string, uint16_t report_descriptor_length, uint8_t boot_device) {
+ memcpy(descriptor_buf, usb_hid_descriptor_template, sizeof(usb_hid_descriptor_template));
+
+ descriptor_buf[HID_DESCRIPTOR_INTERFACE_INDEX] = descriptor_counts->current_interface;
+ descriptor_counts->current_interface++;
+
+ if (boot_device > 0) {
+ descriptor_buf[HID_DESCRIPTOR_SUBCLASS_INDEX] = 1; // BOOT protocol (device) available.
+ descriptor_buf[HID_DESCRIPTOR_INTERFACE_PROTOCOL_INDEX] = boot_device; // 1: keyboard, 2: mouse
+ }
+
+ usb_add_interface_string(*current_interface_string, usb_hid_interface_name);
+ descriptor_buf[HID_DESCRIPTOR_INTERFACE_STRING_INDEX] = *current_interface_string;
+ (*current_interface_string)++;
+
+ descriptor_buf[HID_DESCRIPTOR_LENGTH_INDEX] = report_descriptor_length & 0xFF;
+ descriptor_buf[HID_DESCRIPTOR_LENGTH_INDEX + 1] = (report_descriptor_length >> 8);
+
+ descriptor_buf[HID_IN_ENDPOINT_INDEX] =
+ 0x80 | (USB_HID_EP_NUM_IN ? USB_HID_EP_NUM_IN : descriptor_counts->current_endpoint);
+ descriptor_counts->num_in_endpoints++;
+ descriptor_buf[HID_OUT_ENDPOINT_INDEX] =
+ USB_HID_EP_NUM_OUT ? USB_HID_EP_NUM_OUT : descriptor_counts->current_endpoint;
+ descriptor_counts->num_out_endpoints++;
+ descriptor_counts->current_endpoint++;
+
+ return sizeof(usb_hid_descriptor_template);
+}
+
+// Make up a fresh tuple containing the device objects saved in the static
+// devices table. Save the tuple in usb_hid.devices.
+static void usb_hid_set_devices_from_hid_devices(void) {
+ mp_obj_t tuple_items[num_hid_devices];
+ for (mp_int_t i = 0; i < num_hid_devices; i++) {
+ tuple_items[i] = &hid_devices[i];
+ }
+
+ // Remember tuple for gc purposes.
+ hid_devices_tuple = mp_obj_new_tuple(num_hid_devices, tuple_items);
+ usb_hid_set_devices(hid_devices_tuple);
+}
+
+bool common_hal_usb_hid_disable(void) {
+ return common_hal_usb_hid_enable(mp_const_empty_tuple, 0);
+}
+
+bool common_hal_usb_hid_enable(const mp_obj_t devices, uint8_t boot_device) {
+ // We can't change the devices once we're connected.
+ if (tud_connected()) {
+ return false;
+ }
+
+ const mp_int_t num_devices = MP_OBJ_SMALL_INT_VALUE(mp_obj_len(devices));
+ if (num_devices > MAX_HID_DEVICES) {
+ mp_raise_ValueError_varg(translate("No more than %d HID devices allowed"), MAX_HID_DEVICES);
+ }
+
+ num_hid_devices = num_devices;
+
+ hid_boot_device = boot_device;
+
+ // Remember the devices in static storage so they live across VMs.
+ for (mp_int_t i = 0; i < num_hid_devices; i++) {
+ // devices has already been validated to contain only usb_hid_device_obj_t objects.
+ usb_hid_device_obj_t *device =
+ MP_OBJ_TO_PTR(mp_obj_subscr(devices, MP_OBJ_NEW_SMALL_INT(i), MP_OBJ_SENTINEL));
+ memcpy(&hid_devices[i], device, sizeof(usb_hid_device_obj_t));
+ }
+
+ usb_hid_set_devices_from_hid_devices();
+
+ return true;
+}
+
+// Called when HID devices are ready to be used, when code.py or the REPL starts running.
+void usb_hid_setup_devices(void) {
+
+ // If the host requested a boot device, replace the current list of devices
+ // with a single-element tuple containing the proper boot device.
+ if (hid_boot_device_requested) {
+ memcpy(&hid_devices[0],
+ // Will be 1 (keyboard) or 2 (mouse).
+ hid_boot_device == 1 ? &boot_keyboard_obj : &boot_mouse_obj,
+ sizeof(usb_hid_device_obj_t));
+ num_hid_devices = 1;
+ }
+
+ usb_hid_set_devices_from_hid_devices();
+
+ // Create report buffers on the heap.
+ for (mp_int_t i = 0; i < num_hid_devices; i++) {
+ usb_hid_device_create_report_buffers(&hid_devices[i]);
+ }
+}
+
+// Total length of the report descriptor, with all configured devices.
+size_t usb_hid_report_descriptor_length(void) {
+ size_t total_hid_report_descriptor_length = 0;
+ for (mp_int_t i = 0; i < num_hid_devices; i++) {
+ total_hid_report_descriptor_length += hid_devices[i].report_descriptor_length;
+ }
+
+ return total_hid_report_descriptor_length;
+}
+
+// Build the combined HID report descriptor in the given space.
+void usb_hid_build_report_descriptor(uint8_t *report_descriptor_space, size_t report_descriptor_length) {
+ if (!usb_hid_enabled()) {
+ return;
+ }
+
+ uint8_t *report_descriptor_start = report_descriptor_space;
+
+ for (mp_int_t i = 0; i < num_hid_devices; i++) {
+ usb_hid_device_obj_t *device = &hid_devices[i];
+ // Copy the report descriptor for this device.
+ memcpy(report_descriptor_start, device->report_descriptor, device->report_descriptor_length);
+
+ // Advance to the next free chunk for the next report descriptor.x
+ report_descriptor_start += device->report_descriptor_length;
+
+ // Clear the heap pointer to the bytes of the descriptor.
+ // We don't need it any more and it will get lost when the heap goes away.
+ device->report_descriptor = NULL;
+ }
+}
+
+// Call this after the heap and VM are finished.
+void usb_hid_save_report_descriptor(uint8_t *report_descriptor_space, size_t report_descriptor_length) {
+ if (!usb_hid_enabled()) {
+ return;
+ }
+
+ // Allocate storage that persists across VMs to hold the combined report descriptor.
+ // and to remember the device details.
+
+ // Copy the descriptor from the temporary area to a supervisor storage allocation that
+ // will leave between VM instantiations.
+ hid_report_descriptor_allocation =
+ allocate_memory(align32_size(report_descriptor_length),
+ /*high_address*/ false, /*movable*/ false);
+ memcpy((uint8_t *)hid_report_descriptor_allocation->ptr, report_descriptor_space, report_descriptor_length);
+}
+
+void usb_hid_gc_collect(void) {
+ gc_collect_ptr(hid_devices_tuple);
+
+ // Mark possible heap pointers in the static device list as in use.
+ for (mp_int_t device_idx = 0; device_idx < num_hid_devices; device_idx++) {
+
+ // Cast away the const for .report_descriptor. It could be in flash or on the heap.
+ // Constant report descriptors must be const so that they are used directly from flash
+ // and not copied into RAM.
+ gc_collect_ptr((void *)hid_devices[device_idx].report_descriptor);
+
+ // Collect all the report buffers for this device.
+ for (size_t id_idx = 0; id_idx < hid_devices[device_idx].num_report_ids; id_idx++) {
+ gc_collect_ptr(hid_devices[device_idx].in_report_buffers[id_idx]);
+ gc_collect_ptr(hid_devices[device_idx].out_report_buffers[id_idx]);
+ }
+ }
+}
+
+bool usb_hid_get_device_with_report_id(uint8_t report_id, usb_hid_device_obj_t **device_out, size_t *id_idx_out) {
+ for (uint8_t device_idx = 0; device_idx < num_hid_devices; device_idx++) {
+ usb_hid_device_obj_t *device = &hid_devices[device_idx];
+ for (size_t id_idx = 0; id_idx < device->num_report_ids; id_idx++) {
+ if (device->report_ids[id_idx] == report_id) {
+ *device_out = device;
+ *id_idx_out = id_idx;
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+// Callback invoked when we receive a GET HID REPORT DESCRIPTOR
+// Application returns pointer to descriptor
+// Descriptor contents must exist long enough for transfer to complete
+uint8_t const *tud_hid_descriptor_report_cb(uint8_t itf) {
+ return (uint8_t *)hid_report_descriptor_allocation->ptr;
+}
+
+// Callback invoked when we receive a SET_PROTOCOL request.
+// Protocol is either HID_PROTOCOL_BOOT (0) or HID_PROTOCOL_REPORT (1)
+void tud_hid_set_protocol_cb(uint8_t instance, uint8_t protocol) {
+ hid_boot_device_requested = (protocol == HID_PROTOCOL_BOOT);
+}
diff --git a/circuitpython/shared-module/usb_hid/__init__.h b/circuitpython/shared-module/usb_hid/__init__.h
new file mode 100644
index 0000000..1a33540
--- /dev/null
+++ b/circuitpython/shared-module/usb_hid/__init__.h
@@ -0,0 +1,52 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 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.
+ */
+
+#ifndef SHARED_MODULE_USB_HID___INIT___H
+#define SHARED_MODULE_USB_HID___INIT___H
+
+#include "shared-module/usb_hid/Device.h"
+#include "supervisor/usb.h"
+
+extern usb_hid_device_obj_t usb_hid_devices[];
+
+bool usb_hid_enabled(void);
+uint8_t usb_hid_boot_device(void);
+void usb_hid_set_defaults(void);
+
+size_t usb_hid_add_descriptor(uint8_t *descriptor_buf, descriptor_counts_t *descriptor_counts, uint8_t *current_interface_string, uint16_t report_descriptor_length, uint8_t boot_device);
+size_t usb_hid_descriptor_length(void);
+size_t usb_hid_report_descriptor_length(void);
+
+void usb_hid_setup_devices(void);
+size_t usb_hid_report_descriptor_length(void);
+void usb_hid_build_report_descriptor(uint8_t *report_descriptor_space, size_t report_descriptor_length);
+void usb_hid_save_report_descriptor(uint8_t *report_descriptor_space, size_t report_descriptor_length);
+
+bool usb_hid_get_device_with_report_id(uint8_t report_id, usb_hid_device_obj_t **device_out, size_t *id_idx_out);
+
+void usb_hid_gc_collect(void);
+
+#endif // SHARED_MODULE_USB_HID___INIT___H
diff --git a/circuitpython/shared-module/usb_midi/PortIn.c b/circuitpython/shared-module/usb_midi/PortIn.c
new file mode 100644
index 0000000..b16b77c
--- /dev/null
+++ b/circuitpython/shared-module/usb_midi/PortIn.c
@@ -0,0 +1,38 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Scott Shawcroft 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.
+ */
+
+#include "shared-bindings/usb_midi/PortIn.h"
+#include "shared-module/usb_midi/PortIn.h"
+#include "supervisor/shared/translate.h"
+#include "tusb.h"
+
+size_t common_hal_usb_midi_portin_read(usb_midi_portin_obj_t *self, uint8_t *data, size_t len, int *errcode) {
+ return tud_midi_stream_read(data, len);
+}
+
+uint32_t common_hal_usb_midi_portin_bytes_available(usb_midi_portin_obj_t *self) {
+ return tud_midi_available();
+}
diff --git a/circuitpython/shared-module/usb_midi/PortIn.h b/circuitpython/shared-module/usb_midi/PortIn.h
new file mode 100644
index 0000000..2f72aa4
--- /dev/null
+++ b/circuitpython/shared-module/usb_midi/PortIn.h
@@ -0,0 +1,39 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Scott Shawcroft 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.
+ */
+
+#ifndef SHARED_MODULE_USB_MIDI_PORTIN_H
+#define SHARED_MODULE_USB_MIDI_PORTIN_H
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "py/obj.h"
+
+typedef struct {
+ mp_obj_base_t base;
+} usb_midi_portin_obj_t;
+
+#endif /* SHARED_MODULE_USB_MIDI_PORTIN_H */
diff --git a/circuitpython/shared-module/usb_midi/PortOut.c b/circuitpython/shared-module/usb_midi/PortOut.c
new file mode 100644
index 0000000..4005d8b
--- /dev/null
+++ b/circuitpython/shared-module/usb_midi/PortOut.c
@@ -0,0 +1,38 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Scott Shawcroft 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.
+ */
+
+#include "shared-bindings/usb_midi/PortOut.h"
+#include "shared-module/usb_midi/PortOut.h"
+#include "supervisor/shared/translate.h"
+#include "tusb.h"
+
+size_t common_hal_usb_midi_portout_write(usb_midi_portout_obj_t *self, const uint8_t *data, size_t len, int *errcode) {
+ return tud_midi_stream_write(0, data, len);
+}
+
+bool common_hal_usb_midi_portout_ready_to_tx(usb_midi_portout_obj_t *self) {
+ return tud_midi_mounted();
+}
diff --git a/circuitpython/shared-module/usb_midi/PortOut.h b/circuitpython/shared-module/usb_midi/PortOut.h
new file mode 100644
index 0000000..6b1b884
--- /dev/null
+++ b/circuitpython/shared-module/usb_midi/PortOut.h
@@ -0,0 +1,39 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Scott Shawcroft 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.
+ */
+
+#ifndef SHARED_MODULE_USB_MIDI_PORTOUT_H
+#define SHARED_MODULE_USB_MIDI_PORTOUT_H
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "py/obj.h"
+
+typedef struct {
+ mp_obj_base_t base;
+} usb_midi_portout_obj_t;
+
+#endif /* SHARED_MODULE_USB_MIDI_PORTOUT_H */
diff --git a/circuitpython/shared-module/usb_midi/__init__.c b/circuitpython/shared-module/usb_midi/__init__.c
new file mode 100644
index 0000000..8cac2ba
--- /dev/null
+++ b/circuitpython/shared-module/usb_midi/__init__.c
@@ -0,0 +1,270 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 hathach 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.
+ */
+
+#include "shared-bindings/usb_midi/__init__.h"
+
+#include "py/gc.h"
+#include "py/obj.h"
+#include "py/mphal.h"
+#include "py/runtime.h"
+#include "py/objtuple.h"
+#include "shared-bindings/usb_midi/PortIn.h"
+#include "shared-bindings/usb_midi/PortOut.h"
+#include "supervisor/memory.h"
+#include "supervisor/usb.h"
+#include "tusb.h"
+
+static const uint8_t usb_midi_descriptor_template[] = {
+ // Audio Interface Descriptor
+ 0x09, // 0 bLength
+ 0x04, // 1 bDescriptorType (Interface)
+ 0xFF, // 2 bInterfaceNumber [SET AT RUNTIME]
+#define MIDI_AUDIO_CONTROL_INTERFACE_NUMBER_INDEX (2)
+ 0x00, // 3 bAlternateSetting
+ 0x00, // 4 bNumEndpoints 0
+ 0x01, // 5 bInterfaceClass (Audio)
+ 0x01, // 6 bInterfaceSubClass (Audio Control)
+ 0x00, // 7 bInterfaceProtocol
+ 0xFF, // 8 iInterface (String Index) [SET AT RUNTIME]
+#define MIDI_AUDIO_CONTROL_INTERFACE_STRING_INDEX (8)
+
+ // Audio10 Control Interface Descriptor
+ 0x09, // 9 bLength
+ 0x24, // 10 bDescriptorType (See Next Line)
+ 0x01, // 11 bDescriptorSubtype (CS_INTERFACE -> HEADER)
+ 0x00, 0x01, // 12,13 bcdADC 1.00
+ 0x09, 0x00, // 14,15 wTotalLength 9
+ 0x01, // 16 binCollection 0x01
+ 0xFF, // 17 baInterfaceNr [SET AT RUNTIME: one-element list: same as 20]
+#define MIDI_STREAMING_INTERFACE_NUMBER_INDEX_2 (17)
+
+ // MIDI Streaming Interface Descriptor
+ 0x09, // 18 bLength
+ 0x04, // 19 bDescriptorType (Interface)
+ 0xFF, // 20 bInterfaceNumber [SET AT RUNTIME]
+#define MIDI_STREAMING_INTERFACE_NUMBER_INDEX (20)
+ 0x00, // 21 bAlternateSetting
+ 0x02, // 22 bNumEndpoints 2
+ 0x01, // 23 bInterfaceClass (Audio)
+ 0x03, // 24 bInterfaceSubClass (MIDI Streaming)
+ 0x00, // 25 bInterfaceProtocol
+ 0xFF, // 26 iInterface (String Index) [SET AT RUNTIME]
+#define MIDI_STREAMING_INTERFACE_STRING_INDEX (26)
+
+ // MIDI Header Descriptor
+ 0x07, // 27 bLength
+ 0x24, // 28 bDescriptorType: CLASS SPECIFIC INTERFACE
+ 0x01, // 29 bDescriptorSubtype: MIDI STREAMING HEADER
+ 0x00, 0x01, // 30,31 bsdMSC (MIDI STREAMING) version 1.0
+ 0x25, 0x00, // 32,33 wLength
+
+ // MIDI Embedded In Jack Descriptor
+ 0x06, // 34 bLength
+ 0x24, // 35 bDescriptorType: CLASS SPECIFIC INTERFACE
+ 0x02, // 36 bDescriptorSubtype: MIDI IN JACK
+ 0x01, // 37 bJackType: EMBEDDED
+ 0x01, // 38 id (always 1)
+ 0xFF, // 39 iJack (String Index) [SET AT RUNTIME]
+#define MIDI_IN_JACK_STRING_INDEX (39)
+
+ // MIDI External In Jack Descriptor
+ 0x06, // 40 bLength
+ 0x24, // 41 bDescriptorType: CLASS SPECIFIC INTERFACE
+ 0x02, // 42 bDescriptorSubtype: MIDI IN JACK
+ 0x02, // 43 bJackType: EXTERNAL
+ 0x02, // 44 bJackId (always 2)
+ 0x00, // 45 iJack (String Index)
+
+ // MIDI Embedded Out Jack Descriptor
+ 0x09, // 46 bLength
+ 0x24, // 47 bDescriptorType: CLASS SPECIFIC INTERFACE
+ 0x03, // 48 bDescriptorSubtype: MIDI OUT JACK
+ 0x01, // 49 bJackType: EMBEDDED
+ 0x03, // 50 bJackID (always 3)
+ 0x01, // 51 bNrInputPins (always 1)
+ 0x02, // 52 BaSourceID(1) (always 2)
+ 0x01, // 53 BaSourcePin(1) (always 1)
+ 0xFF, // 54 iJack (String Index) [SET AT RUNTIME]
+#define MIDI_OUT_JACK_STRING_INDEX (54)
+
+ // MIDI External Out Jack Descriptor
+ 0x09, // 55 bLength
+ 0x24, // 56 bDescriptorType: CLASS SPECIFIC INTERFACE
+ 0x03, // 57 bDescriptorSubtype: MIDI OUT JACK
+ 0x02, // 58 bJackType: EXTERNAL
+ 0x04, // 59 bJackID (always 4)
+ 0x01, // 60 bNrInputPins (always 1)
+ 0x01, // 61 BaSourceID(1) (always 1)
+ 0x01, // 62 BaSourcePin(1) (always 1)
+ 0x00, // 63 iJack (String Index)
+
+ // MIDI Streaming Endpoint OUT Descriptor
+ 0x07, // 64 bLength
+ 0x05, // 65 bDescriptorType (EndPoint)
+ 0xFF, // 66 bEndpointAddress (OUT/H2D) [SET AT RUNTIME]
+#define MIDI_STREAMING_OUT_ENDPOINT_INDEX (66)
+ 0x02, // 67 bmAttributes (Bulk)
+ #if USB_HIGHSPEED
+ 0x00, 0x02, // 68,69 wMaxPacketSize (512)
+ #else
+ 0x40, 0x00, // 68,69 wMaxPacketSize (64)
+ #endif
+ 0x00, // 70 bInterval 0 (unit depends on device speed)
+
+ // MIDI Data Endpoint Descriptor
+ 0x05, // 71 bLength
+ 0x25, // 72 bDescriptorType: CLASS SPECIFIC ENDPOINT
+ 0x01, // 73 bDescriptorSubtype: MIDI STREAMING 1.0
+ 0x01, // 74 bNumGrpTrmBlock (always 1)
+ 0x01, // 75 baAssoGrpTrmBlkID(1) (always 1)
+
+ // MIDI IN Data Endpoint
+ 0x07, // 76 bLength
+ 0x05, // 77 bDescriptorType: Endpoint
+ 0xFF, // 78 bEndpointAddress (IN/D2H) [SET AT RUNTIME: 0x80 | number]
+#define MIDI_STREAMING_IN_ENDPOINT_INDEX (78)
+ 0x02, // 79 bmAttributes (Bulk)
+ #if USB_HIGHSPEED
+ 0x00, 0x02, // 80, 81 wMaxPacketSize (512)
+ #else
+ 0x40, 0x00, // 80, 81 wMaxPacketSize (64)
+ #endif
+ 0x00, // 82 bInterval 0 (unit depends on device speed)
+
+ // MIDI Data Endpoint Descriptor
+ 0x05, // 83 bLength
+ 0x25, // 84 bDescriptorType: CLASS SPECIFIC ENDPOINT
+ 0x01, // 85 bDescriptorSubtype: MIDI STREAMING 1.0
+ 0x01, // 86 bNumGrpTrmBlock (always 1)
+ 0x03, // 87 baAssoGrpTrmBlkID(1) (always 3)
+};
+
+// Is the USB MIDI device enabled?
+static bool usb_midi_is_enabled;
+
+void usb_midi_set_defaults(void) {
+ usb_midi_is_enabled = CIRCUITPY_USB_MIDI_ENABLED_DEFAULT;
+}
+
+bool usb_midi_enabled(void) {
+ return usb_midi_is_enabled;
+}
+
+
+size_t usb_midi_descriptor_length(void) {
+ return sizeof(usb_midi_descriptor_template);
+}
+
+static const char midi_streaming_interface_name[] = USB_INTERFACE_NAME " MIDI";
+static const char midi_audio_control_interface_name[] = USB_INTERFACE_NAME " Audio";
+static const char midi_in_jack_name[] = USB_INTERFACE_NAME " usb_midi.ports[0]";
+static const char midi_out_jack_name[] = USB_INTERFACE_NAME " usb_midi.ports[0]";
+
+size_t usb_midi_add_descriptor(uint8_t *descriptor_buf, descriptor_counts_t *descriptor_counts, uint8_t *current_interface_string) {
+ memcpy(descriptor_buf, usb_midi_descriptor_template, sizeof(usb_midi_descriptor_template));
+
+ descriptor_buf[MIDI_AUDIO_CONTROL_INTERFACE_NUMBER_INDEX] = descriptor_counts->current_interface;
+ descriptor_counts->current_interface++;
+
+ descriptor_buf[MIDI_STREAMING_IN_ENDPOINT_INDEX] =
+ 0x80 | (USB_MIDI_EP_NUM_IN ? USB_MIDI_EP_NUM_IN : descriptor_counts->current_endpoint);
+ descriptor_counts->num_in_endpoints++;
+ descriptor_buf[MIDI_STREAMING_OUT_ENDPOINT_INDEX] =
+ USB_MIDI_EP_NUM_OUT ? USB_MIDI_EP_NUM_OUT : descriptor_counts->current_endpoint;
+ descriptor_counts->num_out_endpoints++;
+ descriptor_counts->current_endpoint++;
+
+ descriptor_buf[MIDI_STREAMING_INTERFACE_NUMBER_INDEX] = descriptor_counts->current_interface;
+ descriptor_buf[MIDI_STREAMING_INTERFACE_NUMBER_INDEX_2] = descriptor_counts->current_interface;
+ descriptor_counts->current_interface++;
+
+ usb_add_interface_string(*current_interface_string, midi_streaming_interface_name);
+ descriptor_buf[MIDI_STREAMING_INTERFACE_STRING_INDEX] = *current_interface_string;
+ (*current_interface_string)++;
+
+ usb_add_interface_string(*current_interface_string, midi_audio_control_interface_name);
+ descriptor_buf[MIDI_AUDIO_CONTROL_INTERFACE_STRING_INDEX] = *current_interface_string;
+ (*current_interface_string)++;
+
+ usb_add_interface_string(*current_interface_string, midi_in_jack_name);
+ descriptor_buf[MIDI_IN_JACK_STRING_INDEX] = *current_interface_string;
+ (*current_interface_string)++;
+
+ usb_add_interface_string(*current_interface_string, midi_out_jack_name);
+ descriptor_buf[MIDI_OUT_JACK_STRING_INDEX] = *current_interface_string;
+ (*current_interface_string)++;
+
+ return sizeof(usb_midi_descriptor_template);
+}
+
+static const usb_midi_portin_obj_t midi_portin_obj = {
+ .base = {
+ .type = &usb_midi_portin_type,
+ },
+};
+
+static const usb_midi_portout_obj_t midi_portout_obj = {
+ .base = {
+ .type = &usb_midi_portout_type,
+ }
+};
+
+static const mp_rom_obj_tuple_t midi_ports_tuple = {
+ .base = {
+ .type = &mp_type_tuple,
+ },
+ .len = 2,
+ .items = {
+ MP_ROM_PTR(&midi_portin_obj),
+ MP_ROM_PTR(&midi_portout_obj),
+ },
+};
+
+void usb_midi_setup_ports(void) {
+ // Right now midi_ports_tuple contains no heap objects, but if it does in the future,
+ // it will need to be protected against gc.
+
+ mp_obj_tuple_t *ports = usb_midi_is_enabled ? MP_OBJ_FROM_PTR(&midi_ports_tuple) : mp_const_empty_tuple;
+ mp_map_lookup(&usb_midi_module_globals.map, MP_ROM_QSTR(MP_QSTR_ports), MP_MAP_LOOKUP)->value =
+ MP_OBJ_FROM_PTR(ports);
+}
+
+static bool usb_midi_set_enabled(bool enabled) {
+ // We can't change the descriptors once we're connected.
+ if (tud_connected()) {
+ return false;
+ }
+ usb_midi_is_enabled = enabled;
+ return true;
+}
+
+bool common_hal_usb_midi_disable(void) {
+ return usb_midi_set_enabled(false);
+}
+
+bool common_hal_usb_midi_enable(void) {
+ return usb_midi_set_enabled(true);
+}
diff --git a/circuitpython/shared-module/usb_midi/__init__.h b/circuitpython/shared-module/usb_midi/__init__.h
new file mode 100644
index 0000000..8cc430e
--- /dev/null
+++ b/circuitpython/shared-module/usb_midi/__init__.h
@@ -0,0 +1,39 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Scott Shawcroft 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.
+ */
+
+#ifndef SHARED_MODULE_USB_MIDI___INIT___H
+#define SHARED_MODULE_USB_MIDI___INIT___H
+
+#include "supervisor/usb.h"
+
+bool usb_midi_enabled(void);
+void usb_midi_set_defaults(void);
+void usb_midi_setup_ports(void);
+
+size_t usb_midi_descriptor_length(void);
+size_t usb_midi_add_descriptor(uint8_t *descriptor_buf, descriptor_counts_t *descriptor_counts, uint8_t *current_interface_string);
+
+#endif /* SHARED_MODULE_USB_MIDI___INIT___H */
diff --git a/circuitpython/shared-module/ustack/__init__.c b/circuitpython/shared-module/ustack/__init__.c
new file mode 100644
index 0000000..55e5fa2
--- /dev/null
+++ b/circuitpython/shared-module/ustack/__init__.c
@@ -0,0 +1,52 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2017 Dan Halbert
+ *
+ * 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.
+ */
+
+#include <stdint.h>
+
+#include "py/mpstate.h"
+#include "py/stackctrl.h"
+
+#include "shared-bindings/ustack/__init__.h"
+
+#if MICROPY_MAX_STACK_USAGE
+uint32_t shared_module_ustack_max_stack_usage(void) {
+ // Start at stack limit and move up.
+ // Untouched stack was filled with a sentinel value.
+ // Stop at first non-sentinel byte.
+ char *p = MP_STATE_THREAD(stack_bottom);
+ while (*p++ == MP_MAX_STACK_USAGE_SENTINEL_BYTE) {
+ }
+ return MP_STATE_THREAD(stack_top) - p;
+}
+#endif
+
+uint32_t shared_module_ustack_stack_size() {
+ return MP_STATE_THREAD(stack_limit);
+}
+
+uint32_t shared_module_ustack_stack_usage() {
+ return mp_stack_usage();
+}
diff --git a/circuitpython/shared-module/vectorio/Circle.c b/circuitpython/shared-module/vectorio/Circle.c
new file mode 100644
index 0000000..2ec11fe
--- /dev/null
+++ b/circuitpython/shared-module/vectorio/Circle.c
@@ -0,0 +1,80 @@
+
+#include "shared-bindings/vectorio/Circle.h"
+#include "shared-module/vectorio/__init__.h"
+#include "shared-module/displayio/area.h"
+
+#include "py/runtime.h"
+#include "stdlib.h"
+
+
+void common_hal_vectorio_circle_construct(vectorio_circle_t *self, uint16_t radius, uint16_t color_index) {
+ self->radius = radius;
+ self->on_dirty.obj = NULL;
+ self->color_index = color_index + 1;
+}
+
+void common_hal_vectorio_circle_set_on_dirty(vectorio_circle_t *self, vectorio_event_t on_dirty) {
+ if (self->on_dirty.obj != NULL) {
+ mp_raise_TypeError(translate("circle can only be registered in one parent"));
+ }
+ self->on_dirty = on_dirty;
+}
+
+
+uint32_t common_hal_vectorio_circle_get_pixel(void *obj, int16_t x, int16_t y) {
+ vectorio_circle_t *self = obj;
+ int16_t radius = abs(self->radius);
+ x = abs(x);
+ y = abs(y);
+ if (x + y <= radius) {
+ return self->color_index;
+ }
+ if (x > radius) {
+ return 0;
+ }
+ if (y > radius) {
+ return 0;
+ }
+ const bool pythagorasSmallerThanRadius = (int32_t)x * x + (int32_t)y * y <= (int32_t)radius * radius;
+ return pythagorasSmallerThanRadius ? self->color_index : 0;
+}
+
+
+void common_hal_vectorio_circle_get_area(void *circle, displayio_area_t *out_area) {
+ vectorio_circle_t *self = circle;
+ out_area->x1 = -1 * self->radius - 1;
+ out_area->y1 = -1 * self->radius - 1;
+ out_area->x2 = self->radius + 1;
+ out_area->y2 = self->radius + 1;
+}
+
+int16_t common_hal_vectorio_circle_get_radius(void *obj) {
+ vectorio_circle_t *self = obj;
+ return self->radius;
+}
+
+void common_hal_vectorio_circle_set_radius(void *obj, int16_t radius) {
+ vectorio_circle_t *self = obj;
+ self->radius = abs(radius);
+ if (self->on_dirty.obj != NULL) {
+ self->on_dirty.event(self->on_dirty.obj);
+ }
+}
+
+uint16_t common_hal_vectorio_circle_get_color_index(void *obj) {
+ vectorio_circle_t *self = obj;
+ return self->color_index - 1;
+}
+
+void common_hal_vectorio_circle_set_color_index(void *obj, uint16_t color_index) {
+ vectorio_circle_t *self = obj;
+ self->color_index = abs(color_index + 1);
+ if (self->on_dirty.obj != NULL) {
+ self->on_dirty.event(self->on_dirty.obj);
+ }
+}
+
+mp_obj_t common_hal_vectorio_circle_get_draw_protocol(void *circle) {
+ vectorio_circle_t *self = circle;
+ return self->draw_protocol_instance;
+}
diff --git a/circuitpython/shared-module/vectorio/Circle.h b/circuitpython/shared-module/vectorio/Circle.h
new file mode 100644
index 0000000..6ebd9af
--- /dev/null
+++ b/circuitpython/shared-module/vectorio/Circle.h
@@ -0,0 +1,18 @@
+#ifndef MICROPY_INCLUDED_SHARED_MODULE_VECTORIO_CIRCLE_H
+#define MICROPY_INCLUDED_SHARED_MODULE_VECTORIO_CIRCLE_H
+
+#include <stdint.h>
+
+#include "py/obj.h"
+
+#include "shared-module/vectorio/__init__.h"
+
+typedef struct {
+ mp_obj_base_t base;
+ uint16_t radius;
+ uint16_t color_index;
+ vectorio_event_t on_dirty;
+ mp_obj_t draw_protocol_instance;
+} vectorio_circle_t;
+
+#endif // MICROPY_INCLUDED_SHARED_MODULE_VECTORIO_CIRCLE_H
diff --git a/circuitpython/shared-module/vectorio/Polygon.c b/circuitpython/shared-module/vectorio/Polygon.c
new file mode 100644
index 0000000..10ebdf1
--- /dev/null
+++ b/circuitpython/shared-module/vectorio/Polygon.c
@@ -0,0 +1,204 @@
+#include "shared-module/vectorio/__init__.h"
+#include "shared-bindings/vectorio/Polygon.h"
+#include "shared-module/displayio/area.h"
+
+#include "py/runtime.h"
+#include "py/gc.h"
+
+#include "stdlib.h"
+#include <stdio.h>
+
+
+#define VECTORIO_POLYGON_DEBUG(...) (void)0
+// #define VECTORIO_POLYGON_DEBUG(...) mp_printf(&mp_plat_print, __VA_ARGS__)
+
+
+// Converts a list of points tuples to a flat list of ints for speedier internal use.
+// Also validates the points.
+static void _clobber_points_list(vectorio_polygon_t *self, mp_obj_t points_tuple_list) {
+ size_t len = 0;
+ mp_obj_t *items;
+ mp_obj_list_get(points_tuple_list, &len, &items);
+ VECTORIO_POLYGON_DEBUG(" self.len: %d, len: %d, ", self->len, len);
+
+ if (len < 3) {
+ mp_raise_TypeError(translate("Polygon needs at least 3 points"));
+ }
+
+ if (self->len < 2 * len) {
+ if (self->points_list != NULL) {
+ VECTORIO_POLYGON_DEBUG("free(%d), ", sizeof(self->points_list));
+ gc_free(self->points_list);
+ }
+ self->points_list = gc_alloc(2 * len * sizeof(uint16_t), false, false);
+ VECTORIO_POLYGON_DEBUG("alloc(%p, %d)", self->points_list, 2 * len * sizeof(uint16_t));
+ }
+ self->len = 2 * len;
+
+ for (uint16_t i = 0; i < len; ++i) {
+ size_t tuple_len = 0;
+ mp_obj_t *tuple_items;
+ mp_obj_tuple_get(items[i], &tuple_len, &tuple_items);
+
+ if (tuple_len != 2) {
+ mp_raise_ValueError_varg(translate("%q must be a tuple of length 2"), MP_QSTR_point);
+ }
+ mp_int_t x;
+ mp_int_t y;
+ if (!mp_obj_get_int_maybe(tuple_items[ 0 ], &x)
+ || !mp_obj_get_int_maybe(tuple_items[ 1 ], &y)
+ || x < SHRT_MIN || x > SHRT_MAX || y < SHRT_MIN || y > SHRT_MAX
+ ) {
+ gc_free(self->points_list);
+ self->points_list = NULL;
+ mp_raise_ValueError_varg(translate("unsupported %q type"), MP_QSTR_point);
+ self->len = 0;
+ }
+ self->points_list[2 * i ] = (int16_t)x;
+ self->points_list[2 * i + 1] = (int16_t)y;
+ }
+}
+
+
+
+void common_hal_vectorio_polygon_construct(vectorio_polygon_t *self, mp_obj_t points_list, uint16_t color_index) {
+ VECTORIO_POLYGON_DEBUG("%p polygon_construct: ", self);
+ self->points_list = NULL;
+ self->len = 0;
+ self->on_dirty.obj = NULL;
+ self->color_index = color_index + 1;
+ _clobber_points_list(self, points_list);
+ VECTORIO_POLYGON_DEBUG("\n");
+}
+
+
+mp_obj_t common_hal_vectorio_polygon_get_points(vectorio_polygon_t *self) {
+ VECTORIO_POLYGON_DEBUG("%p common_hal_vectorio_polygon_get_points {len: %d, points_list: %p}\n", self, self->len, self->points_list);
+ mp_obj_list_t *list = MP_OBJ_TO_PTR(mp_obj_new_list(0, NULL));
+
+ VECTORIO_POLYGON_DEBUG(" >points\n");
+ for (uint16_t i = 0; i < self->len; i += 2) {
+ VECTORIO_POLYGON_DEBUG(" (%4d, %4d)\n", self->points_list[i], self->points_list[i + 1]);
+
+ mp_obj_tuple_t *pair = MP_OBJ_TO_PTR(mp_obj_new_tuple(2, NULL));
+ pair->items[0] = mp_obj_new_int((mp_int_t)self->points_list[i ]);
+ pair->items[1] = mp_obj_new_int((mp_int_t)self->points_list[i + 1]);
+
+ mp_obj_list_append(
+ list,
+ pair
+ );
+ }
+ VECTORIO_POLYGON_DEBUG(" <points\n");
+ return MP_OBJ_FROM_PTR(list);
+}
+void common_hal_vectorio_polygon_set_points(vectorio_polygon_t *self, mp_obj_t points_list) {
+ VECTORIO_POLYGON_DEBUG("%p common_hal_vectorio_polygon_set_points: ", self);
+ _clobber_points_list(self, points_list);
+ if (self->on_dirty.obj != NULL) {
+ self->on_dirty.event(self->on_dirty.obj);
+ }
+ VECTORIO_POLYGON_DEBUG("\n");
+}
+
+void common_hal_vectorio_polygon_set_on_dirty(vectorio_polygon_t *self, vectorio_event_t notification) {
+ if (self->on_dirty.obj != NULL) {
+ mp_raise_TypeError(translate("polygon can only be registered in one parent"));
+ }
+ self->on_dirty = notification;
+}
+
+
+void common_hal_vectorio_polygon_get_area(void *polygon, displayio_area_t *area) {
+ vectorio_polygon_t *self = polygon;
+ VECTORIO_POLYGON_DEBUG("%p common_hal_vectorio_polygon_get_area\n");
+
+ area->x1 = SHRT_MAX;
+ area->y1 = SHRT_MAX;
+ area->x2 = SHRT_MIN;
+ area->y2 = SHRT_MIN;
+ for (uint16_t i = 0; i < self->len; ++i) {
+ int16_t x = self->points_list[i];
+ ++i;
+ int16_t y = self->points_list[i];
+ if (x < area->x1) {
+ VECTORIO_POLYGON_DEBUG(" x1: %d\n", x);
+ area->x1 = x;
+ }
+ if (y < area->y1) {
+ VECTORIO_POLYGON_DEBUG(" y1: %d\n", y);
+ area->y1 = y;
+ }
+ if (x > area->x2) {
+ VECTORIO_POLYGON_DEBUG(" x2: %d\n", x);
+ area->x2 = x;
+ }
+ if (y > area->y2) {
+ VECTORIO_POLYGON_DEBUG(" y2: %d\n", y);
+ area->y2 = y;
+ }
+ }
+}
+
+
+// <0 if the point is to the left of the line vector
+// 0 if the point is on the line
+// >0 if the point is to the right of the line vector
+__attribute__((always_inline)) static inline int line_side(int16_t x1, int16_t y1, int16_t x2, int16_t y2, int16_t px, int16_t py) {
+ return (px - x1) * (y2 - y1)
+ - (py - y1) * (x2 - x1);
+}
+
+
+uint32_t common_hal_vectorio_polygon_get_pixel(void *obj, int16_t x, int16_t y) {
+ VECTORIO_POLYGON_DEBUG("%p polygon get_pixel %d, %d\n", obj, x, y);
+ vectorio_polygon_t *self = obj;
+
+ if (self->len == 0) {
+ return 0;
+ }
+
+ int16_t winding_number = 0;
+ int16_t x1 = self->points_list[0];
+ int16_t y1 = self->points_list[1];
+ for (uint16_t i = 2; i <= self->len + 1; ++i) {
+ VECTORIO_POLYGON_DEBUG(" {(%3d, %3d),", x1, y1);
+ int16_t x2 = self->points_list[i % self->len];
+ ++i;
+ int16_t y2 = self->points_list[i % self->len];
+ VECTORIO_POLYGON_DEBUG(" (%3d, %3d)}\n", x2, y2);
+ if (y1 <= y) {
+ if (y2 > y && line_side(x1, y1, x2, y2, x, y) < 0) {
+ // Wind up, point is to the left of the edge vector
+ ++winding_number;
+ VECTORIO_POLYGON_DEBUG(" wind:%2d winding_number:%2d\n", 1, winding_number);
+ }
+ } else if (y2 <= y && line_side(x1, y1, x2, y2, x, y) > 0) {
+ // Wind down, point is to the right of the edge vector
+ --winding_number;
+ VECTORIO_POLYGON_DEBUG(" wind:%2d winding_number:%2d\n", -1, winding_number);
+ }
+
+ x1 = x2;
+ y1 = y2;
+ }
+ return winding_number == 0 ? 0 : self->color_index;
+}
+
+mp_obj_t common_hal_vectorio_polygon_get_draw_protocol(void *polygon) {
+ vectorio_polygon_t *self = polygon;
+ return self->draw_protocol_instance;
+}
+
+uint16_t common_hal_vectorio_polygon_get_color_index(void *obj) {
+ vectorio_polygon_t *self = obj;
+ return self->color_index - 1;
+}
+
+void common_hal_vectorio_polygon_set_color_index(void *obj, uint16_t color_index) {
+ vectorio_polygon_t *self = obj;
+ self->color_index = abs(color_index + 1);
+ if (self->on_dirty.obj != NULL) {
+ self->on_dirty.event(self->on_dirty.obj);
+ }
+}
diff --git a/circuitpython/shared-module/vectorio/Polygon.h b/circuitpython/shared-module/vectorio/Polygon.h
new file mode 100644
index 0000000..795e335
--- /dev/null
+++ b/circuitpython/shared-module/vectorio/Polygon.h
@@ -0,0 +1,19 @@
+#ifndef MICROPY_INCLUDED_SHARED_MODULE_VECTORIO_POLYGON_H
+#define MICROPY_INCLUDED_SHARED_MODULE_VECTORIO_POLYGON_H
+
+#include <stdint.h>
+
+#include "py/obj.h"
+#include "shared-module/vectorio/__init__.h"
+
+typedef struct {
+ mp_obj_base_t base;
+ // An int array[ x, y, ... ]
+ int16_t *points_list;
+ uint16_t len;
+ uint16_t color_index;
+ vectorio_event_t on_dirty;
+ mp_obj_t draw_protocol_instance;
+} vectorio_polygon_t;
+
+#endif // MICROPY_INCLUDED_SHARED_MODULE_VECTORIO_POLYGON_H
diff --git a/circuitpython/shared-module/vectorio/Rectangle.c b/circuitpython/shared-module/vectorio/Rectangle.c
new file mode 100644
index 0000000..fbd3d6b
--- /dev/null
+++ b/circuitpython/shared-module/vectorio/Rectangle.c
@@ -0,0 +1,82 @@
+#include "shared-module/vectorio/__init__.h"
+#include "shared-bindings/vectorio/Rectangle.h"
+#include "shared-module/displayio/area.h"
+
+#include "py/runtime.h"
+#include "stdlib.h"
+
+
+void common_hal_vectorio_rectangle_construct(vectorio_rectangle_t *self, uint32_t width, uint32_t height, uint16_t color_index) {
+ self->width = width;
+ self->height = height;
+ self->color_index = color_index + 1;
+}
+
+void common_hal_vectorio_rectangle_set_on_dirty(vectorio_rectangle_t *self, vectorio_event_t on_dirty) {
+ if (self->on_dirty.obj != NULL) {
+ mp_raise_TypeError(translate("can only be registered in one parent"));
+ }
+ self->on_dirty = on_dirty;
+}
+
+uint32_t common_hal_vectorio_rectangle_get_pixel(void *obj, int16_t x, int16_t y) {
+ vectorio_rectangle_t *self = obj;
+ if (x >= 0 && y >= 0 && x < self->width && y < self->height) {
+ return self->color_index;
+ }
+ return 0;
+}
+
+
+void common_hal_vectorio_rectangle_get_area(void *rectangle, displayio_area_t *out_area) {
+ vectorio_rectangle_t *self = rectangle;
+ out_area->x1 = 0;
+ out_area->y1 = 0;
+ out_area->x2 = self->width;
+ out_area->y2 = self->height;
+}
+
+
+mp_obj_t common_hal_vectorio_rectangle_get_draw_protocol(void *rectangle) {
+ vectorio_rectangle_t *self = rectangle;
+ return self->draw_protocol_instance;
+}
+
+int16_t common_hal_vectorio_rectangle_get_width(void *obj) {
+ vectorio_rectangle_t *self = obj;
+ return self->width;
+}
+
+void common_hal_vectorio_rectangle_set_width(void *obj, int16_t width) {
+ vectorio_rectangle_t *self = obj;
+ self->width = abs(width);
+ if (self->on_dirty.obj != NULL) {
+ self->on_dirty.event(self->on_dirty.obj);
+ }
+}
+
+int16_t common_hal_vectorio_rectangle_get_height(void *obj) {
+ vectorio_rectangle_t *self = obj;
+ return self->height;
+}
+
+void common_hal_vectorio_rectangle_set_height(void *obj, int16_t height) {
+ vectorio_rectangle_t *self = obj;
+ self->height = abs(height);
+ if (self->on_dirty.obj != NULL) {
+ self->on_dirty.event(self->on_dirty.obj);
+ }
+}
+
+uint16_t common_hal_vectorio_rectangle_get_color_index(void *obj) {
+ vectorio_rectangle_t *self = obj;
+ return self->color_index - 1;
+}
+
+void common_hal_vectorio_rectangle_set_color_index(void *obj, uint16_t color_index) {
+ vectorio_rectangle_t *self = obj;
+ self->color_index = abs(color_index + 1);
+ if (self->on_dirty.obj != NULL) {
+ self->on_dirty.event(self->on_dirty.obj);
+ }
+}
diff --git a/circuitpython/shared-module/vectorio/Rectangle.h b/circuitpython/shared-module/vectorio/Rectangle.h
new file mode 100644
index 0000000..2b1decc
--- /dev/null
+++ b/circuitpython/shared-module/vectorio/Rectangle.h
@@ -0,0 +1,18 @@
+#ifndef MICROPY_INCLUDED_SHARED_MODULE_VECTORIO_RECTANGLE_H
+#define MICROPY_INCLUDED_SHARED_MODULE_VECTORIO_RECTANGLE_H
+
+#include <stdint.h>
+
+#include "py/obj.h"
+#include "shared-module/vectorio/__init__.h"
+
+typedef struct {
+ mp_obj_base_t base;
+ uint16_t width;
+ uint16_t height;
+ uint16_t color_index;
+ vectorio_event_t on_dirty;
+ mp_obj_t draw_protocol_instance;
+} vectorio_rectangle_t;
+
+#endif // MICROPY_INCLUDED_SHARED_MODULE_VECTORIO_RECTANGLE_H
diff --git a/circuitpython/shared-module/vectorio/VectorShape.c b/circuitpython/shared-module/vectorio/VectorShape.c
new file mode 100644
index 0000000..04213d3
--- /dev/null
+++ b/circuitpython/shared-module/vectorio/VectorShape.c
@@ -0,0 +1,539 @@
+
+#include "stdlib.h"
+
+#include "shared-module/vectorio/__init__.h"
+#include "shared-bindings/vectorio/VectorShape.h"
+
+#include "py/misc.h"
+#include "py/runtime.h"
+#include "shared-bindings/time/__init__.h"
+#include "shared-bindings/displayio/ColorConverter.h"
+#include "shared-bindings/displayio/Palette.h"
+
+#include "shared-bindings/vectorio/Circle.h"
+#include "shared-bindings/vectorio/Polygon.h"
+#include "shared-bindings/vectorio/Rectangle.h"
+
+// Lifecycle actions.
+#define VECTORIO_SHAPE_DEBUG(...) (void)0
+// #define VECTORIO_SHAPE_DEBUG(...) mp_printf(&mp_plat_print, __VA_ARGS__)
+
+
+// Used in both logging and ifdefs, for extra variables
+// #define VECTORIO_PERF(...) mp_printf(&mp_plat_print, __VA_ARGS__)
+
+
+// Really verbose.
+#define VECTORIO_SHAPE_PIXEL_DEBUG(...) (void)0
+// #define VECTORIO_SHAPE_PIXEL_DEBUG(...) mp_printf(&mp_plat_print, __VA_ARGS__)
+
+#define U32_TO_BINARY_FMT "%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c"
+#define U32_TO_BINARY(u32) \
+ (u32 & 0x80000000 ? '1' : '0'), \
+ (u32 & 0x40000000 ? '1' : '0'), \
+ (u32 & 0x20000000 ? '1' : '0'), \
+ (u32 & 0x10000000 ? '1' : '0'), \
+ (u32 & 0x8000000 ? '1' : '0'), \
+ (u32 & 0x4000000 ? '1' : '0'), \
+ (u32 & 0x2000000 ? '1' : '0'), \
+ (u32 & 0x1000000 ? '1' : '0'), \
+ (u32 & 0x800000 ? '1' : '0'), \
+ (u32 & 0x400000 ? '1' : '0'), \
+ (u32 & 0x200000 ? '1' : '0'), \
+ (u32 & 0x100000 ? '1' : '0'), \
+ (u32 & 0x80000 ? '1' : '0'), \
+ (u32 & 0x40000 ? '1' : '0'), \
+ (u32 & 0x20000 ? '1' : '0'), \
+ (u32 & 0x10000 ? '1' : '0'), \
+ (u32 & 0x8000 ? '1' : '0'), \
+ (u32 & 0x4000 ? '1' : '0'), \
+ (u32 & 0x2000 ? '1' : '0'), \
+ (u32 & 0x1000 ? '1' : '0'), \
+ (u32 & 0x800 ? '1' : '0'), \
+ (u32 & 0x400 ? '1' : '0'), \
+ (u32 & 0x200 ? '1' : '0'), \
+ (u32 & 0x100 ? '1' : '0'), \
+ (u32 & 0x80 ? '1' : '0'), \
+ (u32 & 0x40 ? '1' : '0'), \
+ (u32 & 0x20 ? '1' : '0'), \
+ (u32 & 0x10 ? '1' : '0'), \
+ (u32 & 0x8 ? '1' : '0'), \
+ (u32 & 0x4 ? '1' : '0'), \
+ (u32 & 0x2 ? '1' : '0'), \
+ (u32 & 0x1 ? '1' : '0')
+
+static void short_bound_check(mp_int_t i, qstr name) {
+ if (i < SHRT_MIN || i > SHRT_MAX) {
+ mp_raise_ValueError_varg(translate("%q must be between %d and %d"), name, SHRT_MIN, SHRT_MAX);
+ }
+}
+
+inline __attribute__((always_inline))
+static void area_transpose(displayio_area_t *to_transpose) {
+ int16_t swap = to_transpose->y1;
+ to_transpose->y1 = to_transpose->x1;
+ to_transpose->x1 = swap;
+ swap = to_transpose->y2;
+ to_transpose->y2 = to_transpose->x2;
+ to_transpose->x2 = swap;
+}
+
+inline __attribute__((always_inline))
+static void _get_screen_area(vectorio_vector_shape_t *self, displayio_area_t *out_area) {
+ VECTORIO_SHAPE_DEBUG("%p get_screen_area (%3d,%3d) tform:{x:%d y:%d dx:%d dy:%d scl:%d w:%d h:%d mx:%d my:%d tr:%d}", self, self->x, self->y,
+ self->absolute_transform->x, self->absolute_transform->y, self->absolute_transform->dx, self->absolute_transform->dy, self->absolute_transform->scale,
+ self->absolute_transform->width, self->absolute_transform->height, self->absolute_transform->mirror_x, self->absolute_transform->mirror_y, self->absolute_transform->transpose_xy
+ );
+ self->ishape.get_area(self->ishape.shape, out_area);
+ VECTORIO_SHAPE_DEBUG(" in:{(%5d,%5d), (%5d,%5d)}", out_area->x1, out_area->y1, out_area->x2, out_area->y2);
+
+ int16_t x;
+ int16_t y;
+ if (self->absolute_transform->transpose_xy) {
+ x = self->absolute_transform->x + self->absolute_transform->dx * self->y;
+ y = self->absolute_transform->y + self->absolute_transform->dy * self->x;
+ if (self->absolute_transform->dx < 1) {
+ out_area->y1 = out_area->y1 * -1 + 1;
+ out_area->y2 = out_area->y2 * -1 + 1;
+ }
+ if (self->absolute_transform->dy < 1) {
+ out_area->x1 = out_area->x1 * -1 + 1;
+ out_area->x2 = out_area->x2 * -1 + 1;
+ }
+ area_transpose(out_area);
+ } else {
+ x = self->absolute_transform->x + self->absolute_transform->dx * self->x;
+ y = self->absolute_transform->y + self->absolute_transform->dy * self->y;
+
+ if (self->absolute_transform->dx < 1) {
+ out_area->x1 = out_area->x1 * -1 + 1;
+ out_area->x2 = out_area->x2 * -1 + 1;
+ }
+ if (self->absolute_transform->dy < 1) {
+ out_area->y1 = out_area->y1 * -1 + 1;
+ out_area->y2 = out_area->y2 * -1 + 1;
+ }
+ }
+ displayio_area_canon(out_area);
+ displayio_area_shift(out_area, x, y);
+
+ VECTORIO_SHAPE_DEBUG(" out:{(%5d,%5d), (%5d,%5d)}\n", out_area->x1, out_area->y1, out_area->x2, out_area->y2);
+}
+
+// Get the target pixel based on the shape's coordinate space
+static void screen_to_shape_coordinates(vectorio_vector_shape_t *self, uint16_t x, uint16_t y, int16_t *out_shape_x, int16_t *out_shape_y) {
+ if (self->absolute_transform->transpose_xy) {
+ *out_shape_x = y - self->absolute_transform->y - self->absolute_transform->dy * self->x;
+ *out_shape_y = x - self->absolute_transform->x - self->absolute_transform->dx * self->y;
+
+ VECTORIO_SHAPE_PIXEL_DEBUG(" a(%3d, %3d)", *out_shape_x, *out_shape_y);
+ if (self->absolute_transform->dx < 1) {
+ *out_shape_y *= -1;
+ }
+ if (self->absolute_transform->dy < 1) {
+ *out_shape_x *= -1;
+ }
+ VECTORIO_SHAPE_PIXEL_DEBUG(" b(%3d, %3d)", *out_shape_x, *out_shape_y);
+ } else {
+ *out_shape_x = x - self->absolute_transform->x - self->absolute_transform->dx * self->x;
+ *out_shape_y = y - self->absolute_transform->y - self->absolute_transform->dy * self->y;
+
+ VECTORIO_SHAPE_PIXEL_DEBUG(" a(%3d, %3d)", *out_shape_x, *out_shape_y);
+ if (self->absolute_transform->dx < 1) {
+ *out_shape_x *= -1;
+ }
+ if (self->absolute_transform->dy < 1) {
+ *out_shape_y *= -1;
+ }
+ VECTORIO_SHAPE_PIXEL_DEBUG(" b(%3d, %3d)", *out_shape_x, *out_shape_y);
+
+ // It's mirrored via dx. Maybe we need to add support for also separately mirroring?
+ // if (self->absolute_transform->mirror_x) {
+ // pixel_to_get_x = (shape_area.x2 - shape_area.x1) - (pixel_to_get_x - shape_area.x1) + shape_area.x1 - 1;
+ // }
+ // if (self->absolute_transform->mirror_y) {
+ // pixel_to_get_y = (shape_area.y2 - shape_area.y1) - (pixel_to_get_y - shape_area.y1) + +shape_area.y1 - 1;
+ // }
+ }
+}
+
+static void check_bounds_and_set_x(vectorio_vector_shape_t *self, mp_int_t x) {
+ short_bound_check(x, MP_QSTR_x);
+ self->x = x;
+}
+
+static void check_bounds_and_set_y(vectorio_vector_shape_t *self, mp_int_t y) {
+ short_bound_check(y, MP_QSTR_y);
+ self->y = y;
+}
+
+
+// For use by Group to know where it needs to redraw on layer removal.
+bool vectorio_vector_shape_get_dirty_area(vectorio_vector_shape_t *self, displayio_area_t *out_area) {
+ out_area->x1 = out_area->x2;
+ displayio_area_union(
+ &self->ephemeral_dirty_area,
+ &self->current_area,
+ out_area
+ );
+ return true; // For now just always redraw.
+}
+
+
+// This must be invoked after each time a shape changes its position, shape or appearance in any way.
+void common_hal_vectorio_vector_shape_set_dirty(void *vector_shape) {
+ vectorio_vector_shape_t *self = vector_shape;
+ // In screen space. Need to offset the shape space.
+ displayio_area_t current_area;
+ _get_screen_area(self, &current_area);
+ VECTORIO_SHAPE_DEBUG("%p shape_dirty new:{(%3d,%3d), (%3d,%3d)} dirty:{(%3d,%3d), (%3d,%3d)}",
+ self,
+ current_area.x1, current_area.y1, current_area.x2, current_area.y2,
+ self->ephemeral_dirty_area.x1, self->ephemeral_dirty_area.y1, self->ephemeral_dirty_area.x2, self->ephemeral_dirty_area.y2);
+
+ bool moved = !displayio_area_equal(&current_area, &self->current_area);
+ if (moved) {
+ displayio_area_union(&self->current_area, &self->ephemeral_dirty_area, &self->ephemeral_dirty_area);
+ VECTORIO_SHAPE_DEBUG(" stale:{(%3d,%3d), (%3d,%3d)} -> expanded:{(%3d,%3d), (%3d,%3d)}\n",
+ self->current_area.x1, self->current_area.y1, self->current_area.x2, self->current_area.y2,
+ self->ephemeral_dirty_area.x1, self->ephemeral_dirty_area.y1, self->ephemeral_dirty_area.x2, self->ephemeral_dirty_area.y2);
+
+ // Dirty area tracks the shape's footprint between draws. It's reset on refresh finish.
+ displayio_area_copy(&current_area, &self->current_area);
+ }
+ self->current_area_dirty = true;
+}
+
+
+void common_hal_vectorio_vector_shape_construct(vectorio_vector_shape_t *self,
+ vectorio_ishape_t ishape,
+ mp_obj_t pixel_shader, int32_t x, int32_t y) {
+ VECTORIO_SHAPE_DEBUG("%p vector_shape_construct x:%3d, y:%3d\n", self, x, y);
+ check_bounds_and_set_x(self, x);
+ check_bounds_and_set_y(self, y);
+ self->pixel_shader = pixel_shader;
+ self->ishape = ishape;
+ self->absolute_transform = &null_transform; // Critical to have a valid transform before getting screen area.
+ self->ephemeral_dirty_area.x1 = self->ephemeral_dirty_area.x2; // Cheat to set area to 0
+ self->ephemeral_dirty_area.next = NULL;
+ self->current_area_dirty = true;
+ _get_screen_area(self, &self->current_area);
+}
+
+bool common_hal_vectorio_vector_shape_contains(vectorio_vector_shape_t *self, mp_int_t x, mp_int_t y) {
+ VECTORIO_SHAPE_DEBUG("%p contains(%d, %d)", self);
+ short_bound_check(x, MP_QSTR_x);
+ short_bound_check(y, MP_QSTR_y);
+ int16_t shape_x;
+ int16_t shape_y;
+ screen_to_shape_coordinates(self, x, y, &shape_x, &shape_y);
+ bool shape_contains_coordinates = 0 != self->ishape.get_pixel(self->ishape.shape, shape_x, shape_y);
+ return shape_contains_coordinates;
+}
+
+
+mp_int_t common_hal_vectorio_vector_shape_get_x(vectorio_vector_shape_t *self) {
+ VECTORIO_SHAPE_DEBUG("%p get_x\n", self);
+ return self->x;
+}
+
+
+void common_hal_vectorio_vector_shape_set_x(vectorio_vector_shape_t *self, mp_int_t x) {
+ VECTORIO_SHAPE_DEBUG("%p set_x %d\n", self, x);
+ if (self->x == x) {
+ return;
+ }
+ check_bounds_and_set_x(self, x);
+ common_hal_vectorio_vector_shape_set_dirty(self);
+}
+
+
+mp_int_t common_hal_vectorio_vector_shape_get_y(vectorio_vector_shape_t *self) {
+ VECTORIO_SHAPE_DEBUG("%p get_y\n", self);
+ return self->y;
+}
+
+
+void common_hal_vectorio_vector_shape_set_y(vectorio_vector_shape_t *self, mp_int_t y) {
+ VECTORIO_SHAPE_DEBUG("%p set_y %d\n", self, y);
+ if (self->y == y) {
+ return;
+ }
+ check_bounds_and_set_y(self, y);
+ common_hal_vectorio_vector_shape_set_dirty(self);
+}
+
+mp_obj_tuple_t *common_hal_vectorio_vector_shape_get_location(vectorio_vector_shape_t *self) {
+ VECTORIO_SHAPE_DEBUG("%p get_location\n", self);
+ mp_obj_tuple_t *pair = MP_OBJ_TO_PTR(mp_obj_new_tuple(2, NULL));
+ pair->items[0] = mp_obj_new_int((mp_int_t)self->x);
+ pair->items[1] = mp_obj_new_int((mp_int_t)self->y);
+ return pair;
+}
+
+
+void common_hal_vectorio_vector_shape_set_location(vectorio_vector_shape_t *self, mp_obj_t xy) {
+ VECTORIO_SHAPE_DEBUG("%p set_location\n", self);
+ size_t tuple_len = 0;
+ mp_obj_t *tuple_items;
+ mp_obj_tuple_get(xy, &tuple_len, &tuple_items);
+ if (tuple_len != 2) {
+ mp_raise_TypeError(translate("(x,y) integers required"));
+ }
+
+ mp_int_t x;
+ mp_int_t y;
+ if (!mp_obj_get_int_maybe(tuple_items[ 0 ], &x)
+ || !mp_obj_get_int_maybe(tuple_items[ 1 ], &y)) {
+ mp_raise_ValueError_varg(translate("unsupported %q type"), MP_QSTR_point);
+ }
+ bool dirty = false;
+ if (self->x != x) {
+ check_bounds_and_set_x(self, x);
+ dirty = true;
+ }
+ if (self->y != y) {
+ check_bounds_and_set_y(self, y);
+ dirty = true;
+ }
+ if (dirty) {
+ common_hal_vectorio_vector_shape_set_dirty(self);
+ }
+}
+
+
+mp_obj_t common_hal_vectorio_vector_shape_get_pixel_shader(vectorio_vector_shape_t *self) {
+ VECTORIO_SHAPE_DEBUG("%p get_pixel_shader\n", self);
+ return self->pixel_shader;
+}
+
+void common_hal_vectorio_vector_shape_set_pixel_shader(vectorio_vector_shape_t *self, mp_obj_t pixel_shader) {
+ VECTORIO_SHAPE_DEBUG("%p set_pixel_shader\n", self);
+ self->pixel_shader = pixel_shader;
+ common_hal_vectorio_vector_shape_set_dirty(self);
+}
+
+bool vectorio_vector_shape_fill_area(vectorio_vector_shape_t *self, const _displayio_colorspace_t *colorspace, const displayio_area_t *area, uint32_t *mask, uint32_t *buffer) {
+ // Shape areas are relative to 0,0. This will allow rotation about a known axis.
+ // The consequence is that the area reported by the shape itself is _relative_ to 0,0.
+ // To make it relative to the VectorShape position, we must shift it.
+ // Pixels are drawn on the screen_area (shifted) coordinate space, while pixels are _determined_ from
+ // the shape_area (unshifted) space.
+ #ifdef VECTORIO_PERF
+ uint64_t start = common_hal_time_monotonic_ns();
+ uint64_t pixel_time = 0;
+ #endif
+ VECTORIO_SHAPE_DEBUG("%p fill_area: fill: {(%5d,%5d), (%5d,%5d)}",
+ self,
+ area->x1, area->y1, area->x2, area->y2
+ );
+ displayio_area_t overlap;
+ if (!displayio_area_compute_overlap(area, &self->current_area, &overlap)) {
+ VECTORIO_SHAPE_DEBUG(" no overlap\n");
+ return false;
+ }
+ VECTORIO_SHAPE_DEBUG(", overlap: {(%3d,%3d), (%3d,%3d)}", overlap.x1, overlap.y1, overlap.x2, overlap.y2);
+
+ bool full_coverage = displayio_area_equal(area, &overlap);
+
+ uint8_t pixels_per_byte = 8 / colorspace->depth;
+ VECTORIO_SHAPE_DEBUG(" xy:(%3d %3d) tform:{x:%d y:%d dx:%d dy:%d scl:%d w:%d h:%d mx:%d my:%d tr:%d}",
+ self->x, self->y,
+ self->absolute_transform->x, self->absolute_transform->y, self->absolute_transform->dx, self->absolute_transform->dy, self->absolute_transform->scale,
+ self->absolute_transform->width, self->absolute_transform->height, self->absolute_transform->mirror_x, self->absolute_transform->mirror_y, self->absolute_transform->transpose_xy
+ );
+
+ uint16_t linestride_px = displayio_area_width(area);
+ uint16_t line_dirty_offset_px = (overlap.y1 - area->y1) * linestride_px;
+ uint16_t column_dirty_offset_px = overlap.x1 - area->x1;
+ VECTORIO_SHAPE_DEBUG(", linestride:%3d line_offset:%3d col_offset:%3d depth:%2d ppb:%2d shape:%s",
+ linestride_px, line_dirty_offset_px, column_dirty_offset_px, colorspace->depth, pixels_per_byte, mp_obj_get_type_str(self->ishape.shape));
+
+ displayio_input_pixel_t input_pixel;
+ displayio_output_pixel_t output_pixel;
+
+ displayio_area_t shape_area;
+ self->ishape.get_area(self->ishape.shape, &shape_area);
+
+ uint16_t mask_start_px = line_dirty_offset_px;
+ for (input_pixel.y = overlap.y1; input_pixel.y < overlap.y2; ++input_pixel.y) {
+ mask_start_px += column_dirty_offset_px;
+ for (input_pixel.x = overlap.x1; input_pixel.x < overlap.x2; ++input_pixel.x) {
+ // Check the mask first to see if the pixel has already been set.
+ uint16_t pixel_index = mask_start_px + (input_pixel.x - overlap.x1);
+ uint32_t *mask_doubleword = &(mask[pixel_index / 32]);
+ uint8_t mask_bit = pixel_index % 32;
+ VECTORIO_SHAPE_PIXEL_DEBUG("\n%p pixel_index: %5u mask_bit: %2u mask: "U32_TO_BINARY_FMT, self, pixel_index, mask_bit, U32_TO_BINARY(*mask_doubleword));
+ if ((*mask_doubleword & (1u << mask_bit)) != 0) {
+ VECTORIO_SHAPE_PIXEL_DEBUG(" masked");
+ continue;
+ }
+ output_pixel.pixel = 0;
+
+ // Cast input screen coordinates to shape coordinates to pick the pixel to draw
+ int16_t pixel_to_get_x;
+ int16_t pixel_to_get_y;
+ screen_to_shape_coordinates(self, input_pixel.x, input_pixel.y, &pixel_to_get_x, &pixel_to_get_y);
+
+ VECTORIO_SHAPE_PIXEL_DEBUG(" get_pixel %p (%3d, %3d) -> ( %3d, %3d )", self->ishape.shape, input_pixel.x, input_pixel.y, pixel_to_get_x, pixel_to_get_y);
+ #ifdef VECTORIO_PERF
+ uint64_t pre_pixel = common_hal_time_monotonic_ns();
+ #endif
+ input_pixel.pixel = self->ishape.get_pixel(self->ishape.shape, pixel_to_get_x, pixel_to_get_y);
+ #ifdef VECTORIO_PERF
+ uint64_t post_pixel = common_hal_time_monotonic_ns();
+ pixel_time += post_pixel - pre_pixel;
+ #endif
+ VECTORIO_SHAPE_PIXEL_DEBUG(" -> %d", input_pixel.pixel);
+
+ // vectorio shapes use 0 to mean "area is not covered."
+ // We can skip all the rest of the work for this pixel if it's not currently covered by the shape.
+ if (input_pixel.pixel == 0) {
+ VECTORIO_SHAPE_PIXEL_DEBUG(" (encountered transparent pixel; input area is not fully covered)");
+ full_coverage = false;
+ } else {
+ // Pixel is not transparent. Let's pull the pixel value index down to 0-base for more error-resistant palettes.
+ input_pixel.pixel -= 1;
+ output_pixel.opaque = true;
+
+ if (self->pixel_shader == mp_const_none) {
+ output_pixel.pixel = input_pixel.pixel;
+ } else if (mp_obj_is_type(self->pixel_shader, &displayio_palette_type)) {
+ output_pixel.opaque = displayio_palette_get_color(self->pixel_shader, colorspace, input_pixel.pixel, &output_pixel.pixel);
+ } else if (mp_obj_is_type(self->pixel_shader, &displayio_colorconverter_type)) {
+ displayio_colorconverter_convert(self->pixel_shader, colorspace, &input_pixel, &output_pixel);
+ }
+
+ // We double-check this to fast-path the case when a pixel is not covered by the shape & not call the color converter unnecessarily.
+ if (!output_pixel.opaque) {
+ VECTORIO_SHAPE_PIXEL_DEBUG(" (encountered transparent pixel from colorconverter; input area is not fully covered)");
+ full_coverage = false;
+ }
+
+ *mask_doubleword |= 1u << mask_bit;
+ if (colorspace->depth == 16) {
+ VECTORIO_SHAPE_PIXEL_DEBUG(" buffer = %04x 16", output_pixel.pixel);
+ *(((uint16_t *)buffer) + pixel_index) = output_pixel.pixel;
+ } else if (colorspace->depth == 32) {
+ VECTORIO_SHAPE_PIXEL_DEBUG(" buffer = %04x 32", output_pixel.pixel);
+ *(((uint32_t *)buffer) + pixel_index) = output_pixel.pixel;
+ } else if (colorspace->depth == 8) {
+ VECTORIO_SHAPE_PIXEL_DEBUG(" buffer = %02x 8", output_pixel.pixel);
+ *(((uint8_t *)buffer) + pixel_index) = output_pixel.pixel;
+ } else if (colorspace->depth < 8) {
+ // Reorder the offsets to pack multiple rows into a byte (meaning they share a column).
+ if (!colorspace->pixels_in_byte_share_row) {
+ uint16_t row = pixel_index / linestride_px;
+ uint16_t col = pixel_index % linestride_px;
+ pixel_index = col * pixels_per_byte + (row / pixels_per_byte) * pixels_per_byte * linestride_px + row % pixels_per_byte;
+ }
+ uint8_t shift = (pixel_index % pixels_per_byte) * colorspace->depth;
+ if (colorspace->reverse_pixels_in_byte) {
+ // Reverse the shift by subtracting it from the leftmost shift.
+ shift = (pixels_per_byte - 1) * colorspace->depth - shift;
+ }
+ VECTORIO_SHAPE_PIXEL_DEBUG(" buffer = %2d %d", output_pixel.pixel, colorspace->depth);
+ ((uint8_t *)buffer)[pixel_index / pixels_per_byte] |= output_pixel.pixel << shift;
+ }
+ }
+ }
+ mask_start_px += linestride_px - column_dirty_offset_px;
+ }
+ #ifdef VECTORIO_PERF
+ uint64_t end = common_hal_time_monotonic_ns();
+ uint32_t pixels = (overlap.x2 - overlap.x1) * (overlap.y2 - overlap.y1);
+ VECTORIO_PERF("draw %16s -> shape:{%4dpx, %4.1fms,%9.1fpps fill} shape_pixels:{%6.1fus total, %4.1fus/px}\n",
+ mp_obj_get_type_str(self->ishape.shape),
+ (overlap.x2 - overlap.x1) * (overlap.y2 - overlap.y1),
+ (double)((end - start) / 1000000.0),
+ (double)(MAX(1, pixels * (1000000000.0 / (end - start)))),
+ (double)(pixel_time / 1000.0),
+ (double)(pixel_time / 1000.0 / pixels)
+ );
+ #endif
+ VECTORIO_SHAPE_DEBUG(" -> pixels:%4d\n", (overlap.x2 - overlap.x1) * (overlap.y2 - overlap.y1));
+ return full_coverage;
+}
+
+
+void vectorio_vector_shape_finish_refresh(vectorio_vector_shape_t *self) {
+ if (displayio_area_empty(&self->ephemeral_dirty_area) && !self->current_area_dirty) {
+ return;
+ }
+ VECTORIO_SHAPE_DEBUG("%p finish_refresh was:{(%3d,%3d), (%3d,%3d)}\n", self, self->ephemeral_dirty_area.x1, self->ephemeral_dirty_area.y1, self->ephemeral_dirty_area.x2, self->ephemeral_dirty_area.y2);
+ // Reset dirty area to nothing
+ self->ephemeral_dirty_area.x1 = self->ephemeral_dirty_area.x2; // Cheat to set area to empty
+ self->ephemeral_dirty_area.next = NULL;
+
+ self->current_area_dirty = false; // We don't clear current area so we can remember what to clean up if we move
+ self->current_area.next = NULL;
+
+ VECTORIO_SHAPE_DEBUG("%p finish_refresh now:{(%3d,%3d), (%3d,%3d)}\n", self, self->ephemeral_dirty_area.x1, self->ephemeral_dirty_area.y1, self->ephemeral_dirty_area.x2, self->ephemeral_dirty_area.y2);
+
+ if (mp_obj_is_type(self->pixel_shader, &displayio_palette_type)) {
+ displayio_palette_finish_refresh(self->pixel_shader);
+ } else if (mp_obj_is_type(self->pixel_shader, &displayio_colorconverter_type)) {
+ displayio_colorconverter_finish_refresh(self->pixel_shader);
+ }
+}
+
+
+// Assembles a singly linked list of dirty areas from all components on the display.
+displayio_area_t *vectorio_vector_shape_get_refresh_areas(vectorio_vector_shape_t *self, displayio_area_t *tail) {
+ if (self->current_area_dirty
+ || (mp_obj_is_type(self->pixel_shader, &displayio_palette_type) && displayio_palette_needs_refresh(self->pixel_shader))
+ || (mp_obj_is_type(self->pixel_shader, &displayio_colorconverter_type) && displayio_colorconverter_needs_refresh(self->pixel_shader))
+ ) {
+ if (!displayio_area_empty(&self->ephemeral_dirty_area)) {
+ // Both are dirty, check if we should combine the areas or draw separately
+ // Draws as few pixels as possible both when animations move short distances and large distances.
+ // The display core implementation currently doesn't combine areas to reduce redrawing of masked areas. If it does,
+ // this could be simplified to just return the 2 possibly overlapping areas.
+ displayio_area_t area_swap;
+ displayio_area_compute_overlap(&self->ephemeral_dirty_area, &self->current_area, &area_swap);
+ uint32_t overlap_size = displayio_area_size(&area_swap);
+ displayio_area_union(&self->ephemeral_dirty_area, &self->current_area, &area_swap); // Leave area_swap as the union area for later.
+ uint32_t union_size = displayio_area_size(&area_swap);
+ uint32_t current_size = displayio_area_size(&self->current_area);
+ uint32_t dirty_size = displayio_area_size(&self->ephemeral_dirty_area);
+
+ VECTORIO_SHAPE_DEBUG("%p get_refresh_area: dirty{(%3d,%3d), (%3d,%3d)} + current{(%3d,%3d), (%3d,%3d)} = union{(%3d,%3d), (%3d,%3d)}: union%d - dirty%d - curr%d + overlap%d = excluded%d : ", self,
+ self->ephemeral_dirty_area.x1, self->ephemeral_dirty_area.y1, self->ephemeral_dirty_area.x2, self->ephemeral_dirty_area.y2,
+ self->current_area.x1, self->current_area.y1, self->current_area.x2, self->current_area.y2,
+ area_swap.x1, area_swap.y1, area_swap.x2, area_swap.y2,
+ union_size, dirty_size, current_size, overlap_size, (int32_t)union_size - dirty_size - current_size + overlap_size
+ );
+
+ if ((int32_t)union_size - dirty_size - current_size + overlap_size <= MIN(dirty_size, current_size)) {
+ // The excluded / non-overlapping area from the disjoint dirty and current areas is smaller
+ // than the smallest area we need to draw. Redrawing the overlapping area would cost more
+ // than just drawing the union disjoint area once.
+ VECTORIO_SHAPE_DEBUG("combining to take disjoint area\n");
+ displayio_area_copy(&area_swap, &self->ephemeral_dirty_area);
+ } else {
+ // The excluded area between the 2 dirty areas is larger than the smallest dirty area. It would be
+ // more costly to combine these areas than possibly redraw some overlap.
+ VECTORIO_SHAPE_DEBUG("excluded area too large, drawing separate area\n");
+ self->current_area.next = tail;
+ tail = &self->current_area;
+ }
+
+ self->ephemeral_dirty_area.next = tail;
+ tail = &self->ephemeral_dirty_area;
+ } else {
+ self->current_area.next = tail;
+ tail = &self->current_area;
+ VECTORIO_SHAPE_DEBUG("%p get_refresh_area: redrawing current: {(%3d,%3d), (%3d,%3d)}\n", self, self->current_area.x1, self->current_area.y1, self->current_area.x2, self->current_area.y2);
+ }
+ } else if (!displayio_area_empty(&self->ephemeral_dirty_area)) {
+ self->ephemeral_dirty_area.next = tail;
+ tail = &self->ephemeral_dirty_area;
+ VECTORIO_SHAPE_DEBUG("%p get_refresh_area redrawing dirty: {(%3d,%3d), (%3d,%3d)}\n", self, self->ephemeral_dirty_area.x1, self->ephemeral_dirty_area.y1, self->ephemeral_dirty_area.x2, self->ephemeral_dirty_area.y2);
+ }
+ return tail;
+}
+
+void vectorio_vector_shape_update_transform(vectorio_vector_shape_t *self, displayio_buffer_transform_t *group_transform) {
+ self->absolute_transform = group_transform == NULL ? &null_transform : group_transform;
+ common_hal_vectorio_vector_shape_set_dirty(self);
+}
diff --git a/circuitpython/shared-module/vectorio/VectorShape.h b/circuitpython/shared-module/vectorio/VectorShape.h
new file mode 100644
index 0000000..fdbae96
--- /dev/null
+++ b/circuitpython/shared-module/vectorio/VectorShape.h
@@ -0,0 +1,54 @@
+
+#ifndef MICROPY_INCLUDED_SHARED_MODULE_VECTORIO_SHAPE_H
+#define MICROPY_INCLUDED_SHARED_MODULE_VECTORIO_SHAPE_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "py/obj.h"
+#include "shared-module/displayio/area.h"
+#include "shared-module/displayio/Palette.h"
+
+typedef void get_area_function(mp_obj_t shape, displayio_area_t *out_area);
+typedef uint32_t get_pixel_function(mp_obj_t shape, int16_t x, int16_t y);
+
+// This struct binds a shape's common Shape support functions (its vector shape interface)
+// to its instance pointer. We only check at construction time what the type of the
+// associated shape is and link the correct functions up.
+// Later when using the shape for drawing logic these functions may be invoked
+// unconditionally. This simplifies the addition of new types and restricts the
+// respective responsibilities of VectorShape and actual shape implementations.
+typedef struct {
+ mp_obj_t shape;
+ get_area_function *get_area;
+ get_pixel_function *get_pixel;
+} vectorio_ishape_t;
+
+typedef struct {
+ mp_obj_base_t base;
+ vectorio_ishape_t ishape;
+ mp_obj_t pixel_shader;
+ int16_t x;
+ int16_t y;
+ displayio_buffer_transform_t *absolute_transform;
+ // Tracks current shape footprint and expands outward as the shape dirties and changes.
+ // This is suboptimal if you move your shape far. Could add more state to only redraw
+ // exactly what we left behind.
+ displayio_area_t ephemeral_dirty_area;
+ displayio_area_t current_area;
+ bool current_area_dirty;
+} vectorio_vector_shape_t;
+
+displayio_area_t *vectorio_vector_shape_get_refresh_areas(vectorio_vector_shape_t *self, displayio_area_t *tail);
+
+bool vectorio_vector_shape_get_dirty_area(vectorio_vector_shape_t *self, displayio_area_t *current_dirty_area);
+
+// Area is always in absolute screen coordinates.
+bool vectorio_vector_shape_fill_area(vectorio_vector_shape_t *self, const _displayio_colorspace_t *colorspace, const displayio_area_t *area, uint32_t *mask, uint32_t *buffer);
+
+// Fills in out_area with the maximum bounds of all related pixels in the last rendered frame. Returns
+// false if the vector shape wasn't rendered in the last frame.
+bool vectorio_vector_shape_get_previous_area(vectorio_vector_shape_t *self, displayio_area_t *out_area);
+void vectorio_vector_shape_finish_refresh(vectorio_vector_shape_t *self);
+
+#endif // MICROPY_INCLUDED_SHARED_MODULE_VECTORIO_SHAPE_H
diff --git a/circuitpython/shared-module/vectorio/__init__.c b/circuitpython/shared-module/vectorio/__init__.c
new file mode 100644
index 0000000..f5227ef
--- /dev/null
+++ b/circuitpython/shared-module/vectorio/__init__.c
@@ -0,0 +1,2 @@
+
+// Don't need anything in here yet
diff --git a/circuitpython/shared-module/vectorio/__init__.h b/circuitpython/shared-module/vectorio/__init__.h
new file mode 100644
index 0000000..8da85bb
--- /dev/null
+++ b/circuitpython/shared-module/vectorio/__init__.h
@@ -0,0 +1,14 @@
+#ifndef MICROPY_INCLUDED_SHARED_MODULE_VECTORIO_INIT_H
+#define MICROPY_INCLUDED_SHARED_MODULE_VECTORIO_INIT_H
+
+#include "py/obj.h"
+
+typedef void event_function(mp_obj_t obj);
+
+typedef struct {
+ mp_obj_t obj;
+ event_function *event;
+} vectorio_event_t;
+
+
+#endif
diff --git a/circuitpython/shared-module/zlib/__init__.c b/circuitpython/shared-module/zlib/__init__.c
new file mode 100644
index 0000000..a057e6e
--- /dev/null
+++ b/circuitpython/shared-module/zlib/__init__.c
@@ -0,0 +1,100 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2022 Mark Komus
+ *
+ * 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.
+ */
+
+#include <stdint.h>
+#include <assert.h>
+#include <string.h>
+
+#include "py/obj.h"
+#include "py/runtime.h"
+#include "py/stream.h"
+#include "py/mperrno.h"
+#include "py/builtin.h"
+#include "py/objtuple.h"
+#include "py/binary.h"
+#include "py/parsenum.h"
+
+#include "shared-bindings/zlib/__init__.h"
+
+#define UZLIB_CONF_PARANOID_CHECKS (1)
+#include "lib/uzlib/tinf.h"
+
+#if 0 // print debugging info
+#define DEBUG_printf DEBUG_printf
+#else // don't print debugging info
+#define DEBUG_printf(...) (void)0
+#endif
+
+mp_obj_t common_hal_zlib_decompress(mp_obj_t data, bool is_zlib) {
+ mp_buffer_info_t bufinfo;
+ mp_get_buffer_raise(data, &bufinfo, MP_BUFFER_READ);
+
+ TINF_DATA *decomp = m_new_obj(TINF_DATA);
+ memset(decomp, 0, sizeof(*decomp));
+ DEBUG_printf("sizeof(TINF_DATA)=" UINT_FMT "\n", sizeof(*decomp));
+ uzlib_uncompress_init(decomp, NULL, 0);
+ mp_uint_t dest_buf_size = (bufinfo.len + 15) & ~15;
+ byte *dest_buf = m_new(byte, dest_buf_size);
+
+ decomp->dest = dest_buf;
+ decomp->dest_limit = dest_buf + dest_buf_size;
+ DEBUG_printf("zlib: Initial out buffer: " UINT_FMT " bytes\n", decomp->destSize);
+ decomp->source = bufinfo.buf;
+ decomp->source_limit = (unsigned char *)bufinfo.buf + bufinfo.len;
+ int st;
+
+ if (is_zlib) {
+ st = uzlib_zlib_parse_header(decomp);
+ if (st < 0) {
+ goto error;
+ }
+ }
+
+ while (1) {
+ st = uzlib_uncompress_chksum(decomp);
+ if (st < 0) {
+ goto error;
+ }
+ if (st == TINF_DONE) {
+ break;
+ }
+ size_t offset = decomp->dest - dest_buf;
+ dest_buf = m_renew(byte, dest_buf, dest_buf_size, dest_buf_size + 256);
+ dest_buf_size += 256;
+ decomp->dest = dest_buf + offset;
+ decomp->dest_limit = dest_buf + offset + 256;
+ }
+
+ mp_uint_t final_sz = decomp->dest - dest_buf;
+ DEBUG_printf("zlib: Resizing from " UINT_FMT " to final size: " UINT_FMT " bytes\n", dest_buf_size, final_sz);
+ dest_buf = (byte *)m_renew(byte, dest_buf, dest_buf_size, final_sz);
+ mp_obj_t res = mp_obj_new_bytearray_by_ref(final_sz, dest_buf);
+ m_del_obj(TINF_DATA, decomp);
+ return res;
+
+error:
+ mp_raise_type_arg(&mp_type_ValueError, MP_OBJ_NEW_SMALL_INT(st));
+}