Merge remote-tracking branch 'upstream/main' into stm-alarm
This commit is contained in:
commit
0d3c5222d8
|
@ -11,6 +11,7 @@
|
|||
*.bat text eol=crlf
|
||||
|
||||
# These are binary so should never be modified by git.
|
||||
*.a binary
|
||||
*.png binary
|
||||
*.jpg binary
|
||||
*.dxf binary
|
||||
|
|
|
@ -37,7 +37,7 @@ jobs:
|
|||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y eatmydata
|
||||
sudo eatmydata apt-get install -y gettext librsvg2-bin mingw-w64 latexmk texlive-fonts-recommended texlive-latex-recommended texlive-latex-extra
|
||||
sudo eatmydata apt-get install -y gettext librsvg2-bin mingw-w64 latexmk texlive-fonts-recommended texlive-latex-recommended texlive-latex-extra gcc-aarch64-linux-gnu
|
||||
pip install -r requirements-dev.txt
|
||||
- name: Versions
|
||||
run: |
|
||||
|
@ -71,28 +71,25 @@ jobs:
|
|||
run: make -C mpy-cross -j2
|
||||
- name: Build unix port
|
||||
run: |
|
||||
make -C ports/unix deplibs -j2
|
||||
make -C ports/unix -j2
|
||||
make -C ports/unix coverage -j2
|
||||
make -C ports/unix VARIANT=coverage -j2
|
||||
- name: Test all
|
||||
run: MICROPY_CPYTHON3=python3.8 MICROPY_MICROPYTHON=../ports/unix/micropython_coverage ./run-tests -j1
|
||||
run: MICROPY_CPYTHON3=python3.8 MICROPY_MICROPYTHON=../ports/unix/micropython-coverage ./run-tests -j1
|
||||
working-directory: tests
|
||||
- name: Print failure info
|
||||
run: |
|
||||
shopt -s nullglob;
|
||||
for exp in *.exp;
|
||||
do testbase=$(basename $exp .exp);
|
||||
echo -e "\nFAILURE $testbase";
|
||||
diff -u $testbase.exp $testbase.out;
|
||||
done
|
||||
working-directory: tests
|
||||
if: failure()
|
||||
- name: Native Tests
|
||||
run: MICROPY_CPYTHON3=python3.8 MICROPY_MICROPYTHON=../ports/unix/micropython_coverage ./run-tests -j1 --emit native
|
||||
run: MICROPY_CPYTHON3=python3.8 MICROPY_MICROPYTHON=../ports/unix/micropython-coverage ./run-tests -j1 --emit native
|
||||
working-directory: tests
|
||||
- name: mpy Tests
|
||||
run: MICROPY_CPYTHON3=python3.8 MICROPY_MICROPYTHON=../ports/unix/micropython_coverage ./run-tests -j1 --via-mpy -d basics float
|
||||
run: MICROPY_CPYTHON3=python3.8 MICROPY_MICROPYTHON=../ports/unix/micropython-coverage ./run-tests -j1 --mpy-cross-flags='-mcache-lookup-bc' --via-mpy -d basics float micropython
|
||||
working-directory: tests
|
||||
- name: Native mpy Tests
|
||||
run: MICROPY_CPYTHON3=python3.8 MICROPY_MICROPYTHON=../ports/unix/micropython-coverage ./run-tests -j1 --mpy-cross-flags='-mcache-lookup-bc' --via-mpy --emit native -d basics float micropython
|
||||
working-directory: tests
|
||||
- name: Build mpy-cross.static-aarch64
|
||||
run: make -C mpy-cross -j2 -f Makefile.static-aarch64
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: mpy-cross.static-aarch64
|
||||
path: mpy-cross/mpy-cross.static-aarch64
|
||||
- name: Build mpy-cross.static-raspbian
|
||||
run: make -C mpy-cross -j2 -f Makefile.static-raspbian
|
||||
- uses: actions/upload-artifact@v2
|
||||
|
@ -113,6 +110,7 @@ jobs:
|
|||
path: mpy-cross/mpy-cross.static.exe
|
||||
- name: Upload stubs and mpy-cross builds to S3
|
||||
run: |
|
||||
[ -z "$AWS_ACCESS_KEY_ID" ] || aws s3 cp mpy-cross/mpy-cross.static-aarch64 s3://adafruit-circuit-python/bin/mpy-cross/mpy-cross.static-aarch64-${{ env.CP_VERSION }} --no-progress --region us-east-1
|
||||
[ -z "$AWS_ACCESS_KEY_ID" ] || aws s3 cp mpy-cross/mpy-cross.static-raspbian s3://adafruit-circuit-python/bin/mpy-cross/mpy-cross.static-raspbian-${{ env.CP_VERSION }} --no-progress --region us-east-1
|
||||
[ -z "$AWS_ACCESS_KEY_ID" ] || aws s3 cp mpy-cross/mpy-cross.static s3://adafruit-circuit-python/bin/mpy-cross/mpy-cross.static-amd64-linux-${{ env.CP_VERSION }} --no-progress --region us-east-1
|
||||
[ -z "$AWS_ACCESS_KEY_ID" ] || aws s3 cp mpy-cross/mpy-cross.static.exe s3://adafruit-circuit-python/bin/mpy-cross/mpy-cross.static-x64-windows-${{ env.CP_VERSION }}.exe --no-progress --region us-east-1
|
||||
|
@ -209,6 +207,7 @@ jobs:
|
|||
- "clue_nrf52840_express"
|
||||
- "cp32-m4"
|
||||
- "cp_sapling_m0"
|
||||
- "cp_sapling_m0_revb"
|
||||
- "cp_sapling_m0_spiflash"
|
||||
- "datalore_ip_m4"
|
||||
- "datum_distance"
|
||||
|
@ -237,7 +236,6 @@ jobs:
|
|||
- "feather_mimxrt1011"
|
||||
- "feather_mimxrt1062"
|
||||
- "feather_nrf52840_express"
|
||||
- "feather_radiofruit_zigbee"
|
||||
- "feather_stm32f405_express"
|
||||
- "fluff_m0"
|
||||
- "gemma_m0"
|
||||
|
@ -498,7 +496,7 @@ jobs:
|
|||
id: idf-cache
|
||||
with:
|
||||
path: ${{ github.workspace }}/.idf_tools
|
||||
key: ${{ runner.os }}-idf-tools-${{ hashFiles('.git/modules/ports/esp32s2/esp-idf/HEAD') }}-20210422
|
||||
key: ${{ runner.os }}-idf-tools-${{ hashFiles('.git/modules/ports/esp32s2/esp-idf/HEAD') }}-20210506
|
||||
- name: Clone IDF submodules
|
||||
run: |
|
||||
(cd $IDF_PATH && git submodule update --init)
|
||||
|
|
|
@ -28,16 +28,16 @@ dist/
|
|||
######################
|
||||
*.swp
|
||||
|
||||
# Build directory
|
||||
# Build directories
|
||||
######################
|
||||
build/
|
||||
bin/
|
||||
circuitpython-stubs/
|
||||
build-*/
|
||||
|
||||
# Test failure outputs
|
||||
######################
|
||||
tests/*.exp
|
||||
tests/*.out
|
||||
tests/results/*
|
||||
|
||||
# Python cache files
|
||||
######################
|
||||
|
|
|
@ -762,7 +762,6 @@ today. The names appear in order of pledging.
|
|||
1642 Udine
|
||||
1643 Simon Critchley
|
||||
1644 Sven Haiges, Germany
|
||||
1645 Yi Qing Sim
|
||||
1646 "silicium" ("silicium_one", if "silicium" is busy)
|
||||
1648 Andy O'Malia, @andyomalia
|
||||
1650 RedCamelApps.com
|
||||
|
|
2
LICENSE
2
LICENSE
|
@ -1,6 +1,6 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2013-2019 Damien P. George
|
||||
Copyright (c) 2013-2020 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
|
||||
|
|
6
Makefile
6
Makefile
|
@ -7,7 +7,7 @@
|
|||
|
||||
# You can set these variables from the command line.
|
||||
PYTHON = python3
|
||||
SPHINXOPTS =
|
||||
SPHINXOPTS = -W --keep-going
|
||||
SPHINXBUILD = sphinx-build
|
||||
PAPER =
|
||||
# path to build the generated docs
|
||||
|
@ -223,7 +223,7 @@ pseudoxml:
|
|||
all-source:
|
||||
|
||||
locale/circuitpython.pot: all-source
|
||||
find $(TRANSLATE_SOURCES) -type d \( $(TRANSLATE_SOURCES_EXC) \) -prune -o -type f \( -iname "*.c" -o -iname "*.h" \) -print | (LC_ALL=C sort) | xgettext -f- -L C -s --add-location=file --keyword=translate -o - | sed -e '/"POT-Creation-Date: /d' > $@
|
||||
find $(TRANSLATE_SOURCES) -type d \( $(TRANSLATE_SOURCES_EXC) \) -prune -o -type f \( -iname "*.c" -o -iname "*.h" \) -print | (LC_ALL=C sort) | xgettext -f- -L C -s --add-location=file --keyword=translate --keyword=MP_ERROR_TEXT -o - | sed -e '/"POT-Creation-Date: /d' > $@
|
||||
|
||||
# Historically, `make translate` updated the .pot file and ran msgmerge.
|
||||
# However, this was a frequent source of merge conflicts. Weblate can perform
|
||||
|
@ -248,7 +248,7 @@ merge-translate:
|
|||
|
||||
.PHONY: check-translate
|
||||
check-translate:
|
||||
find $(TRANSLATE_SOURCES) -type d \( $(TRANSLATE_SOURCES_EXC) \) -prune -o -type f \( -iname "*.c" -o -iname "*.h" \) -print | (LC_ALL=C sort) | xgettext -f- -L C -s --add-location=file --keyword=translate -o circuitpython.pot.tmp -p locale
|
||||
find $(TRANSLATE_SOURCES) -type d \( $(TRANSLATE_SOURCES_EXC) \) -prune -o -type f \( -iname "*.c" -o -iname "*.h" \) -print | (LC_ALL=C sort) | xgettext -f- -L C -s --add-location=file --keyword=translate --keyword=MP_ERROR_TEXT -o circuitpython.pot.tmp -p locale
|
||||
$(PYTHON) tools/check_translations.py locale/circuitpython.pot.tmp locale/circuitpython.pot; status=$$?; rm -f locale/circuitpython.pot.tmp; exit $$status
|
||||
|
||||
stubs:
|
||||
|
|
|
@ -62,33 +62,4 @@ The tinyusb examples already include a "WebUSB serial" example.
|
|||
Basically, this feature was ported into CircuitPython by pulling code snippets out of the
|
||||
tinyusb example, and putting them where they best belonged in the CircuitPython codebase.
|
||||
|
||||
There was one complication:
|
||||
|
||||
tinyusb uses C preprocessor macros to define things like USB descriptors.
|
||||
|
||||
CircuitPython uses a Python program (tools/gen_usb_descriptor.py) to create USB descriptors (etc.)
|
||||
using "helper objects" from another repo (adafruit_usb_descriptor). This means some of the example
|
||||
code had to be adapted to the new programing model, and gen_usb_descriptor gained new command-line
|
||||
options to control the generated code.
|
||||
|
||||
The generated files go into the "build" directory, look for autogen_usb_descriptor.c and
|
||||
genhdr/autogen_usb_descriptor.h.
|
||||
|
||||
|
||||
Also worth pointing out - the re-use of the CDC connect/disconnect mechanism is not actually part
|
||||
of the WebUSB standard, it's more of "common idiom". We make use of it here because we need to know
|
||||
when we should be paying attention to the WebUSB serial interface, and when we should ignore it..
|
||||
|
||||
## Possible future work areas
|
||||
|
||||
The current code uses the existing Python infrastructure to create the Interface descriptor, but
|
||||
simply outputs the code snippets from the original tinyusb demo code to create the WEBUSB_URL,
|
||||
BOS, and MS_OS_20 descriptors. I suppose additional work could be done to add these to the
|
||||
adafruit_usb_descriptor project, and then gen_usb_descriptor.py could be modified to make use
|
||||
of them.
|
||||
|
||||
Program gen_usb_descriptor.py creates objects for most interface types, regardless of whether or
|
||||
not they are actually enabled. This increases the size of a generated string table. I made the
|
||||
new vendor-interface-related code not do this (because some of the ARM platforms would no longer
|
||||
build), but I did not go back and do this for the other interface types (CDC, MIDI, HID, etc.)
|
||||
Some FLASH savings are probably possible if this is done.
|
||||
### TODO: This needs to be reworked for dynamic USB descriptors.
|
||||
|
|
4
conf.py
4
conf.py
|
@ -25,7 +25,6 @@ import sys
|
|||
import urllib.parse
|
||||
import time
|
||||
|
||||
import recommonmark
|
||||
from sphinx.transforms import SphinxTransform
|
||||
from docutils import nodes
|
||||
from sphinx import addnodes
|
||||
|
@ -68,8 +67,9 @@ extensions = [
|
|||
'sphinx.ext.intersphinx',
|
||||
'sphinx.ext.todo',
|
||||
'sphinx.ext.coverage',
|
||||
'sphinx_search.extension',
|
||||
'rstjinja',
|
||||
'recommonmark',
|
||||
'myst_parser',
|
||||
]
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
|
|
|
@ -238,11 +238,36 @@ Module description
|
|||
After the license comment::
|
||||
|
||||
"""
|
||||
`<module name>` - <Short description>
|
||||
`<module name>`
|
||||
=================================================
|
||||
<Longer description.>
|
||||
|
||||
<Longer description>
|
||||
|
||||
* Author(s):
|
||||
|
||||
Implementation Notes
|
||||
--------------------
|
||||
|
||||
|
||||
**Hardware:**
|
||||
|
||||
* `Adafruit Device Description
|
||||
<hyperlink>`_ (Product ID: <Product Number>)
|
||||
|
||||
**Software and Dependencies:**
|
||||
|
||||
* Adafruit CircuitPython firmware for the supported boards:
|
||||
https://circuitpython.org/downloads
|
||||
|
||||
* Adafruit's Bus Device library:
|
||||
https://github.com/adafruit/Adafruit_CircuitPython_BusDevice
|
||||
|
||||
* Adafruit's Register library:
|
||||
https://github.com/adafruit/Adafruit_CircuitPython_Register
|
||||
|
||||
"""
|
||||
|
||||
|
||||
Class description
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
@ -269,6 +294,58 @@ Renders as:
|
|||
:param ~busio.I2C i2c_bus: The I2C bus the DS3231 is connected to.
|
||||
:param int address: The I2C address of the device. Defaults to :const:`0x40`
|
||||
|
||||
|
||||
Documenting Parameters
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Although there are different ways to document class and functions definitions in Python,
|
||||
the following is the prevalent method of documenting parameters
|
||||
for CircuitPython libraries. When documenting class parameters you should use the
|
||||
following structure:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
:param param_type param_name: Parameter_description
|
||||
|
||||
|
||||
param_type
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
The type of the parameter. This could be among other `int`, `float`, `str` `bool`, etc.
|
||||
To document an object in the CircuitPython domain, you need to include a ``~`` before the
|
||||
definition as shown in the following example:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
:param ~busio.I2C i2c_bus: The I2C bus the DS3231 is connected to.
|
||||
|
||||
|
||||
To include references to CircuitPython modules, cookiecutter creates an entry in the
|
||||
intersphinx_mapping section in the ``conf.py`` file located within the ``docs`` directory.
|
||||
To add different types outside CircuitPython you need to include them in the intersphinx_mapping::
|
||||
|
||||
|
||||
intersphinx_mapping = {
|
||||
"python": ("https://docs.python.org/3.4", None),
|
||||
"BusDevice":("https://circuitpython.readthedocs.io/projects/busdevice/en/latest/", None,),
|
||||
"CircuitPython": ("https://circuitpython.readthedocs.io/en/latest/", None),
|
||||
}
|
||||
|
||||
The intersphinx_mapping above includes references to Python, BusDevice and CircuitPython
|
||||
Documentation
|
||||
|
||||
|
||||
param_name
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Parameter name used in the class or method definition
|
||||
|
||||
|
||||
Parameter_description
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Parameter description. When the parameter defaults to a particular value, it is good
|
||||
practice to include the default::
|
||||
|
||||
:param int pitch: Pitch value for the servo. Defaults to :const:`4500`
|
||||
|
||||
|
||||
Attributes
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
@ -383,6 +460,14 @@ Renders as:
|
|||
|
||||
:param float degrees: Degrees to turn right
|
||||
|
||||
Documentation References to other Libraries
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
When you need to make references to documentation in other libraries you should refer the class using single
|
||||
backticks ``:class:`~adafruit_motor.servo.Servo```. You must also add the reference in the ``conf.py`` file in the
|
||||
``intersphinx_mapping section`` by adding a new entry::
|
||||
|
||||
"adafruit_motor": ("https://circuitpython.readthedocs.io/projects/motor/en/latest/", None,),
|
||||
|
||||
Use BusDevice
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
|
@ -442,6 +527,50 @@ SPI Example
|
|||
spi.readinto(self.buf)
|
||||
return self.buf[0]
|
||||
|
||||
|
||||
|
||||
Class documentation example template
|
||||
--------------------------------------------------------------------------------
|
||||
When documenting classes, you should use the following template to illustrate basic usage.
|
||||
It is similar with the simpletest example, however this will display the information in the Read The Docs
|
||||
documentation.
|
||||
The advantage of using this template is it makes the documentation consistent across the libraries.
|
||||
|
||||
This is an example for a AHT20 temperature sensor. Include the following after the class parameter:
|
||||
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
"""
|
||||
|
||||
**Quickstart: Importing and using the AHT10/AHT20 temperature sensor**
|
||||
|
||||
Here is an example of using the :class:`AHTx0` class.
|
||||
First you will need to import the libraries to use the sensor
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import board
|
||||
import adafruit_ahtx0
|
||||
|
||||
Once this is done you can define your `board.I2C` object and define your sensor object
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
i2c = board.I2C() # uses board.SCL and board.SDA
|
||||
aht = adafruit_ahtx0.AHTx0(i2c)
|
||||
|
||||
Now you have access to the temperature and humidity using
|
||||
the :attr:`temperature` and :attr:`relative_humidity` attributes
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
temperature = aht.temperature
|
||||
relative_humidity = aht.relative_humidity
|
||||
|
||||
"""
|
||||
|
||||
|
||||
Use composition
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
|
@ -458,10 +587,10 @@ object instead of the pins themselves. This allows the calling code to provide
|
|||
any object with the appropriate methods such as an I2C expansion board.
|
||||
|
||||
Another example is to expect a :py:class:`~digitalio.DigitalInOut` for a pin to
|
||||
toggle instead of a :py:class:`~microcontroller.Pin` from `board`. Taking in the
|
||||
:py:class:`~microcontroller.Pin` object alone would limit the driver to pins on
|
||||
the actual microcontroller instead of pins provided by another driver such as an
|
||||
IO expander.
|
||||
toggle instead of a :py:class:`~microcontroller.Pin` from :py:mod:`board`.
|
||||
Taking in the :py:class:`~microcontroller.Pin` object alone would limit the
|
||||
driver to pins on the actual microcontroller instead of pins provided by another
|
||||
driver such as an IO expander.
|
||||
|
||||
Lots of small modules
|
||||
--------------------------------------------------------------------------------
|
||||
|
@ -524,6 +653,14 @@ You could other examples if needed featuring different
|
|||
functionalities of the library.
|
||||
If you add additional examples, be sure to include them in the ``examples.rst``. Naming of the examples
|
||||
files should use the name of the library followed by a description, using underscore to separate them.
|
||||
When using print statements you should use the ``" ".format()`` format, as there are particular boards
|
||||
that are not capable to use f-strings.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
text_to_display = "World!"
|
||||
|
||||
print("Hello {}".format(text_to_display))
|
||||
|
||||
Sensor properties and units
|
||||
--------------------------------------------------------------------------------
|
||||
|
|
|
@ -36,6 +36,7 @@ Full Table of Contents
|
|||
:caption: MicroPython specific
|
||||
|
||||
library/index.rst
|
||||
reference/glossary.rst
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
:mod:`array` -- arrays of numeric data
|
||||
======================================
|
||||
=======================================
|
||||
|
||||
.. module:: array
|
||||
:synopsis: efficient arrays of numeric data
|
||||
|
@ -13,7 +13,7 @@ floating-point support).
|
|||
Classes
|
||||
-------
|
||||
|
||||
.. class:: array.array(typecode, [iterable])
|
||||
.. class:: array(typecode, [iterable])
|
||||
|
||||
Create array with elements of given type. Initial contents of the
|
||||
array are given by an `iterable`. If it is not provided, an empty
|
||||
|
|
|
@ -78,7 +78,7 @@ Example::
|
|||
Functions
|
||||
---------
|
||||
|
||||
.. function:: open(stream, \*, flags=0, pagesize=0, cachesize=0, minkeypage=0)
|
||||
.. function:: open(stream, *, flags=0, pagesize=0, cachesize=0, minkeypage=0)
|
||||
|
||||
Open a database from a random-access ``stream`` (like an open file). All
|
||||
other parameters are optional and keyword-only, and allow to tweak advanced
|
||||
|
@ -118,7 +118,7 @@ Methods
|
|||
Flush any data in cache to the underlying stream.
|
||||
|
||||
.. method:: btree.__getitem__(key)
|
||||
btree.get(key, default=None)
|
||||
btree.get(key, default=None, /)
|
||||
btree.__setitem__(key, val)
|
||||
btree.__detitem__(key)
|
||||
btree.__contains__(key)
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|see_cpython_module| :mod:`cpython:errno`.
|
||||
|
||||
This module provides access to symbolic error codes for `OSError` exception.
|
||||
A particular inventory of codes depends on :term:`MicroPython port`.
|
||||
|
||||
Constants
|
||||
---------
|
||||
|
@ -14,7 +15,8 @@ Constants
|
|||
.. data:: EEXIST, EAGAIN, etc.
|
||||
|
||||
Error codes, based on ANSI C/POSIX standard. All error codes start with
|
||||
"E". Errors are usually accessible as ``exc.args[0]``
|
||||
"E". As mentioned above, inventory of the codes depends on
|
||||
:term:`MicroPython port`. Errors are usually accessible as ``exc.args[0]``
|
||||
where ``exc`` is an instance of `OSError`. Usage example::
|
||||
|
||||
try:
|
||||
|
@ -28,5 +30,5 @@ Constants
|
|||
Dictionary mapping numeric error codes to strings with symbolic error
|
||||
code (see above)::
|
||||
|
||||
>>> print(errno.errorcode[uerrno.EEXIST])
|
||||
>>> print(errno.errorcode[errno.EEXIST])
|
||||
EEXIST
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
:mod:`framebuf` --- Frame buffer manipulation
|
||||
:mod:`framebuf` --- frame buffer manipulation
|
||||
=============================================
|
||||
|
||||
.. include:: ../templates/unsupported_in_circuitpython.inc
|
||||
|
@ -21,7 +21,7 @@ For example::
|
|||
import framebuf
|
||||
|
||||
# FrameBuffer needs 2 bytes for every RGB565 pixel
|
||||
fbuf = FrameBuffer(bytearray(10 * 100 * 2), 10, 100, framebuf.RGB565)
|
||||
fbuf = framebuf.FrameBuffer(bytearray(10 * 100 * 2), 10, 100, framebuf.RGB565)
|
||||
|
||||
fbuf.fill(0)
|
||||
fbuf.text('MicroPython!', 0, 0, 0xffff)
|
||||
|
@ -30,7 +30,7 @@ For example::
|
|||
Constructors
|
||||
------------
|
||||
|
||||
.. class:: FrameBuffer(buffer, width, height, format, stride=width)
|
||||
.. class:: FrameBuffer(buffer, width, height, format, stride=width, /)
|
||||
|
||||
Construct a FrameBuffer object. The parameters are:
|
||||
|
||||
|
@ -132,7 +132,7 @@ Constants
|
|||
|
||||
Monochrome (1-bit) color format
|
||||
This defines a mapping where the bits in a byte are horizontally mapped.
|
||||
Each byte occupies 8 horizontal pixels with bit 0 being the leftmost.
|
||||
Each byte occupies 8 horizontal pixels with bit 7 being the leftmost.
|
||||
Subsequent bytes appear at successive horizontal locations until the
|
||||
rightmost edge is reached. Further bytes are rendered on the next row, one
|
||||
pixel lower.
|
||||
|
@ -141,7 +141,7 @@ Constants
|
|||
|
||||
Monochrome (1-bit) color format
|
||||
This defines a mapping where the bits in a byte are horizontally mapped.
|
||||
Each byte occupies 8 horizontal pixels with bit 7 being the leftmost.
|
||||
Each byte occupies 8 horizontal pixels with bit 0 being the leftmost.
|
||||
Subsequent bytes appear at successive horizontal locations until the
|
||||
rightmost edge is reached. Further bytes are rendered on the next row, one
|
||||
pixel lower.
|
||||
|
|
|
@ -37,6 +37,7 @@ with the ``u`` prefix dropped:
|
|||
json.rst
|
||||
re.rst
|
||||
sys.rst
|
||||
uasyncio.rst
|
||||
uctypes.rst
|
||||
uselect.rst
|
||||
uzlib.rst
|
||||
|
|
|
@ -73,17 +73,26 @@ Functions
|
|||
|
||||
.. function:: heap_lock()
|
||||
.. function:: heap_unlock()
|
||||
.. function:: heap_locked()
|
||||
|
||||
Lock or unlock the heap. When locked no memory allocation can occur and a
|
||||
`MemoryError` will be raised if any heap allocation is attempted.
|
||||
`heap_locked()` returns a true value if the heap is currently locked.
|
||||
|
||||
These functions can be nested, ie `heap_lock()` can be called multiple times
|
||||
in a row and the lock-depth will increase, and then `heap_unlock()` must be
|
||||
called the same number of times to make the heap available again.
|
||||
|
||||
Both `heap_unlock()` and `heap_locked()` return the current lock depth
|
||||
(after unlocking for the former) as a non-negative integer, with 0 meaning
|
||||
the heap is not locked.
|
||||
|
||||
If the REPL becomes active with the heap locked then it will be forcefully
|
||||
unlocked.
|
||||
|
||||
Note: `heap_locked()` is not enabled on most ports by default,
|
||||
requires ``MICROPY_PY_MICROPYTHON_HEAP_LOCKED``.
|
||||
|
||||
.. function:: kbd_intr(chr)
|
||||
|
||||
Set the character that will raise a `KeyboardInterrupt` exception. By
|
||||
|
@ -94,3 +103,36 @@ Functions
|
|||
This function can be used to prevent the capturing of Ctrl-C on the
|
||||
incoming stream of characters that is usually used for the REPL, in case
|
||||
that stream is used for other purposes.
|
||||
|
||||
.. function:: schedule(func, arg)
|
||||
|
||||
Schedule the function *func* to be executed "very soon". The function
|
||||
is passed the value *arg* as its single argument. "Very soon" means that
|
||||
the MicroPython runtime will do its best to execute the function at the
|
||||
earliest possible time, given that it is also trying to be efficient, and
|
||||
that the following conditions hold:
|
||||
|
||||
- A scheduled function will never preempt another scheduled function.
|
||||
- Scheduled functions are always executed "between opcodes" which means
|
||||
that all fundamental Python operations (such as appending to a list)
|
||||
are guaranteed to be atomic.
|
||||
- A given port may define "critical regions" within which scheduled
|
||||
functions will never be executed. Functions may be scheduled within
|
||||
a critical region but they will not be executed until that region
|
||||
is exited. An example of a critical region is a preempting interrupt
|
||||
handler (an IRQ).
|
||||
|
||||
A use for this function is to schedule a callback from a preempting IRQ.
|
||||
Such an IRQ puts restrictions on the code that runs in the IRQ (for example
|
||||
the heap may be locked) and scheduling a function to call later will lift
|
||||
those restrictions.
|
||||
|
||||
Note: If `schedule()` is called from a preempting IRQ, when memory
|
||||
allocation is not allowed and the callback to be passed to `schedule()` is
|
||||
a bound method, passing this directly will fail. This is because creating a
|
||||
reference to a bound method causes memory allocation. A solution is to
|
||||
create a reference to the method in the class constructor and to pass that
|
||||
reference to `schedule()`.
|
||||
|
||||
There is a finite queue to hold the scheduled functions and `schedule()`
|
||||
will raise a `RuntimeError` if the queue is full.
|
||||
|
|
|
@ -124,7 +124,7 @@ Functions
|
|||
string for first position which matches regex (which still may be
|
||||
0 if regex is anchored).
|
||||
|
||||
.. function:: sub(regex_str, replace, string, count=0, flags=0)
|
||||
.. function:: sub(regex_str, replace, string, count=0, flags=0, /)
|
||||
|
||||
Compile *regex_str* and search for it in *string*, replacing all matches
|
||||
with *replace*, and returning the new string.
|
||||
|
@ -138,11 +138,12 @@ Functions
|
|||
If *count* is specified and non-zero then substitution will stop after
|
||||
this many substitutions are made. The *flags* argument is ignored.
|
||||
|
||||
Note: availability of this function depends on MicroPython port.
|
||||
Note: availability of this function depends on :term:`MicroPython port`.
|
||||
|
||||
.. data:: DEBUG
|
||||
|
||||
Flag value, display debug information about compiled expression.
|
||||
(Availability depends on :term:`MicroPython port`.)
|
||||
|
||||
|
||||
.. _regex:
|
||||
|
@ -155,14 +156,14 @@ Compiled regular expression. Instances of this class are created using
|
|||
|
||||
.. method:: regex.match(string)
|
||||
regex.search(string)
|
||||
regex.sub(replace, string, count=0, flags=0)
|
||||
regex.sub(replace, string, count=0, flags=0, /)
|
||||
|
||||
Similar to the module-level functions :meth:`match`, :meth:`search`
|
||||
and :meth:`sub`.
|
||||
Using methods is (much) more efficient if the same regex is applied to
|
||||
multiple strings.
|
||||
|
||||
.. method:: regex.split(string, max_split=-1)
|
||||
.. method:: regex.split(string, max_split=-1, /)
|
||||
|
||||
Split a *string* using regex. If *max_split* is given, it specifies
|
||||
maximum number of splits to perform. Returns list of strings (there
|
||||
|
@ -183,7 +184,7 @@ to the replacement function in `sub()`.
|
|||
|
||||
Return a tuple containing all the substrings of the groups of the match.
|
||||
|
||||
Note: availability of this method depends on MicroPython port.
|
||||
Note: availability of this method depends on :term:`MicroPython port`.
|
||||
|
||||
.. method:: match.start([index])
|
||||
match.end([index])
|
||||
|
@ -192,10 +193,10 @@ to the replacement function in `sub()`.
|
|||
substring group that was matched. *index* defaults to the entire
|
||||
group, otherwise it will select a group.
|
||||
|
||||
Note: availability of these methods depends on MicroPython port.
|
||||
Note: availability of these methods depends on :term:`MicroPython port`.
|
||||
|
||||
.. method:: match.span([index])
|
||||
|
||||
Returns the 2-tuple ``(match.start(index), match.end(index))``.
|
||||
|
||||
Note: availability of this method depends on MicroPython port.
|
||||
Note: availability of this method depends on :term:`MicroPython port`.
|
||||
|
|
|
@ -11,26 +11,12 @@
|
|||
Functions
|
||||
---------
|
||||
|
||||
.. function:: exit(retval=0)
|
||||
.. function:: exit(retval=0, /)
|
||||
|
||||
Terminate current program with a given exit code. Underlyingly, this
|
||||
function raise as `SystemExit` exception. If an argument is given, its
|
||||
value given as an argument to `SystemExit`.
|
||||
|
||||
.. function:: print_exception(exc, file=sys.stdout)
|
||||
|
||||
Print exception with a traceback to a file-like object *file* (or
|
||||
`sys.stdout` by default).
|
||||
|
||||
.. admonition:: Difference to CPython
|
||||
:class: attention
|
||||
|
||||
This is simplified version of a function which appears in the
|
||||
``traceback`` module in CPython. Unlike ``traceback.print_exception()``,
|
||||
this function takes just exception value instead of exception type,
|
||||
exception value, and traceback object; *file* argument should be
|
||||
positional; further arguments are not supported.
|
||||
|
||||
Constants
|
||||
---------
|
||||
|
||||
|
@ -122,3 +108,9 @@ Constants
|
|||
.. data:: version_info
|
||||
|
||||
Python language version that this implementation conforms to, as a tuple of ints.
|
||||
|
||||
.. admonition:: Difference to CPython
|
||||
:class: attention
|
||||
|
||||
Only the first three version numbers (major, minor, micro) are supported and
|
||||
they can be referenced only by index, not by name.
|
||||
|
|
|
@ -0,0 +1,303 @@
|
|||
:mod:`uasyncio` --- asynchronous I/O scheduler
|
||||
==============================================
|
||||
|
||||
.. module:: uasyncio
|
||||
:synopsis: asynchronous I/O scheduler for writing concurrent code
|
||||
|
||||
|see_cpython_module|
|
||||
`asyncio <https://docs.python.org/3.8/library/asyncio.html>`_
|
||||
|
||||
Example::
|
||||
|
||||
import uasyncio
|
||||
|
||||
async def blink(led, period_ms):
|
||||
while True:
|
||||
led.on()
|
||||
await uasyncio.sleep_ms(5)
|
||||
led.off()
|
||||
await uasyncio.sleep_ms(period_ms)
|
||||
|
||||
async def main(led1, led2):
|
||||
uasyncio.create_task(blink(led1, 700))
|
||||
uasyncio.create_task(blink(led2, 400))
|
||||
await uasyncio.sleep_ms(10_000)
|
||||
|
||||
# Running on a pyboard
|
||||
from pyb import LED
|
||||
uasyncio.run(main(LED(1), LED(2)))
|
||||
|
||||
# Running on a generic board
|
||||
from machine import Pin
|
||||
uasyncio.run(main(Pin(1), Pin(2)))
|
||||
|
||||
Core functions
|
||||
--------------
|
||||
|
||||
.. function:: create_task(coro)
|
||||
|
||||
Create a new task from the given coroutine and schedule it to run.
|
||||
|
||||
Returns the corresponding `Task` object.
|
||||
|
||||
.. function:: run(coro)
|
||||
|
||||
Create a new task from the given coroutine and run it until it completes.
|
||||
|
||||
Returns the value returned by *coro*.
|
||||
|
||||
.. function:: sleep(t)
|
||||
|
||||
Sleep for *t* seconds (can be a float).
|
||||
|
||||
This is a coroutine.
|
||||
|
||||
.. function:: sleep_ms(t)
|
||||
|
||||
Sleep for *t* milliseconds.
|
||||
|
||||
This is a coroutine, and a MicroPython extension.
|
||||
|
||||
Additional functions
|
||||
--------------------
|
||||
|
||||
.. function:: wait_for(awaitable, timeout)
|
||||
|
||||
Wait for the *awaitable* to complete, but cancel it if it takes longer
|
||||
that *timeout* seconds. If *awaitable* is not a task then a task will be
|
||||
created from it.
|
||||
|
||||
If a timeout occurs, it cancels the task and raises ``asyncio.TimeoutError``:
|
||||
this should be trapped by the caller.
|
||||
|
||||
Returns the return value of *awaitable*.
|
||||
|
||||
This is a coroutine.
|
||||
|
||||
.. function:: wait_for_ms(awaitable, timeout)
|
||||
|
||||
Similar to `wait_for` but *timeout* is an integer in milliseconds.
|
||||
|
||||
This is a coroutine, and a MicroPython extension.
|
||||
|
||||
.. function:: gather(*awaitables, return_exceptions=False)
|
||||
|
||||
Run all *awaitables* concurrently. Any *awaitables* that are not tasks are
|
||||
promoted to tasks.
|
||||
|
||||
Returns a list of return values of all *awaitables*.
|
||||
|
||||
This is a coroutine.
|
||||
|
||||
class Task
|
||||
----------
|
||||
|
||||
.. class:: Task()
|
||||
|
||||
This object wraps a coroutine into a running task. Tasks can be waited on
|
||||
using ``await task``, which will wait for the task to complete and return
|
||||
the return value of the task.
|
||||
|
||||
Tasks should not be created directly, rather use `create_task` to create them.
|
||||
|
||||
.. method:: Task.cancel()
|
||||
|
||||
Cancel the task by injecting a ``CancelledError`` into it. The task may
|
||||
or may not ignore this exception.
|
||||
|
||||
class Event
|
||||
-----------
|
||||
|
||||
.. class:: Event()
|
||||
|
||||
Create a new event which can be used to synchronise tasks. Events start
|
||||
in the cleared state.
|
||||
|
||||
.. method:: Event.is_set()
|
||||
|
||||
Returns ``True`` if the event is set, ``False`` otherwise.
|
||||
|
||||
.. method:: Event.set()
|
||||
|
||||
Set the event. Any tasks waiting on the event will be scheduled to run.
|
||||
|
||||
.. method:: Event.clear()
|
||||
|
||||
Clear the event.
|
||||
|
||||
.. method:: Event.wait()
|
||||
|
||||
Wait for the event to be set. If the event is already set then it returns
|
||||
immediately.
|
||||
|
||||
This is a coroutine.
|
||||
|
||||
class Lock
|
||||
----------
|
||||
|
||||
.. class:: Lock()
|
||||
|
||||
Create a new lock which can be used to coordinate tasks. Locks start in
|
||||
the unlocked state.
|
||||
|
||||
In addition to the methods below, locks can be used in an ``async with`` statement.
|
||||
|
||||
.. method:: Lock.locked()
|
||||
|
||||
Returns ``True`` if the lock is locked, otherwise ``False``.
|
||||
|
||||
.. method:: Lock.acquire()
|
||||
|
||||
Wait for the lock to be in the unlocked state and then lock it in an atomic
|
||||
way. Only one task can acquire the lock at any one time.
|
||||
|
||||
This is a coroutine.
|
||||
|
||||
.. method:: Lock.release()
|
||||
|
||||
Release the lock. If any tasks are waiting on the lock then the next one in the
|
||||
queue is scheduled to run and the lock remains locked. Otherwise, no tasks are
|
||||
waiting an the lock becomes unlocked.
|
||||
|
||||
TCP stream connections
|
||||
----------------------
|
||||
|
||||
.. function:: open_connection(host, port)
|
||||
|
||||
Open a TCP connection to the given *host* and *port*. The *host* address will be
|
||||
resolved using `socket.getaddrinfo`, which is currently a blocking call.
|
||||
|
||||
Returns a pair of streams: a reader and a writer stream.
|
||||
Will raise a socket-specific ``OSError`` if the host could not be resolved or if
|
||||
the connection could not be made.
|
||||
|
||||
This is a coroutine.
|
||||
|
||||
.. function:: start_server(callback, host, port, backlog=5)
|
||||
|
||||
Start a TCP server on the given *host* and *port*. The *callback* will be
|
||||
called with incoming, accepted connections, and be passed 2 arguments: reader
|
||||
and writer streams for the connection.
|
||||
|
||||
Returns a `Server` object.
|
||||
|
||||
This is a coroutine.
|
||||
|
||||
.. class:: Stream()
|
||||
|
||||
This represents a TCP stream connection. To minimise code this class implements
|
||||
both a reader and a writer, and both ``StreamReader`` and ``StreamWriter`` alias to
|
||||
this class.
|
||||
|
||||
.. method:: Stream.get_extra_info(v)
|
||||
|
||||
Get extra information about the stream, given by *v*. The valid values for *v* are:
|
||||
``peername``.
|
||||
|
||||
.. method:: Stream.close()
|
||||
|
||||
Close the stream.
|
||||
|
||||
.. method:: Stream.wait_closed()
|
||||
|
||||
Wait for the stream to close.
|
||||
|
||||
This is a coroutine.
|
||||
|
||||
.. method:: Stream.read(n)
|
||||
|
||||
Read up to *n* bytes and return them.
|
||||
|
||||
This is a coroutine.
|
||||
|
||||
.. method:: Stream.readline()
|
||||
|
||||
Read a line and return it.
|
||||
|
||||
This is a coroutine.
|
||||
|
||||
.. method:: Stream.write(buf)
|
||||
|
||||
Accumulated *buf* to the output buffer. The data is only flushed when
|
||||
`Stream.drain` is called. It is recommended to call `Stream.drain` immediately
|
||||
after calling this function.
|
||||
|
||||
.. method:: Stream.drain()
|
||||
|
||||
Drain (write) all buffered output data out to the stream.
|
||||
|
||||
This is a coroutine.
|
||||
|
||||
.. class:: Server()
|
||||
|
||||
This represents the server class returned from `start_server`. It can be used
|
||||
in an ``async with`` statement to close the server upon exit.
|
||||
|
||||
.. method:: Server.close()
|
||||
|
||||
Close the server.
|
||||
|
||||
.. method:: Server.wait_closed()
|
||||
|
||||
Wait for the server to close.
|
||||
|
||||
This is a coroutine.
|
||||
|
||||
Event Loop
|
||||
----------
|
||||
|
||||
.. function:: get_event_loop()
|
||||
|
||||
Return the event loop used to schedule and run tasks. See `Loop`.
|
||||
|
||||
.. function:: new_event_loop()
|
||||
|
||||
Reset the event loop and return it.
|
||||
|
||||
Note: since MicroPython only has a single event loop this function just
|
||||
resets the loop's state, it does not create a new one.
|
||||
|
||||
.. class:: Loop()
|
||||
|
||||
This represents the object which schedules and runs tasks. It cannot be
|
||||
created, use `get_event_loop` instead.
|
||||
|
||||
.. method:: Loop.create_task(coro)
|
||||
|
||||
Create a task from the given *coro* and return the new `Task` object.
|
||||
|
||||
.. method:: Loop.run_forever()
|
||||
|
||||
Run the event loop until `stop()` is called.
|
||||
|
||||
.. method:: Loop.run_until_complete(awaitable)
|
||||
|
||||
Run the given *awaitable* until it completes. If *awaitable* is not a task
|
||||
then it will be promoted to one.
|
||||
|
||||
.. method:: Loop.stop()
|
||||
|
||||
Stop the event loop.
|
||||
|
||||
.. method:: Loop.close()
|
||||
|
||||
Close the event loop.
|
||||
|
||||
.. method:: Loop.set_exception_handler(handler)
|
||||
|
||||
Set the exception handler to call when a Task raises an exception that is not
|
||||
caught. The *handler* should accept two arguments: ``(loop, context)``.
|
||||
|
||||
.. method:: Loop.get_exception_handler()
|
||||
|
||||
Get the current exception handler. Returns the handler, or ``None`` if no
|
||||
custom handler is set.
|
||||
|
||||
.. method:: Loop.default_exception_handler(context)
|
||||
|
||||
The default exception handler that is called.
|
||||
|
||||
.. method:: Loop.call_exception_handler(context)
|
||||
|
||||
Call the current exception handler. The argument *context* is passed through and
|
||||
is a dictionary containing keys: ``'message'``, ``'exception'``, ``'future'``.
|
|
@ -182,7 +182,7 @@ Following are encoding examples for various field types:
|
|||
Module contents
|
||||
---------------
|
||||
|
||||
.. class:: struct(addr, descriptor, layout_type=NATIVE)
|
||||
.. class:: struct(addr, descriptor, layout_type=NATIVE, /)
|
||||
|
||||
Instantiate a "foreign data structure" object based on structure address in
|
||||
memory, descriptor (encoded as a dictionary), and layout type (see below).
|
||||
|
@ -202,7 +202,7 @@ Module contents
|
|||
Layout type for a native structure - with data endianness and alignment
|
||||
conforming to the ABI of the system on which MicroPython runs.
|
||||
|
||||
.. function:: sizeof(struct, layout_type=NATIVE)
|
||||
.. function:: sizeof(struct, layout_type=NATIVE, /)
|
||||
|
||||
Return size of data structure in bytes. The *struct* argument can be
|
||||
either a structure class or a specific instantiated structure object
|
||||
|
|
|
@ -60,7 +60,7 @@ Methods
|
|||
Modify the *eventmask* for *obj*. If *obj* is not registered, `OSError`
|
||||
is raised with error of ENOENT.
|
||||
|
||||
.. method:: poll.poll(timeout=-1)
|
||||
.. method:: poll.poll(timeout=-1, /)
|
||||
|
||||
Wait for at least one of the registered objects to become ready or have an
|
||||
exceptional condition, with optional timeout in milliseconds (if *timeout*
|
||||
|
@ -83,7 +83,7 @@ Methods
|
|||
|
||||
Tuples returned may contain more than 2 elements as described above.
|
||||
|
||||
.. method:: poll.ipoll(timeout=-1, flags=0)
|
||||
.. method:: poll.ipoll(timeout=-1, flags=0, /)
|
||||
|
||||
Like :meth:`poll.poll`, but instead returns an iterator which yields a
|
||||
``callee-owned tuples``. This function provides efficient, allocation-free
|
||||
|
|
|
@ -16,7 +16,7 @@ is not yet implemented.
|
|||
Functions
|
||||
---------
|
||||
|
||||
.. function:: decompress(data, wbits=0, bufsize=0)
|
||||
.. function:: decompress(data, wbits=0, bufsize=0, /)
|
||||
|
||||
Return decompressed *data* as bytes. *wbits* is DEFLATE dictionary window
|
||||
size used during compression (8-15, the dictionary size is power of 2 of
|
||||
|
@ -25,7 +25,7 @@ Functions
|
|||
to be raw DEFLATE stream. *bufsize* parameter is for compatibility with
|
||||
CPython and is ignored.
|
||||
|
||||
.. class:: DecompIO(stream, wbits=0)
|
||||
.. class:: DecompIO(stream, wbits=0, /)
|
||||
|
||||
Create a ``stream`` wrapper which allows transparent decompression of
|
||||
compressed data in another *stream*. This allows to process compressed
|
||||
|
|
|
@ -0,0 +1,175 @@
|
|||
Glossary
|
||||
========
|
||||
|
||||
.. glossary::
|
||||
|
||||
baremetal
|
||||
A system without a (full-fledged) operating system, for example an
|
||||
:term:`MCU`-based system. When running on a baremetal system,
|
||||
MicroPython effectively functions like a small operating system,
|
||||
running user programs and providing a command interpreter
|
||||
(:term:`REPL`).
|
||||
|
||||
buffer protocol
|
||||
Any Python object that can be automatically converted into bytes, such
|
||||
as ``bytes``, ``bytearray``, ``memoryview`` and ``str`` objects, which
|
||||
all implement the "buffer protocol".
|
||||
|
||||
board
|
||||
Typically this refers to a printed circuit board (PCB) containing a
|
||||
:term:`microcontroller <MCU>` and supporting components.
|
||||
MicroPython firmware is typically provided per-board, as the firmware
|
||||
contains both MCU-specific functionality but also board-level
|
||||
functionality such as drivers or pin names.
|
||||
|
||||
bytecode
|
||||
A compact representation of a Python program that generated by
|
||||
compiling the Python source code. This is what the VM actually
|
||||
executes. Bytecode is typically generated automatically at runtime and
|
||||
is invisible to the user. Note that while :term:`CPython` and
|
||||
MicroPython both use bytecode, the format is different. You can also
|
||||
pre-compile source code offline using the :term:`cross-compiler`.
|
||||
|
||||
callee-owned tuple
|
||||
This is a MicroPython-specific construct where, for efficiency
|
||||
reasons, some built-in functions or methods may re-use the same
|
||||
underlying tuple object to return data. This avoids having to allocate
|
||||
a new tuple for every call, and reduces heap fragmentation. Programs
|
||||
should not hold references to callee-owned tuples and instead only
|
||||
extract data from them (or make a copy).
|
||||
|
||||
CircuitPython
|
||||
A variant of MicroPython developed by `Adafruit Industries
|
||||
<https://circuitpython.org>`_.
|
||||
|
||||
CPython
|
||||
CPython is the reference implementation of the Python programming
|
||||
language, and the most well-known one. It is, however, one of many
|
||||
implementations (including Jython, IronPython, PyPy, and MicroPython).
|
||||
While MicroPython's implementation differs substantially from CPython,
|
||||
it aims to maintain as much compatibility as possible.
|
||||
|
||||
cross-compiler
|
||||
Also known as ``mpy-cross``. This tool runs on your PC and converts a
|
||||
:term:`.py file` containing MicroPython code into a :term:`.mpy file`
|
||||
containing MicroPython bytecode. This means it loads faster (the board
|
||||
doesn't have to compile the code), and uses less space on flash (the
|
||||
bytecode is more space efficient).
|
||||
|
||||
driver
|
||||
A MicroPython library that implements support for a particular
|
||||
component, such as a sensor or display.
|
||||
|
||||
FFI
|
||||
Acronym for Foreign Function Interface. A mechanism used by the
|
||||
:term:`MicroPython Unix port` to access operating system functionality.
|
||||
This is not available on :term:`baremetal` ports.
|
||||
|
||||
filesystem
|
||||
Most MicroPython ports and boards provide a filesystem stored in flash
|
||||
that is available to user code via the standard Python file APIs such
|
||||
as ``open()``. Some boards also make this internal filesystem
|
||||
accessible to the host via USB mass-storage.
|
||||
|
||||
frozen module
|
||||
A Python module that has been cross compiled and bundled into the
|
||||
firmware image. This reduces RAM requirements as the code is executed
|
||||
directly from flash.
|
||||
|
||||
Garbage Collector
|
||||
A background process that runs in Python (and MicroPython) to reclaim
|
||||
unused memory in the :term:`heap`.
|
||||
|
||||
GPIO
|
||||
General-purpose input/output. The simplest means to control electrical
|
||||
signals (commonly referred to as "pins") on a microcontroller. GPIO
|
||||
typically allows pins to be either input or output, and to set or get
|
||||
their digital value (logical "0" or "1"). MicroPython abstracts GPIO
|
||||
access using the :class:`machine.Pin` and :class:`machine.Signal`
|
||||
classes.
|
||||
|
||||
GPIO port
|
||||
A group of :term:`GPIO` pins, usually based on hardware properties of
|
||||
these pins (e.g. controllable by the same register).
|
||||
|
||||
heap
|
||||
A region of RAM where MicroPython stores dynamic data. It is managed
|
||||
automatically by the :term:`Garbage Collector`. Different MCUs and
|
||||
boards have vastly different amounts of RAM available for the heap, so
|
||||
this will affect how complex your program can be.
|
||||
|
||||
interned string
|
||||
An optimisation used by MicroPython to improve the efficiency of
|
||||
working with strings. An interned string is referenced by its (unique)
|
||||
identity rather than its address and can therefore be quickly compared
|
||||
just by its identifier. It also means that identical strings can be
|
||||
de-duplicated in memory. String interning is almost always invisible to
|
||||
the user.
|
||||
|
||||
MCU
|
||||
Microcontroller. Microcontrollers usually have much less resources
|
||||
than a desktop, laptop, or phone, but are smaller, cheaper and
|
||||
require much less power. MicroPython is designed to be small and
|
||||
optimized enough to run on an average modern microcontroller.
|
||||
|
||||
MicroPython port
|
||||
MicroPython supports different :term:`boards <board>`, RTOSes, and
|
||||
OSes, and can be relatively easily adapted to new systems. MicroPython
|
||||
with support for a particular system is called a "port" to that
|
||||
system. Different ports may have widely different functionality. This
|
||||
documentation is intended to be a reference of the generic APIs
|
||||
available across different ports ("MicroPython core"). Note that some
|
||||
ports may still omit some APIs described here (e.g. due to resource
|
||||
constraints). Any such differences, and port-specific extensions
|
||||
beyond the MicroPython core functionality, would be described in the
|
||||
separate port-specific documentation.
|
||||
|
||||
MicroPython Unix port
|
||||
The unix port is one of the major :term:`MicroPython ports
|
||||
<MicroPython port>`. It is intended to run on POSIX-compatible
|
||||
operating systems, like Linux, MacOS, FreeBSD, Solaris, etc. It also
|
||||
serves as the basis of Windows port. The Unix port is very useful for
|
||||
quick development and testing of the MicroPython language and
|
||||
machine-independent features. It can also function in a similar way to
|
||||
:term:`CPython`'s ``python`` executable.
|
||||
|
||||
.mpy file
|
||||
The output of the :term:`cross-compiler`. A compiled form of a
|
||||
:term:`.py file` that contains MicroPython bytecode instead of Python
|
||||
source code.
|
||||
|
||||
native
|
||||
Usually refers to "native code", i.e. machine code for the target
|
||||
microcontroller (such as ARM Thumb, Xtensa, x86/x64). The ``@native``
|
||||
decorator can be applied to a MicroPython function to generate native
|
||||
code instead of bytecode for that function, which will likely be
|
||||
faster but use more RAM.
|
||||
|
||||
port
|
||||
Usually short for :term:`MicroPython port`, but could also refer to
|
||||
:term:`GPIO port`.
|
||||
|
||||
.py file
|
||||
A file containing Python source code.
|
||||
|
||||
REPL
|
||||
An acronym for "Read, Eval, Print, Loop". This is the interactive
|
||||
Python prompt, useful for debugging or testing short snippets of code.
|
||||
Most MicroPython boards make a REPL available over a UART, and this is
|
||||
typically accessible on a host PC via USB.
|
||||
|
||||
stream
|
||||
Also known as a "file-like object". An Python object which provides
|
||||
sequential read-write access to the underlying data. A stream object
|
||||
implements a corresponding interface, which consists of methods like
|
||||
``read()``, ``write()``, ``readinto()``, ``seek()``, ``flush()``,
|
||||
``close()``, etc. A stream is an important concept in MicroPython;
|
||||
many I/O objects implement the stream interface, and thus can be used
|
||||
consistently and interchangeably in different contexts. For more
|
||||
information on streams in MicroPython, see the `io` module.
|
||||
|
||||
UART
|
||||
Acronym for "Universal Asynchronous Receiver/Transmitter". This is a
|
||||
peripheral that sends data over a pair of pins (TX & RX). Many boards
|
||||
include a way to make at least one of the UARTs available to a host PC
|
||||
as a serial port over USB.
|
|
@ -1,7 +1,8 @@
|
|||
sphinx<4
|
||||
recommonmark==0.6.0
|
||||
myst-parser==0.14.0
|
||||
sphinxcontrib-svg2pdfconverter==0.1.0
|
||||
astroid
|
||||
sphinx-autoapi
|
||||
isort
|
||||
black
|
||||
readthedocs-sphinx-search
|
||||
|
|
|
@ -155,7 +155,8 @@ def get_settings_from_makefile(port_dir, board_name):
|
|||
|
||||
settings = {}
|
||||
for line in contents.stdout.split('\n'):
|
||||
m = re.match(r'^([A-Z][A-Z0-9_]*) = (.*)$', line)
|
||||
# Handle both = and := definitions.
|
||||
m = re.match(r'^([A-Z][A-Z0-9_]*) :?= (.*)$', line)
|
||||
if m:
|
||||
settings[m.group(1)] = m.group(2)
|
||||
|
||||
|
|
|
@ -4,6 +4,6 @@
|
|||
|
||||
.. |see_cpython_module| replace::
|
||||
|
||||
*This module implements a subset of the corresponding* ``CPython`` *module,
|
||||
as described below. For more information, refer to the original*
|
||||
``CPython`` *documentation:*
|
||||
*This module implements a subset of the corresponding* :term:`CPython` *module,
|
||||
as described below. For more information, refer to the original
|
||||
CPython documentation:*
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
*.mpy
|
|
@ -0,0 +1,37 @@
|
|||
# Location of top-level MicroPython directory
|
||||
MPY_DIR = ../../..
|
||||
|
||||
# Name of module (different to built-in btree so it can coexist)
|
||||
MOD = btree_$(ARCH)
|
||||
|
||||
# Source files (.c or .py)
|
||||
SRC = btree_c.c btree_py.py
|
||||
|
||||
# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin)
|
||||
ARCH = x64
|
||||
|
||||
BTREE_DIR = $(MPY_DIR)/lib/berkeley-db-1.xx
|
||||
BTREE_DEFS = -D__DBINTERFACE_PRIVATE=1 -Dmpool_error="(void)" -Dabort=abort_ "-Dvirt_fd_t=void*" $(BTREE_DEFS_EXTRA)
|
||||
CFLAGS += -I$(BTREE_DIR)/PORT/include
|
||||
CFLAGS += -Wno-old-style-definition -Wno-sign-compare -Wno-unused-parameter $(BTREE_DEFS)
|
||||
|
||||
SRC += $(addprefix $(realpath $(BTREE_DIR))/,\
|
||||
btree/bt_close.c \
|
||||
btree/bt_conv.c \
|
||||
btree/bt_delete.c \
|
||||
btree/bt_get.c \
|
||||
btree/bt_open.c \
|
||||
btree/bt_overflow.c \
|
||||
btree/bt_page.c \
|
||||
btree/bt_put.c \
|
||||
btree/bt_search.c \
|
||||
btree/bt_seq.c \
|
||||
btree/bt_split.c \
|
||||
btree/bt_utils.c \
|
||||
mpool/mpool.c \
|
||||
)
|
||||
|
||||
include $(MPY_DIR)/py/dynruntime.mk
|
||||
|
||||
# btree needs gnu99 defined
|
||||
CFLAGS += -std=gnu99
|
|
@ -0,0 +1,147 @@
|
|||
#define MICROPY_PY_BTREE (1)
|
||||
|
||||
#include "py/dynruntime.h"
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#if !defined(__linux__)
|
||||
void *memcpy(void *dst, const void *src, size_t n) {
|
||||
return mp_fun_table.memmove_(dst, src, n);
|
||||
}
|
||||
void *memset(void *s, int c, size_t n) {
|
||||
return mp_fun_table.memset_(s, c, n);
|
||||
}
|
||||
#endif
|
||||
|
||||
void *memmove(void *dest, const void *src, size_t n) {
|
||||
return mp_fun_table.memmove_(dest, src, n);
|
||||
}
|
||||
|
||||
void *malloc(size_t n) {
|
||||
void *ptr = m_malloc(n, false);
|
||||
return ptr;
|
||||
}
|
||||
void *realloc(void *ptr, size_t n) {
|
||||
mp_printf(&mp_plat_print, "UNDEF %d\n", __LINE__);
|
||||
return NULL;
|
||||
}
|
||||
void *calloc(size_t n, size_t m) {
|
||||
void *ptr = m_malloc(n * m, false);
|
||||
// memory already cleared by conservative GC
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void free(void *ptr) {
|
||||
m_free(ptr);
|
||||
}
|
||||
|
||||
void abort_(void) {
|
||||
nlr_raise(mp_obj_new_exception(mp_load_global(MP_QSTR_RuntimeError)));
|
||||
}
|
||||
|
||||
int native_errno;
|
||||
#if defined(__linux__)
|
||||
int *__errno_location (void)
|
||||
#else
|
||||
int *__errno (void)
|
||||
#endif
|
||||
{
|
||||
return &native_errno;
|
||||
}
|
||||
|
||||
ssize_t mp_stream_posix_write(void *stream, const void *buf, size_t len) {
|
||||
mp_obj_base_t* o = stream;
|
||||
const mp_stream_p_t *stream_p = o->type->protocol;
|
||||
mp_uint_t out_sz = stream_p->write(MP_OBJ_FROM_PTR(stream), buf, len, &native_errno);
|
||||
if (out_sz == MP_STREAM_ERROR) {
|
||||
return -1;
|
||||
} else {
|
||||
return out_sz;
|
||||
}
|
||||
}
|
||||
|
||||
ssize_t mp_stream_posix_read(void *stream, void *buf, size_t len) {
|
||||
mp_obj_base_t* o = stream;
|
||||
const mp_stream_p_t *stream_p = o->type->protocol;
|
||||
mp_uint_t out_sz = stream_p->read(MP_OBJ_FROM_PTR(stream), buf, len, &native_errno);
|
||||
if (out_sz == MP_STREAM_ERROR) {
|
||||
return -1;
|
||||
} else {
|
||||
return out_sz;
|
||||
}
|
||||
}
|
||||
|
||||
off_t mp_stream_posix_lseek(void *stream, off_t offset, int whence) {
|
||||
const mp_obj_base_t* o = stream;
|
||||
const mp_stream_p_t *stream_p = o->type->protocol;
|
||||
struct mp_stream_seek_t seek_s;
|
||||
seek_s.offset = offset;
|
||||
seek_s.whence = whence;
|
||||
mp_uint_t res = stream_p->ioctl(MP_OBJ_FROM_PTR(stream), MP_STREAM_SEEK, (mp_uint_t)(uintptr_t)&seek_s, &native_errno);
|
||||
if (res == MP_STREAM_ERROR) {
|
||||
return -1;
|
||||
}
|
||||
return seek_s.offset;
|
||||
}
|
||||
|
||||
int mp_stream_posix_fsync(void *stream) {
|
||||
mp_obj_base_t* o = stream;
|
||||
const mp_stream_p_t *stream_p = o->type->protocol;
|
||||
mp_uint_t res = stream_p->ioctl(MP_OBJ_FROM_PTR(stream), MP_STREAM_FLUSH, 0, &native_errno);
|
||||
if (res == MP_STREAM_ERROR) {
|
||||
return -1;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
mp_obj_type_t btree_type;
|
||||
|
||||
#include "extmod/modbtree.c"
|
||||
|
||||
mp_map_elem_t btree_locals_dict_table[8];
|
||||
STATIC MP_DEFINE_CONST_DICT(btree_locals_dict, btree_locals_dict_table);
|
||||
|
||||
STATIC mp_obj_t btree_open(size_t n_args, const mp_obj_t *args) {
|
||||
// Make sure we got a stream object
|
||||
mp_get_stream_raise(args[0], MP_STREAM_OP_READ | MP_STREAM_OP_WRITE | MP_STREAM_OP_IOCTL);
|
||||
|
||||
BTREEINFO openinfo = {0};
|
||||
openinfo.flags = mp_obj_get_int(args[1]);
|
||||
openinfo.cachesize = mp_obj_get_int(args[2]);
|
||||
openinfo.psize = mp_obj_get_int(args[3]);
|
||||
openinfo.minkeypage = mp_obj_get_int(args[4]);
|
||||
DB *db = __bt_open(MP_OBJ_TO_PTR(args[0]), &btree_stream_fvtable, &openinfo, 0);
|
||||
if (db == NULL) {
|
||||
mp_raise_OSError(native_errno);
|
||||
}
|
||||
|
||||
return MP_OBJ_FROM_PTR(btree_new(db, args[0]));
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(btree_open_obj, 5, 5, btree_open);
|
||||
|
||||
mp_obj_t mpy_init(mp_obj_fun_bc_t *self, size_t n_args, size_t n_kw, mp_obj_t *args) {
|
||||
MP_DYNRUNTIME_INIT_ENTRY
|
||||
|
||||
btree_type.base.type = (void*)&mp_fun_table.type_type;
|
||||
btree_type.name = MP_QSTR_btree;
|
||||
btree_type.print = btree_print;
|
||||
btree_type.getiter = btree_getiter;
|
||||
btree_type.iternext = btree_iternext;
|
||||
btree_type.binary_op = btree_binary_op;
|
||||
btree_type.subscr = btree_subscr;
|
||||
btree_locals_dict_table[0] = (mp_map_elem_t){ MP_OBJ_NEW_QSTR(MP_QSTR_close), MP_OBJ_FROM_PTR(&btree_close_obj) };
|
||||
btree_locals_dict_table[1] = (mp_map_elem_t){ MP_OBJ_NEW_QSTR(MP_QSTR_flush), MP_OBJ_FROM_PTR(&btree_flush_obj) };
|
||||
btree_locals_dict_table[2] = (mp_map_elem_t){ MP_OBJ_NEW_QSTR(MP_QSTR_get), MP_OBJ_FROM_PTR(&btree_get_obj) };
|
||||
btree_locals_dict_table[3] = (mp_map_elem_t){ MP_OBJ_NEW_QSTR(MP_QSTR_put), MP_OBJ_FROM_PTR(&btree_put_obj) };
|
||||
btree_locals_dict_table[4] = (mp_map_elem_t){ MP_OBJ_NEW_QSTR(MP_QSTR_seq), MP_OBJ_FROM_PTR(&btree_seq_obj) };
|
||||
btree_locals_dict_table[5] = (mp_map_elem_t){ MP_OBJ_NEW_QSTR(MP_QSTR_keys), MP_OBJ_FROM_PTR(&btree_keys_obj) };
|
||||
btree_locals_dict_table[6] = (mp_map_elem_t){ MP_OBJ_NEW_QSTR(MP_QSTR_values), MP_OBJ_FROM_PTR(&btree_values_obj) };
|
||||
btree_locals_dict_table[7] = (mp_map_elem_t){ MP_OBJ_NEW_QSTR(MP_QSTR_items), MP_OBJ_FROM_PTR(&btree_items_obj) };
|
||||
btree_type.locals_dict = (void*)&btree_locals_dict;
|
||||
|
||||
mp_store_global(MP_QSTR__open, MP_OBJ_FROM_PTR(&btree_open_obj));
|
||||
mp_store_global(MP_QSTR_INCL, MP_OBJ_NEW_SMALL_INT(FLAG_END_KEY_INCL));
|
||||
mp_store_global(MP_QSTR_DESC, MP_OBJ_NEW_SMALL_INT(FLAG_DESC));
|
||||
|
||||
MP_DYNRUNTIME_INIT_EXIT
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
# Implemented in Python to support keyword arguments
|
||||
def open(stream, *, flags=0, cachesize=0, pagesize=0, minkeypage=0):
|
||||
return _open(stream, flags, cachesize, pagesize, minkeypage)
|
|
@ -0,0 +1,14 @@
|
|||
# Location of top-level MicroPython directory
|
||||
MPY_DIR = ../../..
|
||||
|
||||
# Name of module
|
||||
MOD = features0
|
||||
|
||||
# Source files (.c or .py)
|
||||
SRC = features0.c
|
||||
|
||||
# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin)
|
||||
ARCH = x64
|
||||
|
||||
# Include to get the rules for compiling and linking the module
|
||||
include $(MPY_DIR)/py/dynruntime.mk
|
|
@ -0,0 +1,40 @@
|
|||
/* This example demonstrates the following features in a native module:
|
||||
- defining a simple function exposed to Python
|
||||
- defining a local, helper C function
|
||||
- getting and creating integer objects
|
||||
*/
|
||||
|
||||
// Include the header file to get access to the MicroPython API
|
||||
#include "py/dynruntime.h"
|
||||
|
||||
// Helper function to compute factorial
|
||||
STATIC mp_int_t factorial_helper(mp_int_t x) {
|
||||
if (x == 0) {
|
||||
return 1;
|
||||
}
|
||||
return x * factorial_helper(x - 1);
|
||||
}
|
||||
|
||||
// This is the function which will be called from Python, as factorial(x)
|
||||
STATIC mp_obj_t factorial(mp_obj_t x_obj) {
|
||||
// Extract the integer from the MicroPython input object
|
||||
mp_int_t x = mp_obj_get_int(x_obj);
|
||||
// Calculate the factorial
|
||||
mp_int_t result = factorial_helper(x);
|
||||
// Convert the result to a MicroPython integer object and return it
|
||||
return mp_obj_new_int(result);
|
||||
}
|
||||
// Define a Python reference to the function above
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(factorial_obj, factorial);
|
||||
|
||||
// This is the entry point and is called when the module is imported
|
||||
mp_obj_t mpy_init(mp_obj_fun_bc_t *self, size_t n_args, size_t n_kw, mp_obj_t *args) {
|
||||
// This must be first, it sets up the globals dict and other things
|
||||
MP_DYNRUNTIME_INIT_ENTRY
|
||||
|
||||
// Make the function available in the module's namespace
|
||||
mp_store_global(MP_QSTR_factorial, MP_OBJ_FROM_PTR(&factorial_obj));
|
||||
|
||||
// This must be last, it restores the globals dict
|
||||
MP_DYNRUNTIME_INIT_EXIT
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
# Location of top-level MicroPython directory
|
||||
MPY_DIR = ../../..
|
||||
|
||||
# Name of module
|
||||
MOD = features1
|
||||
|
||||
# Source files (.c or .py)
|
||||
SRC = features1.c
|
||||
|
||||
# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin)
|
||||
ARCH = x64
|
||||
|
||||
# Include to get the rules for compiling and linking the module
|
||||
include $(MPY_DIR)/py/dynruntime.mk
|
|
@ -0,0 +1,106 @@
|
|||
/* This example demonstrates the following features in a native module:
|
||||
- defining simple functions exposed to Python
|
||||
- defining local, helper C functions
|
||||
- defining constant integers and strings exposed to Python
|
||||
- getting and creating integer objects
|
||||
- creating Python lists
|
||||
- raising exceptions
|
||||
- allocating memory
|
||||
- BSS and constant data (rodata)
|
||||
- relocated pointers in rodata
|
||||
*/
|
||||
|
||||
// Include the header file to get access to the MicroPython API
|
||||
#include "py/dynruntime.h"
|
||||
|
||||
// BSS (zero) data
|
||||
uint16_t data16[4];
|
||||
|
||||
// Constant data (rodata)
|
||||
const uint8_t table8[] = { 0, 1, 1, 2, 3, 5, 8, 13 };
|
||||
const uint16_t table16[] = { 0x1000, 0x2000 };
|
||||
|
||||
// Constant data pointing to BSS/constant data
|
||||
uint16_t *const table_ptr16a[] = { &data16[0], &data16[1], &data16[2], &data16[3] };
|
||||
const uint16_t *const table_ptr16b[] = { &table16[0], &table16[1] };
|
||||
|
||||
// A simple function that adds its 2 arguments (must be integers)
|
||||
STATIC mp_obj_t add(mp_obj_t x_in, mp_obj_t y_in) {
|
||||
mp_int_t x = mp_obj_get_int(x_in);
|
||||
mp_int_t y = mp_obj_get_int(y_in);
|
||||
return mp_obj_new_int(x + y);
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_2(add_obj, add);
|
||||
|
||||
// A local helper function (not exposed to Python)
|
||||
STATIC mp_int_t fibonacci_helper(mp_int_t x) {
|
||||
if (x < MP_ARRAY_SIZE(table8)) {
|
||||
return table8[x];
|
||||
} else {
|
||||
return fibonacci_helper(x - 1) + fibonacci_helper(x - 2);
|
||||
}
|
||||
}
|
||||
|
||||
// A function which computes Fibonacci numbers
|
||||
STATIC mp_obj_t fibonacci(mp_obj_t x_in) {
|
||||
mp_int_t x = mp_obj_get_int(x_in);
|
||||
if (x < 0) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("can't compute negative Fibonacci number"));
|
||||
}
|
||||
return mp_obj_new_int(fibonacci_helper(x));
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(fibonacci_obj, fibonacci);
|
||||
|
||||
// A function that accesses the BSS data
|
||||
STATIC mp_obj_t access(size_t n_args, const mp_obj_t *args) {
|
||||
if (n_args == 0) {
|
||||
// Create a list holding all items from data16
|
||||
mp_obj_list_t *lst = MP_OBJ_TO_PTR(mp_obj_new_list(MP_ARRAY_SIZE(data16), NULL));
|
||||
for (int i = 0; i < MP_ARRAY_SIZE(data16); ++i) {
|
||||
lst->items[i] = mp_obj_new_int(data16[i]);
|
||||
}
|
||||
return MP_OBJ_FROM_PTR(lst);
|
||||
} else if (n_args == 1) {
|
||||
// Get one item from data16
|
||||
mp_int_t idx = mp_obj_get_int(args[0]) & 3;
|
||||
return mp_obj_new_int(data16[idx]);
|
||||
} else {
|
||||
// Set one item in data16 (via table_ptr16a)
|
||||
mp_int_t idx = mp_obj_get_int(args[0]) & 3;
|
||||
*table_ptr16a[idx] = mp_obj_get_int(args[1]);
|
||||
return mp_const_none;
|
||||
}
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(access_obj, 0, 2, access);
|
||||
|
||||
// A function that allocates memory and creates a bytearray
|
||||
STATIC mp_obj_t make_array(void) {
|
||||
uint16_t *ptr = m_new(uint16_t, MP_ARRAY_SIZE(table_ptr16b));
|
||||
for (int i = 0; i < MP_ARRAY_SIZE(table_ptr16b); ++i) {
|
||||
ptr[i] = *table_ptr16b[i];
|
||||
}
|
||||
return mp_obj_new_bytearray_by_ref(sizeof(uint16_t) * MP_ARRAY_SIZE(table_ptr16b), ptr);
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_0(make_array_obj, make_array);
|
||||
|
||||
// This is the entry point and is called when the module is imported
|
||||
mp_obj_t mpy_init(mp_obj_fun_bc_t *self, size_t n_args, size_t n_kw, mp_obj_t *args) {
|
||||
// This must be first, it sets up the globals dict and other things
|
||||
MP_DYNRUNTIME_INIT_ENTRY
|
||||
|
||||
// Messages can be printed as usualy
|
||||
mp_printf(&mp_plat_print, "initialising module self=%p\n", self);
|
||||
|
||||
// Make the functions available in the module's namespace
|
||||
mp_store_global(MP_QSTR_add, MP_OBJ_FROM_PTR(&add_obj));
|
||||
mp_store_global(MP_QSTR_fibonacci, MP_OBJ_FROM_PTR(&fibonacci_obj));
|
||||
mp_store_global(MP_QSTR_access, MP_OBJ_FROM_PTR(&access_obj));
|
||||
mp_store_global(MP_QSTR_make_array, MP_OBJ_FROM_PTR(&make_array_obj));
|
||||
|
||||
// Add some constants to the module's namespace
|
||||
mp_store_global(MP_QSTR_VAL, MP_OBJ_NEW_SMALL_INT(42));
|
||||
mp_store_global(MP_QSTR_MSG, MP_OBJ_NEW_QSTR(MP_QSTR_HELLO_MICROPYTHON));
|
||||
|
||||
// This must be last, it restores the globals dict
|
||||
MP_DYNRUNTIME_INIT_EXIT
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
# Location of top-level MicroPython directory
|
||||
MPY_DIR = ../../..
|
||||
|
||||
# Name of module
|
||||
MOD = features2
|
||||
|
||||
# Source files (.c or .py)
|
||||
SRC = main.c prod.c test.py
|
||||
|
||||
# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin)
|
||||
ARCH = x64
|
||||
|
||||
# Include to get the rules for compiling and linking the module
|
||||
include $(MPY_DIR)/py/dynruntime.mk
|
|
@ -0,0 +1,83 @@
|
|||
/* This example demonstrates the following features in a native module:
|
||||
- using floats
|
||||
- defining additional code in Python (see test.py)
|
||||
- have extra C code in a separate file (see prod.c)
|
||||
*/
|
||||
|
||||
// Include the header file to get access to the MicroPython API
|
||||
#include "py/dynruntime.h"
|
||||
|
||||
// Include the header for auxiliary C code for this module
|
||||
#include "prod.h"
|
||||
|
||||
// Automatically detect if this module should include double-precision code.
|
||||
// If double precision is supported by the target architecture then it can
|
||||
// be used in native module regardless of what float setting the target
|
||||
// MicroPython runtime uses (being none, float or double).
|
||||
#if defined(__i386__) || defined(__x86_64__) || (defined(__ARM_FP) && (__ARM_FP & 8))
|
||||
#define USE_DOUBLE 1
|
||||
#else
|
||||
#define USE_DOUBLE 0
|
||||
#endif
|
||||
|
||||
// A function that uses the default float type configured for the current target
|
||||
// This default can be overridden by specifying MICROPY_FLOAT_IMPL at the make level
|
||||
STATIC mp_obj_t add(mp_obj_t x, mp_obj_t y) {
|
||||
return mp_obj_new_float(mp_obj_get_float(x) + mp_obj_get_float(y));
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_2(add_obj, add);
|
||||
|
||||
// A function that explicitly uses single precision floats
|
||||
STATIC mp_obj_t add_f(mp_obj_t x, mp_obj_t y) {
|
||||
return mp_obj_new_float_from_f(mp_obj_get_float_to_f(x) + mp_obj_get_float_to_f(y));
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_2(add_f_obj, add_f);
|
||||
|
||||
#if USE_DOUBLE
|
||||
// A function that explicitly uses double precision floats
|
||||
STATIC mp_obj_t add_d(mp_obj_t x, mp_obj_t y) {
|
||||
return mp_obj_new_float_from_d(mp_obj_get_float_to_d(x) + mp_obj_get_float_to_d(y));
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_2(add_d_obj, add_d);
|
||||
#endif
|
||||
|
||||
// A function that computes the product of floats in an array.
|
||||
// This function uses the most general C argument interface, which is more difficult
|
||||
// to use but has access to the globals dict of the module via self->globals.
|
||||
STATIC mp_obj_t productf(mp_obj_fun_bc_t *self, size_t n_args, size_t n_kw, mp_obj_t *args) {
|
||||
// Check number of arguments is valid
|
||||
mp_arg_check_num_mp(n_args, n_kw, 1, 1, false);
|
||||
|
||||
// Extract buffer pointer and verify typecode
|
||||
mp_buffer_info_t bufinfo;
|
||||
mp_get_buffer_raise(args[0], &bufinfo, MP_BUFFER_RW);
|
||||
if (bufinfo.typecode != 'f') {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("expecting float array"));
|
||||
}
|
||||
|
||||
// Compute product, store result back in first element of array
|
||||
float *ptr = bufinfo.buf;
|
||||
float prod = prod_array(bufinfo.len / sizeof(*ptr), ptr);
|
||||
ptr[0] = prod;
|
||||
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
// This is the entry point and is called when the module is imported
|
||||
mp_obj_t mpy_init(mp_obj_fun_bc_t *self, size_t n_args, size_t n_kw, mp_obj_t *args) {
|
||||
// This must be first, it sets up the globals dict and other things
|
||||
MP_DYNRUNTIME_INIT_ENTRY
|
||||
|
||||
// Make the functions available in the module's namespace
|
||||
mp_store_global(MP_QSTR_add, MP_OBJ_FROM_PTR(&add_obj));
|
||||
mp_store_global(MP_QSTR_add_f, MP_OBJ_FROM_PTR(&add_f_obj));
|
||||
#if USE_DOUBLE
|
||||
mp_store_global(MP_QSTR_add_d, MP_OBJ_FROM_PTR(&add_d_obj));
|
||||
#endif
|
||||
|
||||
// The productf function uses the most general C argument interface
|
||||
mp_store_global(MP_QSTR_productf, MP_DYNRUNTIME_MAKE_FUNCTION(productf));
|
||||
|
||||
// This must be last, it restores the globals dict
|
||||
MP_DYNRUNTIME_INIT_EXIT
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
#include "prod.h"
|
||||
|
||||
float prod_array(int n, float *ar) {
|
||||
float ans = 1;
|
||||
for (int i = 0; i < n; ++i) {
|
||||
ans *= ar[i];
|
||||
}
|
||||
return ans;
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
float prod_array(int n, float *ar);
|
|
@ -0,0 +1,29 @@
|
|||
# This Python code will be merged with the C code in main.c
|
||||
|
||||
import array
|
||||
|
||||
|
||||
def isclose(a, b):
|
||||
return abs(a - b) < 1e-3
|
||||
|
||||
|
||||
def test():
|
||||
tests = [
|
||||
isclose(add(0.1, 0.2), 0.3),
|
||||
isclose(add_f(0.1, 0.2), 0.3),
|
||||
]
|
||||
|
||||
ar = array.array("f", [1, 2, 3.5])
|
||||
productf(ar)
|
||||
tests.append(isclose(ar[0], 7))
|
||||
|
||||
if "add_d" in globals():
|
||||
tests.append(isclose(add_d(0.1, 0.2), 0.3))
|
||||
|
||||
print(tests)
|
||||
|
||||
if not all(tests):
|
||||
raise SystemExit(1)
|
||||
|
||||
|
||||
test()
|
|
@ -0,0 +1,13 @@
|
|||
# Location of top-level MicroPython directory
|
||||
MPY_DIR = ../../..
|
||||
|
||||
# Name of module (different to built-in framebuf so it can coexist)
|
||||
MOD = framebuf_$(ARCH)
|
||||
|
||||
# Source files (.c or .py)
|
||||
SRC = framebuf.c
|
||||
|
||||
# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin)
|
||||
ARCH = x64
|
||||
|
||||
include $(MPY_DIR)/py/dynruntime.mk
|
|
@ -0,0 +1,49 @@
|
|||
#define MICROPY_PY_FRAMEBUF (1)
|
||||
|
||||
#include "py/dynruntime.h"
|
||||
|
||||
#if !defined(__linux__)
|
||||
void *memset(void *s, int c, size_t n) {
|
||||
return mp_fun_table.memset_(s, c, n);
|
||||
}
|
||||
#endif
|
||||
|
||||
mp_obj_type_t mp_type_framebuf;
|
||||
|
||||
#include "extmod/modframebuf.c"
|
||||
|
||||
mp_map_elem_t framebuf_locals_dict_table[10];
|
||||
STATIC MP_DEFINE_CONST_DICT(framebuf_locals_dict, framebuf_locals_dict_table);
|
||||
|
||||
mp_obj_t mpy_init(mp_obj_fun_bc_t *self, size_t n_args, size_t n_kw, mp_obj_t *args) {
|
||||
MP_DYNRUNTIME_INIT_ENTRY
|
||||
|
||||
mp_type_framebuf.base.type = (void*)&mp_type_type;
|
||||
mp_type_framebuf.name = MP_QSTR_FrameBuffer;
|
||||
mp_type_framebuf.make_new = framebuf_make_new;
|
||||
mp_type_framebuf.buffer_p.get_buffer = framebuf_get_buffer;
|
||||
framebuf_locals_dict_table[0] = (mp_map_elem_t){ MP_OBJ_NEW_QSTR(MP_QSTR_fill), MP_OBJ_FROM_PTR(&framebuf_fill_obj) };
|
||||
framebuf_locals_dict_table[1] = (mp_map_elem_t){ MP_OBJ_NEW_QSTR(MP_QSTR_fill_rect), MP_OBJ_FROM_PTR(&framebuf_fill_rect_obj) };
|
||||
framebuf_locals_dict_table[2] = (mp_map_elem_t){ MP_OBJ_NEW_QSTR(MP_QSTR_pixel), MP_OBJ_FROM_PTR(&framebuf_pixel_obj) };
|
||||
framebuf_locals_dict_table[3] = (mp_map_elem_t){ MP_OBJ_NEW_QSTR(MP_QSTR_hline), MP_OBJ_FROM_PTR(&framebuf_hline_obj) };
|
||||
framebuf_locals_dict_table[4] = (mp_map_elem_t){ MP_OBJ_NEW_QSTR(MP_QSTR_vline), MP_OBJ_FROM_PTR(&framebuf_vline_obj) };
|
||||
framebuf_locals_dict_table[5] = (mp_map_elem_t){ MP_OBJ_NEW_QSTR(MP_QSTR_rect), MP_OBJ_FROM_PTR(&framebuf_rect_obj) };
|
||||
framebuf_locals_dict_table[6] = (mp_map_elem_t){ MP_OBJ_NEW_QSTR(MP_QSTR_line), MP_OBJ_FROM_PTR(&framebuf_line_obj) };
|
||||
framebuf_locals_dict_table[7] = (mp_map_elem_t){ MP_OBJ_NEW_QSTR(MP_QSTR_blit), MP_OBJ_FROM_PTR(&framebuf_blit_obj) };
|
||||
framebuf_locals_dict_table[8] = (mp_map_elem_t){ MP_OBJ_NEW_QSTR(MP_QSTR_scroll), MP_OBJ_FROM_PTR(&framebuf_scroll_obj) };
|
||||
framebuf_locals_dict_table[9] = (mp_map_elem_t){ MP_OBJ_NEW_QSTR(MP_QSTR_text), MP_OBJ_FROM_PTR(&framebuf_text_obj) };
|
||||
mp_type_framebuf.locals_dict = (void*)&framebuf_locals_dict;
|
||||
|
||||
mp_store_global(MP_QSTR_FrameBuffer, MP_OBJ_FROM_PTR(&mp_type_framebuf));
|
||||
mp_store_global(MP_QSTR_FrameBuffer1, MP_OBJ_FROM_PTR(&legacy_framebuffer1_obj));
|
||||
mp_store_global(MP_QSTR_MVLSB, MP_OBJ_NEW_SMALL_INT(FRAMEBUF_MVLSB));
|
||||
mp_store_global(MP_QSTR_MONO_VLSB, MP_OBJ_NEW_SMALL_INT(FRAMEBUF_MVLSB));
|
||||
mp_store_global(MP_QSTR_RGB565, MP_OBJ_NEW_SMALL_INT(FRAMEBUF_RGB565));
|
||||
mp_store_global(MP_QSTR_GS2_HMSB, MP_OBJ_NEW_SMALL_INT(FRAMEBUF_GS2_HMSB));
|
||||
mp_store_global(MP_QSTR_GS4_HMSB, MP_OBJ_NEW_SMALL_INT(FRAMEBUF_GS4_HMSB));
|
||||
mp_store_global(MP_QSTR_GS8, MP_OBJ_NEW_SMALL_INT(FRAMEBUF_GS8));
|
||||
mp_store_global(MP_QSTR_MONO_HLSB, MP_OBJ_NEW_SMALL_INT(FRAMEBUF_MHLSB));
|
||||
mp_store_global(MP_QSTR_MONO_HMSB, MP_OBJ_NEW_SMALL_INT(FRAMEBUF_MHMSB));
|
||||
|
||||
MP_DYNRUNTIME_INIT_EXIT
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
# Location of top-level MicroPython directory
|
||||
MPY_DIR = ../../..
|
||||
|
||||
# Name of module (different to built-in uheapq so it can coexist)
|
||||
MOD = uheapq_$(ARCH)
|
||||
|
||||
# Source files (.c or .py)
|
||||
SRC = uheapq.c
|
||||
|
||||
# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin)
|
||||
ARCH = x64
|
||||
|
||||
include $(MPY_DIR)/py/dynruntime.mk
|
|
@ -0,0 +1,16 @@
|
|||
#define MICROPY_PY_UHEAPQ (1)
|
||||
|
||||
#include "py/dynruntime.h"
|
||||
|
||||
#include "extmod/moduheapq.c"
|
||||
|
||||
mp_obj_t mpy_init(mp_obj_fun_bc_t *self, size_t n_args, size_t n_kw, mp_obj_t *args) {
|
||||
MP_DYNRUNTIME_INIT_ENTRY
|
||||
|
||||
mp_store_global(MP_QSTR___name__, MP_OBJ_NEW_QSTR(MP_QSTR_uheapq));
|
||||
mp_store_global(MP_QSTR_heappush, MP_OBJ_FROM_PTR(&mod_uheapq_heappush_obj));
|
||||
mp_store_global(MP_QSTR_heappop, MP_OBJ_FROM_PTR(&mod_uheapq_heappop_obj));
|
||||
mp_store_global(MP_QSTR_heapify, MP_OBJ_FROM_PTR(&mod_uheapq_heapify_obj));
|
||||
|
||||
MP_DYNRUNTIME_INIT_EXIT
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
# Location of top-level MicroPython directory
|
||||
MPY_DIR = ../../..
|
||||
|
||||
# Name of module (different to built-in urandom so it can coexist)
|
||||
MOD = urandom_$(ARCH)
|
||||
|
||||
# Source files (.c or .py)
|
||||
SRC = urandom.c
|
||||
|
||||
# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin)
|
||||
ARCH = x64
|
||||
|
||||
include $(MPY_DIR)/py/dynruntime.mk
|
|
@ -0,0 +1,33 @@
|
|||
#define MICROPY_PY_URANDOM (1)
|
||||
#define MICROPY_PY_URANDOM_EXTRA_FUNCS (1)
|
||||
|
||||
#include "py/dynruntime.h"
|
||||
|
||||
// Dynamic native modules don't support a data section so these must go in the BSS
|
||||
uint32_t yasmarang_pad, yasmarang_n, yasmarang_d;
|
||||
uint8_t yasmarang_dat;
|
||||
|
||||
#include "extmod/modurandom.c"
|
||||
|
||||
mp_obj_t mpy_init(mp_obj_fun_bc_t *self, size_t n_args, size_t n_kw, mp_obj_t *args) {
|
||||
MP_DYNRUNTIME_INIT_ENTRY
|
||||
|
||||
yasmarang_pad = 0xeda4baba;
|
||||
yasmarang_n = 69;
|
||||
yasmarang_d = 233;
|
||||
|
||||
mp_store_global(MP_QSTR___name__, MP_OBJ_NEW_QSTR(MP_QSTR_urandom));
|
||||
mp_store_global(MP_QSTR_getrandbits, MP_OBJ_FROM_PTR(&mod_urandom_getrandbits_obj));
|
||||
mp_store_global(MP_QSTR_seed, MP_OBJ_FROM_PTR(&mod_urandom_seed_obj));
|
||||
#if MICROPY_PY_URANDOM_EXTRA_FUNCS
|
||||
mp_store_global(MP_QSTR_randrange, MP_OBJ_FROM_PTR(&mod_urandom_randrange_obj));
|
||||
mp_store_global(MP_QSTR_randint, MP_OBJ_FROM_PTR(&mod_urandom_randint_obj));
|
||||
mp_store_global(MP_QSTR_choice, MP_OBJ_FROM_PTR(&mod_urandom_choice_obj));
|
||||
#if MICROPY_PY_BUILTINS_FLOAT
|
||||
mp_store_global(MP_QSTR_random, MP_OBJ_FROM_PTR(&mod_urandom_random_obj));
|
||||
mp_store_global(MP_QSTR_uniform, MP_OBJ_FROM_PTR(&mod_urandom_uniform_obj));
|
||||
#endif
|
||||
#endif
|
||||
|
||||
MP_DYNRUNTIME_INIT_EXIT
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
# Location of top-level MicroPython directory
|
||||
MPY_DIR = ../../..
|
||||
|
||||
# Name of module (different to built-in ure so it can coexist)
|
||||
MOD = ure_$(ARCH)
|
||||
|
||||
# Source files (.c or .py)
|
||||
SRC = ure.c
|
||||
|
||||
# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin)
|
||||
ARCH = x64
|
||||
|
||||
include $(MPY_DIR)/py/dynruntime.mk
|
|
@ -0,0 +1,78 @@
|
|||
#define MICROPY_STACK_CHECK (1)
|
||||
#define MICROPY_PY_URE (1)
|
||||
#define MICROPY_PY_URE_MATCH_GROUPS (1)
|
||||
#define MICROPY_PY_URE_MATCH_SPAN_START_END (1)
|
||||
#define MICROPY_PY_URE_SUB (0) // requires vstr interface
|
||||
|
||||
#include <alloca.h>
|
||||
#include "py/dynruntime.h"
|
||||
|
||||
#define STACK_LIMIT (2048)
|
||||
|
||||
const char *stack_top;
|
||||
|
||||
void mp_stack_check(void) {
|
||||
// Assumes descending stack on target
|
||||
volatile char dummy;
|
||||
if (stack_top - &dummy >= STACK_LIMIT) {
|
||||
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("maximum recursion depth exceeded"));
|
||||
}
|
||||
}
|
||||
|
||||
#if !defined(__linux__)
|
||||
void *memcpy(void *dst, const void *src, size_t n) {
|
||||
return mp_fun_table.memmove_(dst, src, n);
|
||||
}
|
||||
void *memset(void *s, int c, size_t n) {
|
||||
return mp_fun_table.memset_(s, c, n);
|
||||
}
|
||||
#endif
|
||||
|
||||
void *memmove(void *dest, const void *src, size_t n) {
|
||||
return mp_fun_table.memmove_(dest, src, n);
|
||||
}
|
||||
|
||||
mp_obj_type_t match_type;
|
||||
mp_obj_type_t re_type;
|
||||
|
||||
#include "extmod/modure.c"
|
||||
|
||||
mp_map_elem_t match_locals_dict_table[5];
|
||||
STATIC MP_DEFINE_CONST_DICT(match_locals_dict, match_locals_dict_table);
|
||||
|
||||
mp_map_elem_t re_locals_dict_table[3];
|
||||
STATIC MP_DEFINE_CONST_DICT(re_locals_dict, re_locals_dict_table);
|
||||
|
||||
mp_obj_t mpy_init(mp_obj_fun_bc_t *self, size_t n_args, size_t n_kw, mp_obj_t *args) {
|
||||
MP_DYNRUNTIME_INIT_ENTRY
|
||||
|
||||
char dummy;
|
||||
stack_top = &dummy;
|
||||
|
||||
// Because MP_QSTR_start/end/split are static, xtensa and xtensawin will make a small data section
|
||||
// to copy in this key/value pair if they are specified as a struct, so assign them separately.
|
||||
|
||||
match_type.base.type = (void*)&mp_fun_table.type_type;
|
||||
match_type.name = MP_QSTR_match;
|
||||
match_type.print = match_print;
|
||||
match_locals_dict_table[0] = (mp_map_elem_t){ MP_OBJ_NEW_QSTR(MP_QSTR_group), MP_OBJ_FROM_PTR(&match_group_obj) };
|
||||
match_locals_dict_table[1] = (mp_map_elem_t){ MP_OBJ_NEW_QSTR(MP_QSTR_groups), MP_OBJ_FROM_PTR(&match_groups_obj) };
|
||||
match_locals_dict_table[2] = (mp_map_elem_t){ MP_OBJ_NEW_QSTR(MP_QSTR_span), MP_OBJ_FROM_PTR(&match_span_obj) };
|
||||
match_locals_dict_table[3] = (mp_map_elem_t){ MP_OBJ_NEW_QSTR(MP_QSTR_start), MP_OBJ_FROM_PTR(&match_start_obj) };
|
||||
match_locals_dict_table[4] = (mp_map_elem_t){ MP_OBJ_NEW_QSTR(MP_QSTR_end), MP_OBJ_FROM_PTR(&match_end_obj) };
|
||||
match_type.locals_dict = (void*)&match_locals_dict;
|
||||
|
||||
re_type.base.type = (void*)&mp_fun_table.type_type;
|
||||
re_type.name = MP_QSTR_ure;
|
||||
re_type.print = re_print;
|
||||
re_locals_dict_table[0] = (mp_map_elem_t){ MP_OBJ_NEW_QSTR(MP_QSTR_match), MP_OBJ_FROM_PTR(&re_match_obj) };
|
||||
re_locals_dict_table[1] = (mp_map_elem_t){ MP_OBJ_NEW_QSTR(MP_QSTR_search), MP_OBJ_FROM_PTR(&re_search_obj) };
|
||||
re_locals_dict_table[2] = (mp_map_elem_t){ MP_OBJ_NEW_QSTR(MP_QSTR_split), MP_OBJ_FROM_PTR(&re_split_obj) };
|
||||
re_type.locals_dict = (void*)&re_locals_dict;
|
||||
|
||||
mp_store_global(MP_QSTR_compile, MP_OBJ_FROM_PTR(&mod_re_compile_obj));
|
||||
mp_store_global(MP_QSTR_match, MP_OBJ_FROM_PTR(&re_match_obj));
|
||||
mp_store_global(MP_QSTR_search, MP_OBJ_FROM_PTR(&re_search_obj));
|
||||
|
||||
MP_DYNRUNTIME_INIT_EXIT
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
# Location of top-level MicroPython directory
|
||||
MPY_DIR = ../../..
|
||||
|
||||
# Name of module (different to built-in uzlib so it can coexist)
|
||||
MOD = uzlib_$(ARCH)
|
||||
|
||||
# Source files (.c or .py)
|
||||
SRC = uzlib.c
|
||||
|
||||
# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin)
|
||||
ARCH = x64
|
||||
|
||||
include $(MPY_DIR)/py/dynruntime.mk
|
|
@ -0,0 +1,35 @@
|
|||
#define MICROPY_PY_UZLIB (1)
|
||||
|
||||
#include "py/dynruntime.h"
|
||||
|
||||
#if !defined(__linux__)
|
||||
void *memset(void *s, int c, size_t n) {
|
||||
return mp_fun_table.memset_(s, c, n);
|
||||
}
|
||||
#endif
|
||||
|
||||
mp_obj_type_t decompio_type;
|
||||
|
||||
#include "extmod/moduzlib.c"
|
||||
|
||||
mp_map_elem_t decompio_locals_dict_table[3];
|
||||
STATIC MP_DEFINE_CONST_DICT(decompio_locals_dict, decompio_locals_dict_table);
|
||||
|
||||
mp_obj_t mpy_init(mp_obj_fun_bc_t *self, size_t n_args, size_t n_kw, mp_obj_t *args) {
|
||||
MP_DYNRUNTIME_INIT_ENTRY
|
||||
|
||||
decompio_type.base.type = mp_fun_table.type_type;
|
||||
decompio_type.name = MP_QSTR_DecompIO;
|
||||
decompio_type.make_new = decompio_make_new;
|
||||
decompio_type.protocol = &decompio_stream_p;
|
||||
decompio_locals_dict_table[0] = (mp_map_elem_t){ MP_OBJ_NEW_QSTR(MP_QSTR_read), MP_OBJ_FROM_PTR(&mp_stream_read_obj) };
|
||||
decompio_locals_dict_table[1] = (mp_map_elem_t){ MP_OBJ_NEW_QSTR(MP_QSTR_readinto), MP_OBJ_FROM_PTR(&mp_stream_readinto_obj) };
|
||||
decompio_locals_dict_table[2] = (mp_map_elem_t){ MP_OBJ_NEW_QSTR(MP_QSTR_readline), MP_OBJ_FROM_PTR(&mp_stream_unbuffered_readline_obj) };
|
||||
decompio_type.locals_dict = (void*)&decompio_locals_dict;
|
||||
|
||||
mp_store_global(MP_QSTR___name__, MP_OBJ_NEW_QSTR(MP_QSTR_uzlib));
|
||||
mp_store_global(MP_QSTR_decompress, MP_OBJ_FROM_PTR(&mod_uzlib_decompress_obj));
|
||||
mp_store_global(MP_QSTR_DecompIO, MP_OBJ_FROM_PTR(&decompio_type));
|
||||
|
||||
MP_DYNRUNTIME_INIT_EXIT
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
# Makefile directives for BlueKitchen BTstack
|
||||
|
||||
ifeq ($(MICROPY_BLUETOOTH_BTSTACK),1)
|
||||
|
||||
MICROPY_BLUETOOTH_BTSTACK_USB ?= 0
|
||||
|
||||
BTSTACK_EXTMOD_DIR = extmod/btstack
|
||||
|
||||
EXTMOD_SRC_C += extmod/btstack/modbluetooth_btstack.c
|
||||
|
||||
INC += -I$(TOP)/$(BTSTACK_EXTMOD_DIR)
|
||||
|
||||
CFLAGS_MOD += -DMICROPY_BLUETOOTH_BTSTACK=1
|
||||
|
||||
BTSTACK_DIR = $(TOP)/lib/btstack
|
||||
|
||||
ifneq ($(wildcard $(BTSTACK_DIR)/src),)
|
||||
|
||||
include $(BTSTACK_DIR)/src/Makefile.inc
|
||||
include $(BTSTACK_DIR)/src/ble/Makefile.inc
|
||||
|
||||
INC += -I$(BTSTACK_DIR)/src
|
||||
INC += -I$(BTSTACK_DIR)/3rd-party/bluedroid/decoder/include
|
||||
INC += -I$(BTSTACK_DIR)/3rd-party/bluedroid/encoder/include
|
||||
INC += -I$(BTSTACK_DIR)/3rd-party/md5
|
||||
INC += -I$(BTSTACK_DIR)/3rd-party/yxml
|
||||
|
||||
SRC_BTSTACK = \
|
||||
$(addprefix lib/btstack/src/, $(SRC_FILES)) \
|
||||
$(addprefix lib/btstack/src/ble/, $(filter-out %_tlv.c, $(SRC_BLE_FILES))) \
|
||||
lib/btstack/platform/embedded/btstack_run_loop_embedded.c
|
||||
|
||||
ifeq ($(MICROPY_BLUETOOTH_BTSTACK_USB),1)
|
||||
SRC_BTSTACK += \
|
||||
lib/btstack/platform/libusb/hci_transport_h2_libusb.c
|
||||
|
||||
CFLAGS += $(shell pkg-config libusb-1.0 --cflags)
|
||||
LDFLAGS += $(shell pkg-config libusb-1.0 --libs)
|
||||
endif
|
||||
|
||||
ifeq ($(MICROPY_BLUETOOTH_BTSTACK_ENABLE_CLASSIC),1)
|
||||
include $(BTSTACK_DIR)/src/classic/Makefile.inc
|
||||
SRC_BTSTACK += \
|
||||
$(addprefix lib/btstack/src/classic/, $(SRC_CLASSIC_FILES))
|
||||
endif
|
||||
|
||||
LIB_SRC_C += $(SRC_BTSTACK)
|
||||
|
||||
# Suppress some warnings.
|
||||
BTSTACK_WARNING_CFLAGS = -Wno-old-style-definition -Wno-unused-variable -Wno-unused-parameter
|
||||
ifneq ($(CC),clang)
|
||||
BTSTACK_WARNING_CFLAGS += -Wno-format
|
||||
endif
|
||||
$(BUILD)/lib/btstack/src/%.o: CFLAGS += $(BTSTACK_WARNING_CFLAGS)
|
||||
|
||||
endif
|
||||
endif
|
|
@ -0,0 +1,47 @@
|
|||
#ifndef MICROPY_INCLUDED_EXTMOD_BTSTACK_BTSTACK_CONFIG_H
|
||||
#define MICROPY_INCLUDED_EXTMOD_BTSTACK_BTSTACK_CONFIG_H
|
||||
|
||||
// BTstack features that can be enabled
|
||||
#define ENABLE_BLE
|
||||
#define ENABLE_LE_PERIPHERAL
|
||||
#define ENABLE_LE_CENTRAL
|
||||
// #define ENABLE_CLASSIC
|
||||
#define ENABLE_LE_DATA_CHANNELS
|
||||
// #define ENABLE_LOG_INFO
|
||||
#define ENABLE_LOG_ERROR
|
||||
|
||||
// BTstack configuration. buffers, sizes, ...
|
||||
#define HCI_ACL_PAYLOAD_SIZE 1021
|
||||
#define MAX_NR_GATT_CLIENTS 1
|
||||
#define MAX_NR_HCI_CONNECTIONS 1
|
||||
#define MAX_NR_L2CAP_SERVICES 3
|
||||
#define MAX_NR_L2CAP_CHANNELS 3
|
||||
#define MAX_NR_RFCOMM_MULTIPLEXERS 1
|
||||
#define MAX_NR_RFCOMM_SERVICES 1
|
||||
#define MAX_NR_RFCOMM_CHANNELS 1
|
||||
#define MAX_NR_BTSTACK_LINK_KEY_DB_MEMORY_ENTRIES 2
|
||||
#define MAX_NR_BNEP_SERVICES 1
|
||||
#define MAX_NR_BNEP_CHANNELS 1
|
||||
#define MAX_NR_HFP_CONNECTIONS 1
|
||||
#define MAX_NR_WHITELIST_ENTRIES 1
|
||||
#define MAX_NR_SM_LOOKUP_ENTRIES 3
|
||||
#define MAX_NR_SERVICE_RECORD_ITEMS 1
|
||||
#define MAX_NR_AVDTP_STREAM_ENDPOINTS 1
|
||||
#define MAX_NR_AVDTP_CONNECTIONS 1
|
||||
#define MAX_NR_AVRCP_CONNECTIONS 1
|
||||
|
||||
#define MAX_NR_LE_DEVICE_DB_ENTRIES 4
|
||||
|
||||
// Link Key DB and LE Device DB using TLV on top of Flash Sector interface
|
||||
// #define NVM_NUM_DEVICE_DB_ENTRIES 16
|
||||
|
||||
// We don't give btstack a malloc, so use a fixed-size ATT DB.
|
||||
#define MAX_ATT_DB_SIZE 512
|
||||
|
||||
// BTstack HAL configuration
|
||||
#define HAVE_EMBEDDED_TIME_MS
|
||||
|
||||
// Some USB dongles take longer to respond to HCI reset (e.g. BCM20702A).
|
||||
#define HCI_RESET_RESEND_TIMEOUT_MS 1000
|
||||
|
||||
#endif // MICROPY_INCLUDED_EXTMOD_BTSTACK_BTSTACK_CONFIG_H
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2020 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.
|
||||
*/
|
||||
|
||||
#ifndef MICROPY_INCLUDED_EXTMOD_BTSTACK_MODBLUETOOTH_BTSTACK_H
|
||||
#define MICROPY_INCLUDED_EXTMOD_BTSTACK_MODBLUETOOTH_BTSTACK_H
|
||||
|
||||
#if MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_BTSTACK
|
||||
|
||||
#include "extmod/modbluetooth.h"
|
||||
|
||||
#include "lib/btstack/src/btstack.h"
|
||||
|
||||
typedef struct _mp_btstack_pending_op_t mp_btstack_pending_op_t;
|
||||
|
||||
typedef struct _mp_bluetooth_btstack_root_pointers_t {
|
||||
// This stores both the advertising data and the scan response data, concatenated together.
|
||||
uint8_t *adv_data;
|
||||
// Total length of both.
|
||||
size_t adv_data_alloc;
|
||||
|
||||
// Characteristic (and descriptor) value storage.
|
||||
mp_gatts_db_t gatts_db;
|
||||
|
||||
btstack_linked_list_t pending_ops;
|
||||
|
||||
#if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE
|
||||
// Registration for notify/indicate events.
|
||||
gatt_client_notification_t notification;
|
||||
#endif
|
||||
} mp_bluetooth_btstack_root_pointers_t;
|
||||
|
||||
enum {
|
||||
MP_BLUETOOTH_BTSTACK_STATE_OFF,
|
||||
MP_BLUETOOTH_BTSTACK_STATE_STARTING,
|
||||
MP_BLUETOOTH_BTSTACK_STATE_ACTIVE,
|
||||
MP_BLUETOOTH_BTSTACK_STATE_TIMEOUT,
|
||||
};
|
||||
|
||||
extern volatile int mp_bluetooth_btstack_state;
|
||||
|
||||
void mp_bluetooth_btstack_port_init(void);
|
||||
void mp_bluetooth_btstack_port_deinit(void);
|
||||
void mp_bluetooth_btstack_port_start(void);
|
||||
|
||||
#endif // MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_BTSTACK
|
||||
|
||||
#endif // MICROPY_INCLUDED_EXTMOD_BTSTACK_MODBLUETOOTH_BTSTACK_H
|
|
@ -1,7 +1,8 @@
|
|||
/*********************************************************************
|
||||
* Source: https://github.com/B-Con/crypto-algorithms
|
||||
* Filename: sha256.c
|
||||
* Author: Brad Conte (brad AT bradconte.com)
|
||||
* Copyright:
|
||||
* Copyright: This code is released into the public domain.
|
||||
* Disclaimer: This code is presented "as is" without any guarantees.
|
||||
* Details: Implementation of the SHA-256 hashing algorithm.
|
||||
SHA-256 is one of the three algorithms in the SHA2
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
/*********************************************************************
|
||||
* Source: https://github.com/B-Con/crypto-algorithms
|
||||
* Filename: sha256.h
|
||||
* Author: Brad Conte (brad AT bradconte.com)
|
||||
* Copyright:
|
||||
* Copyright: This code is released into the public domain.
|
||||
* Disclaimer: This code is presented "as is" without any guarantees.
|
||||
* Details: Defines the API for the corresponding SHA1 implementation.
|
||||
*********************************************************************/
|
||||
|
|
|
@ -0,0 +1,232 @@
|
|||
# This makefile fragment provides rules to build 3rd-party components for extmod modules
|
||||
|
||||
################################################################################
|
||||
# VFS FAT FS
|
||||
|
||||
OOFATFS_DIR = lib/oofatfs
|
||||
|
||||
# this sets the config file for FatFs
|
||||
CFLAGS_MOD += -DFFCONF_H=\"$(OOFATFS_DIR)/ffconf.h\"
|
||||
|
||||
ifeq ($(MICROPY_VFS_FAT),1)
|
||||
CFLAGS_MOD += -DMICROPY_VFS_FAT=1
|
||||
SRC_MOD += $(addprefix $(OOFATFS_DIR)/,\
|
||||
ff.c \
|
||||
ffunicode.c \
|
||||
)
|
||||
endif
|
||||
|
||||
################################################################################
|
||||
# VFS littlefs
|
||||
|
||||
LITTLEFS_DIR = lib/littlefs
|
||||
|
||||
ifeq ($(MICROPY_VFS_LFS1),1)
|
||||
CFLAGS_MOD += -DMICROPY_VFS_LFS1=1
|
||||
CFLAGS_MOD += -DLFS1_NO_MALLOC -DLFS1_NO_DEBUG -DLFS1_NO_WARN -DLFS1_NO_ERROR -DLFS1_NO_ASSERT
|
||||
SRC_MOD += $(addprefix $(LITTLEFS_DIR)/,\
|
||||
lfs1.c \
|
||||
lfs1_util.c \
|
||||
)
|
||||
else
|
||||
CFLAGS_MOD += -DMICROPY_VFS_LFS1=0
|
||||
endif
|
||||
|
||||
ifeq ($(MICROPY_VFS_LFS2),1)
|
||||
CFLAGS_MOD += -DMICROPY_VFS_LFS2=1
|
||||
CFLAGS_MOD += -DLFS2_NO_MALLOC -DLFS2_NO_DEBUG -DLFS2_NO_WARN -DLFS2_NO_ERROR -DLFS2_NO_ASSERT
|
||||
SRC_MOD += $(addprefix $(LITTLEFS_DIR)/,\
|
||||
lfs2.c \
|
||||
lfs2_util.c \
|
||||
)
|
||||
else
|
||||
CFLAGS_MOD += -DMICROPY_VFS_LFS2=0
|
||||
endif
|
||||
|
||||
################################################################################
|
||||
# ussl
|
||||
|
||||
ifeq ($(MICROPY_PY_USSL),1)
|
||||
CFLAGS_MOD += -DMICROPY_PY_USSL=1
|
||||
ifeq ($(MICROPY_SSL_AXTLS),1)
|
||||
CFLAGS_MOD += -DMICROPY_SSL_AXTLS=1 -I$(TOP)/lib/axtls/ssl -I$(TOP)/lib/axtls/crypto -I$(TOP)/extmod/axtls-include
|
||||
AXTLS_DIR = lib/axtls
|
||||
$(BUILD)/$(AXTLS_DIR)/%.o: CFLAGS += -Wno-all -Wno-unused-parameter -Wno-uninitialized -Wno-sign-compare -Wno-old-style-definition -Dmp_stream_errno=errno $(AXTLS_DEFS_EXTRA)
|
||||
SRC_MOD += $(addprefix $(AXTLS_DIR)/,\
|
||||
ssl/asn1.c \
|
||||
ssl/loader.c \
|
||||
ssl/tls1.c \
|
||||
ssl/tls1_svr.c \
|
||||
ssl/tls1_clnt.c \
|
||||
ssl/x509.c \
|
||||
crypto/aes.c \
|
||||
crypto/bigint.c \
|
||||
crypto/crypto_misc.c \
|
||||
crypto/hmac.c \
|
||||
crypto/md5.c \
|
||||
crypto/rsa.c \
|
||||
crypto/sha1.c \
|
||||
)
|
||||
else ifeq ($(MICROPY_SSL_MBEDTLS),1)
|
||||
MBEDTLS_DIR = lib/mbedtls
|
||||
CFLAGS_MOD += -DMICROPY_SSL_MBEDTLS=1 -I$(TOP)/$(MBEDTLS_DIR)/include
|
||||
SRC_MOD += $(addprefix $(MBEDTLS_DIR)/library/,\
|
||||
aes.c \
|
||||
aesni.c \
|
||||
arc4.c \
|
||||
asn1parse.c \
|
||||
asn1write.c \
|
||||
base64.c \
|
||||
bignum.c \
|
||||
blowfish.c \
|
||||
camellia.c \
|
||||
ccm.c \
|
||||
certs.c \
|
||||
chacha20.c \
|
||||
chachapoly.c \
|
||||
cipher.c \
|
||||
cipher_wrap.c \
|
||||
cmac.c \
|
||||
ctr_drbg.c \
|
||||
debug.c \
|
||||
des.c \
|
||||
dhm.c \
|
||||
ecdh.c \
|
||||
ecdsa.c \
|
||||
ecjpake.c \
|
||||
ecp.c \
|
||||
ecp_curves.c \
|
||||
entropy.c \
|
||||
entropy_poll.c \
|
||||
error.c \
|
||||
gcm.c \
|
||||
havege.c \
|
||||
hmac_drbg.c \
|
||||
md2.c \
|
||||
md4.c \
|
||||
md5.c \
|
||||
md.c \
|
||||
md_wrap.c \
|
||||
oid.c \
|
||||
padlock.c \
|
||||
pem.c \
|
||||
pk.c \
|
||||
pkcs11.c \
|
||||
pkcs12.c \
|
||||
pkcs5.c \
|
||||
pkparse.c \
|
||||
pk_wrap.c \
|
||||
pkwrite.c \
|
||||
platform.c \
|
||||
platform_util.c \
|
||||
poly1305.c \
|
||||
ripemd160.c \
|
||||
rsa.c \
|
||||
rsa_internal.c \
|
||||
sha1.c \
|
||||
sha256.c \
|
||||
sha512.c \
|
||||
ssl_cache.c \
|
||||
ssl_ciphersuites.c \
|
||||
ssl_cli.c \
|
||||
ssl_cookie.c \
|
||||
ssl_srv.c \
|
||||
ssl_ticket.c \
|
||||
ssl_tls.c \
|
||||
timing.c \
|
||||
x509.c \
|
||||
x509_create.c \
|
||||
x509_crl.c \
|
||||
x509_crt.c \
|
||||
x509_csr.c \
|
||||
x509write_crt.c \
|
||||
x509write_csr.c \
|
||||
xtea.c \
|
||||
)
|
||||
endif
|
||||
endif
|
||||
|
||||
################################################################################
|
||||
# lwip
|
||||
|
||||
ifeq ($(MICROPY_PY_LWIP),1)
|
||||
# A port should add an include path where lwipopts.h can be found (eg extmod/lwip-include)
|
||||
LWIP_DIR = lib/lwip/src
|
||||
INC += -I$(TOP)/$(LWIP_DIR)/include
|
||||
CFLAGS_MOD += -DMICROPY_PY_LWIP=1
|
||||
$(BUILD)/$(LWIP_DIR)/core/ipv4/dhcp.o: CFLAGS_MOD += -Wno-address
|
||||
SRC_MOD += extmod/modlwip.c lib/netutils/netutils.c
|
||||
SRC_MOD += $(addprefix $(LWIP_DIR)/,\
|
||||
apps/mdns/mdns.c \
|
||||
core/def.c \
|
||||
core/dns.c \
|
||||
core/inet_chksum.c \
|
||||
core/init.c \
|
||||
core/ip.c \
|
||||
core/mem.c \
|
||||
core/memp.c \
|
||||
core/netif.c \
|
||||
core/pbuf.c \
|
||||
core/raw.c \
|
||||
core/stats.c \
|
||||
core/sys.c \
|
||||
core/tcp.c \
|
||||
core/tcp_in.c \
|
||||
core/tcp_out.c \
|
||||
core/timeouts.c \
|
||||
core/udp.c \
|
||||
core/ipv4/autoip.c \
|
||||
core/ipv4/dhcp.c \
|
||||
core/ipv4/etharp.c \
|
||||
core/ipv4/icmp.c \
|
||||
core/ipv4/igmp.c \
|
||||
core/ipv4/ip4_addr.c \
|
||||
core/ipv4/ip4.c \
|
||||
core/ipv4/ip4_frag.c \
|
||||
core/ipv6/dhcp6.c \
|
||||
core/ipv6/ethip6.c \
|
||||
core/ipv6/icmp6.c \
|
||||
core/ipv6/inet6.c \
|
||||
core/ipv6/ip6_addr.c \
|
||||
core/ipv6/ip6.c \
|
||||
core/ipv6/ip6_frag.c \
|
||||
core/ipv6/mld6.c \
|
||||
core/ipv6/nd6.c \
|
||||
netif/ethernet.c \
|
||||
)
|
||||
ifeq ($(MICROPY_PY_LWIP_SLIP),1)
|
||||
CFLAGS_MOD += -DMICROPY_PY_LWIP_SLIP=1
|
||||
SRC_MOD += $(LWIP_DIR)/netif/slipif.c
|
||||
endif
|
||||
endif
|
||||
|
||||
################################################################################
|
||||
# btree
|
||||
|
||||
ifeq ($(MICROPY_PY_BTREE),1)
|
||||
BTREE_DIR = lib/berkeley-db-1.xx
|
||||
BTREE_DEFS = -D__DBINTERFACE_PRIVATE=1 -Dmpool_error=printf -Dabort=abort_ "-Dvirt_fd_t=void*" $(BTREE_DEFS_EXTRA)
|
||||
INC += -I$(TOP)/$(BTREE_DIR)/PORT/include
|
||||
SRC_MOD += extmod/modbtree.c
|
||||
SRC_MOD += $(addprefix $(BTREE_DIR)/,\
|
||||
btree/bt_close.c \
|
||||
btree/bt_conv.c \
|
||||
btree/bt_debug.c \
|
||||
btree/bt_delete.c \
|
||||
btree/bt_get.c \
|
||||
btree/bt_open.c \
|
||||
btree/bt_overflow.c \
|
||||
btree/bt_page.c \
|
||||
btree/bt_put.c \
|
||||
btree/bt_search.c \
|
||||
btree/bt_seq.c \
|
||||
btree/bt_split.c \
|
||||
btree/bt_utils.c \
|
||||
mpool/mpool.c \
|
||||
)
|
||||
CFLAGS_MOD += -DMICROPY_PY_BTREE=1
|
||||
# we need to suppress certain warnings to get berkeley-db to compile cleanly
|
||||
# and we have separate BTREE_DEFS so the definitions don't interfere with other source code
|
||||
$(BUILD)/$(BTREE_DIR)/%.o: CFLAGS += -Wno-old-style-definition -Wno-sign-compare -Wno-unused-parameter $(BTREE_DEFS)
|
||||
$(BUILD)/extmod/modbtree.o: CFLAGS += $(BTREE_DEFS)
|
||||
endif
|
|
@ -1,26 +0,0 @@
|
|||
// Copyright (c) 2016 Paul Sokolovsky
|
||||
// SPDX-FileCopyrightText: 2014 MicroPython & CircuitPython contributors (https://github.com/adafruit/circuitpython/graphs/contributors)
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2014-2016 Damien P. George
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#ifndef MICROPY_INCLUDED_EXTMOD_MISC_H
|
||||
#define MICROPY_INCLUDED_EXTMOD_MISC_H
|
||||
|
||||
// This file contains cumulative declarations for extmod/ .
|
||||
|
||||
#include <stddef.h>
|
||||
#include "py/runtime.h"
|
||||
|
||||
MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_uos_dupterm_obj);
|
||||
|
||||
#if MICROPY_PY_OS_DUPTERM
|
||||
bool mp_uos_dupterm_is_builtin_stream(mp_const_obj_t stream);
|
||||
int mp_uos_dupterm_rx_chr(void);
|
||||
void mp_uos_dupterm_tx_strn(const char *str, size_t len);
|
||||
void mp_uos_deactivate(size_t dupterm_idx, const char *msg, mp_obj_t exc);
|
||||
#else
|
||||
#define mp_uos_dupterm_tx_strn(s, l)
|
||||
#endif
|
||||
|
||||
#endif // MICROPY_INCLUDED_EXTMOD_MISC_H
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
typedef struct _mp_obj_btree_t {
|
||||
mp_obj_base_t base;
|
||||
mp_obj_t stream; // retain a reference to prevent GC from reclaiming it
|
||||
DB *db;
|
||||
mp_obj_t start_key;
|
||||
mp_obj_t end_key;
|
||||
|
@ -31,7 +32,9 @@ typedef struct _mp_obj_btree_t {
|
|||
byte next_flags;
|
||||
} mp_obj_btree_t;
|
||||
|
||||
#if !MICROPY_ENABLE_DYNRUNTIME
|
||||
STATIC const mp_obj_type_t btree_type;
|
||||
#endif
|
||||
|
||||
#define CHECK_ERROR(res) \
|
||||
if (res == RET_ERROR) { \
|
||||
|
@ -39,12 +42,13 @@ STATIC const mp_obj_type_t btree_type;
|
|||
}
|
||||
|
||||
void __dbpanic(DB *db) {
|
||||
printf("__dbpanic(%p)\n", db);
|
||||
mp_printf(&mp_plat_print, "__dbpanic(%p)\n", db);
|
||||
}
|
||||
|
||||
STATIC mp_obj_btree_t *btree_new(DB *db) {
|
||||
STATIC mp_obj_btree_t *btree_new(DB *db, mp_obj_t stream) {
|
||||
mp_obj_btree_t *o = m_new_obj(mp_obj_btree_t);
|
||||
o->base.type = &btree_type;
|
||||
o->stream = stream;
|
||||
o->db = db;
|
||||
o->start_key = mp_const_none;
|
||||
o->end_key = mp_const_none;
|
||||
|
@ -226,14 +230,14 @@ STATIC mp_obj_t btree_iternext(mp_obj_t self_in) {
|
|||
}
|
||||
|
||||
STATIC mp_obj_t btree_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) {
|
||||
mp_obj_btree_t *self = mp_instance_cast_to_native_base(self_in, &btree_type);
|
||||
mp_obj_btree_t *self = mp_obj_cast_to_native_base(self_in, &btree_type);
|
||||
if (value == MP_OBJ_NULL) {
|
||||
// delete
|
||||
DBT key;
|
||||
key.data = (void *)mp_obj_str_get_data(index, &key.size);
|
||||
int res = __bt_delete(self->db, &key, 0);
|
||||
if (res == RET_SPECIAL) {
|
||||
nlr_raise(mp_obj_new_exception(&mp_type_KeyError));
|
||||
mp_raise_type(&mp_type_KeyError);
|
||||
}
|
||||
CHECK_ERROR(res);
|
||||
return mp_const_none;
|
||||
|
@ -243,7 +247,7 @@ STATIC mp_obj_t btree_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) {
|
|||
key.data = (void *)mp_obj_str_get_data(index, &key.size);
|
||||
int res = __bt_get(self->db, &key, &val, 0);
|
||||
if (res == RET_SPECIAL) {
|
||||
nlr_raise(mp_obj_new_exception(&mp_type_KeyError));
|
||||
mp_raise_type(&mp_type_KeyError);
|
||||
}
|
||||
CHECK_ERROR(res);
|
||||
return mp_obj_new_bytes(val.data, val.size);
|
||||
|
@ -274,6 +278,7 @@ STATIC mp_obj_t btree_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs
|
|||
}
|
||||
}
|
||||
|
||||
#if !MICROPY_ENABLE_DYNRUNTIME
|
||||
STATIC const mp_rom_map_elem_t btree_locals_dict_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&btree_close_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_flush), MP_ROM_PTR(&btree_flush_obj) },
|
||||
|
@ -298,14 +303,16 @@ STATIC const mp_obj_type_t btree_type = {
|
|||
.subscr = btree_subscr,
|
||||
.locals_dict = (void *)&btree_locals_dict,
|
||||
};
|
||||
#endif
|
||||
|
||||
STATIC FILEVTABLE btree_stream_fvtable = {
|
||||
STATIC const FILEVTABLE btree_stream_fvtable = {
|
||||
mp_stream_posix_read,
|
||||
mp_stream_posix_write,
|
||||
mp_stream_posix_lseek,
|
||||
mp_stream_posix_fsync
|
||||
};
|
||||
|
||||
#if !MICROPY_ENABLE_DYNRUNTIME
|
||||
STATIC mp_obj_t mod_btree_open(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_flags, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
|
||||
|
@ -335,7 +342,7 @@ STATIC mp_obj_t mod_btree_open(size_t n_args, const mp_obj_t *pos_args, mp_map_t
|
|||
if (db == NULL) {
|
||||
mp_raise_OSError(errno);
|
||||
}
|
||||
return MP_OBJ_FROM_PTR(btree_new(db));
|
||||
return MP_OBJ_FROM_PTR(btree_new(db, pos_args[0]));
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(mod_btree_open_obj, 1, mod_btree_open);
|
||||
|
||||
|
@ -352,5 +359,6 @@ const mp_obj_module_t mp_module_btree = {
|
|||
.base = { &mp_type_module },
|
||||
.globals = (mp_obj_dict_t *)&mp_module_btree_globals,
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif // MICROPY_PY_BTREE
|
||||
|
|
|
@ -22,12 +22,15 @@ typedef struct _mp_obj_framebuf_t {
|
|||
uint8_t format;
|
||||
} mp_obj_framebuf_t;
|
||||
|
||||
#if !MICROPY_ENABLE_DYNRUNTIME
|
||||
STATIC const mp_obj_type_t mp_type_framebuf;
|
||||
#endif
|
||||
|
||||
typedef void (*setpixel_t)(const mp_obj_framebuf_t *, int, int, uint32_t);
|
||||
typedef uint32_t (*getpixel_t)(const mp_obj_framebuf_t *, int, int);
|
||||
typedef void (*fill_rect_t)(const mp_obj_framebuf_t *, int, int, int, int, uint32_t);
|
||||
|
||||
typedef struct _mp_framebuf_p_t {
|
||||
MP_PROTOCOL_HEAD
|
||||
setpixel_t setpixel;
|
||||
getpixel_t getpixel;
|
||||
fill_rect_t fill_rect;
|
||||
|
@ -208,14 +211,14 @@ STATIC void gs8_fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, int
|
|||
}
|
||||
}
|
||||
|
||||
STATIC mp_framebuf_p_t formats[] = {
|
||||
[FRAMEBUF_MVLSB] = {MP_PROTO_IMPLEMENT(MP_QSTR_protocol_framebuf) mvlsb_setpixel, mvlsb_getpixel, mvlsb_fill_rect},
|
||||
[FRAMEBUF_RGB565] = {MP_PROTO_IMPLEMENT(MP_QSTR_protocol_framebuf) rgb565_setpixel, rgb565_getpixel, rgb565_fill_rect},
|
||||
[FRAMEBUF_GS2_HMSB] = {MP_PROTO_IMPLEMENT(MP_QSTR_protocol_framebuf) gs2_hmsb_setpixel, gs2_hmsb_getpixel, gs2_hmsb_fill_rect},
|
||||
[FRAMEBUF_GS4_HMSB] = {MP_PROTO_IMPLEMENT(MP_QSTR_protocol_framebuf) gs4_hmsb_setpixel, gs4_hmsb_getpixel, gs4_hmsb_fill_rect},
|
||||
[FRAMEBUF_GS8] = {MP_PROTO_IMPLEMENT(MP_QSTR_protocol_framebuf) gs8_setpixel, gs8_getpixel, gs8_fill_rect},
|
||||
[FRAMEBUF_MHLSB] = {MP_PROTO_IMPLEMENT(MP_QSTR_protocol_framebuf) mono_horiz_setpixel, mono_horiz_getpixel, mono_horiz_fill_rect},
|
||||
[FRAMEBUF_MHMSB] = {MP_PROTO_IMPLEMENT(MP_QSTR_protocol_framebuf) mono_horiz_setpixel, mono_horiz_getpixel, mono_horiz_fill_rect},
|
||||
STATIC const mp_framebuf_p_t formats[] = {
|
||||
[FRAMEBUF_MVLSB] = {mvlsb_setpixel, mvlsb_getpixel, mvlsb_fill_rect},
|
||||
[FRAMEBUF_RGB565] = {rgb565_setpixel, rgb565_getpixel, rgb565_fill_rect},
|
||||
[FRAMEBUF_GS2_HMSB] = {gs2_hmsb_setpixel, gs2_hmsb_getpixel, gs2_hmsb_fill_rect},
|
||||
[FRAMEBUF_GS4_HMSB] = {gs4_hmsb_setpixel, gs4_hmsb_getpixel, gs4_hmsb_fill_rect},
|
||||
[FRAMEBUF_GS8] = {gs8_setpixel, gs8_getpixel, gs8_fill_rect},
|
||||
[FRAMEBUF_MHLSB] = {mono_horiz_setpixel, mono_horiz_getpixel, mono_horiz_fill_rect},
|
||||
[FRAMEBUF_MHMSB] = {mono_horiz_setpixel, mono_horiz_getpixel, mono_horiz_fill_rect},
|
||||
};
|
||||
|
||||
static inline void setpixel(const mp_obj_framebuf_t *fb, int x, int y, uint32_t col) {
|
||||
|
@ -278,18 +281,23 @@ STATIC mp_obj_t framebuf_make_new(const mp_obj_type_t *type, size_t n_args, cons
|
|||
case FRAMEBUF_GS8:
|
||||
break;
|
||||
default:
|
||||
mp_raise_ValueError(translate("invalid format"));
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("invalid format"));
|
||||
}
|
||||
|
||||
return MP_OBJ_FROM_PTR(o);
|
||||
}
|
||||
|
||||
#if !(defined(MICROPY_ENABLE_DYNRUNTIME) && MICROPY_ENABLE_DYNRUNTIME)
|
||||
STATIC const mp_obj_type_t mp_type_framebuf;
|
||||
#endif
|
||||
|
||||
// Helper to ensure we have the native super class instead of a subclass.
|
||||
static mp_obj_framebuf_t *native_framebuf(mp_obj_t framebuf_obj) {
|
||||
mp_obj_t native_framebuf = mp_instance_cast_to_native_base(framebuf_obj, &mp_type_framebuf);
|
||||
mp_obj_t native_framebuf = mp_obj_cast_to_native_base(framebuf_obj, &mp_type_framebuf);
|
||||
mp_obj_assert_native_inited(native_framebuf);
|
||||
if (native_framebuf == MP_OBJ_NULL) {
|
||||
mp_raise_TypeError(NULL);
|
||||
}
|
||||
return MP_OBJ_TO_PTR(native_framebuf);
|
||||
}
|
||||
|
||||
|
@ -577,6 +585,7 @@ STATIC mp_obj_t framebuf_text(size_t n_args, const mp_obj_t *args) {
|
|||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_text_obj, 4, 5, framebuf_text);
|
||||
|
||||
#if !MICROPY_ENABLE_DYNRUNTIME
|
||||
STATIC const mp_rom_map_elem_t framebuf_locals_dict_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR_fill), MP_ROM_PTR(&framebuf_fill_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_fill_rect), MP_ROM_PTR(&framebuf_fill_rect_obj) },
|
||||
|
@ -598,6 +607,7 @@ STATIC const mp_obj_type_t mp_type_framebuf = {
|
|||
.buffer_p = { .get_buffer = framebuf_get_buffer },
|
||||
.locals_dict = (mp_obj_dict_t *)&framebuf_locals_dict,
|
||||
};
|
||||
#endif
|
||||
|
||||
// this factory function is provided for backwards compatibility with old FrameBuffer1 class
|
||||
STATIC mp_obj_t legacy_framebuffer1(size_t n_args, const mp_obj_t *args) {
|
||||
|
@ -621,6 +631,7 @@ STATIC mp_obj_t legacy_framebuffer1(size_t n_args, const mp_obj_t *args) {
|
|||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(legacy_framebuffer1_obj, 3, 4, legacy_framebuffer1);
|
||||
|
||||
#if !MICROPY_ENABLE_DYNRUNTIME
|
||||
STATIC const mp_rom_map_elem_t framebuf_module_globals_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_framebuf) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_FrameBuffer), MP_ROM_PTR(&mp_type_framebuf) },
|
||||
|
@ -641,5 +652,6 @@ const mp_obj_module_t mp_module_framebuf = {
|
|||
.base = { &mp_type_module },
|
||||
.globals = (mp_obj_dict_t *)&framebuf_module_globals,
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif // MICROPY_PY_FRAMEBUF
|
||||
|
|
|
@ -0,0 +1,294 @@
|
|||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2020 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/runtime.h"
|
||||
#include "py/smallint.h"
|
||||
#include "py/pairheap.h"
|
||||
#include "py/mphal.h"
|
||||
|
||||
#if MICROPY_PY_UASYNCIO
|
||||
|
||||
typedef struct _mp_obj_task_t {
|
||||
mp_pairheap_t pairheap;
|
||||
mp_obj_t coro;
|
||||
mp_obj_t data;
|
||||
mp_obj_t waiting;
|
||||
|
||||
mp_obj_t ph_key;
|
||||
} mp_obj_task_t;
|
||||
|
||||
typedef struct _mp_obj_task_queue_t {
|
||||
mp_obj_base_t base;
|
||||
mp_obj_task_t *heap;
|
||||
} mp_obj_task_queue_t;
|
||||
|
||||
STATIC const mp_obj_type_t task_queue_type;
|
||||
STATIC const mp_obj_type_t task_type;
|
||||
|
||||
STATIC mp_obj_t task_queue_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *args, mp_map_t *kw_args);
|
||||
|
||||
/******************************************************************************/
|
||||
// Ticks for task ordering in pairing heap
|
||||
|
||||
STATIC mp_obj_t ticks(void) {
|
||||
return MP_OBJ_NEW_SMALL_INT(mp_hal_ticks_ms() & (MICROPY_PY_UTIME_TICKS_PERIOD - 1));
|
||||
}
|
||||
|
||||
STATIC mp_int_t ticks_diff(mp_obj_t t1_in, mp_obj_t t0_in) {
|
||||
mp_uint_t t0 = MP_OBJ_SMALL_INT_VALUE(t0_in);
|
||||
mp_uint_t t1 = MP_OBJ_SMALL_INT_VALUE(t1_in);
|
||||
mp_int_t diff = ((t1 - t0 + MICROPY_PY_UTIME_TICKS_PERIOD / 2) & (MICROPY_PY_UTIME_TICKS_PERIOD - 1))
|
||||
- MICROPY_PY_UTIME_TICKS_PERIOD / 2;
|
||||
return diff;
|
||||
}
|
||||
|
||||
STATIC int task_lt(mp_pairheap_t *n1, mp_pairheap_t *n2) {
|
||||
mp_obj_task_t *t1 = (mp_obj_task_t *)n1;
|
||||
mp_obj_task_t *t2 = (mp_obj_task_t *)n2;
|
||||
return MP_OBJ_SMALL_INT_VALUE(ticks_diff(t1->ph_key, t2->ph_key)) < 0;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
// TaskQueue class
|
||||
|
||||
STATIC mp_obj_t task_queue_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) {
|
||||
(void)args;
|
||||
mp_arg_check_num(n_args, kw_args, 0, 0, false);
|
||||
mp_obj_task_queue_t *self = m_new_obj(mp_obj_task_queue_t);
|
||||
self->base.type = type;
|
||||
self->heap = (mp_obj_task_t *)mp_pairheap_new(task_lt);
|
||||
return MP_OBJ_FROM_PTR(self);
|
||||
}
|
||||
|
||||
STATIC mp_obj_t task_queue_peek(mp_obj_t self_in) {
|
||||
mp_obj_task_queue_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
if (self->heap == NULL) {
|
||||
return mp_const_none;
|
||||
} else {
|
||||
return MP_OBJ_FROM_PTR(self->heap);
|
||||
}
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(task_queue_peek_obj, task_queue_peek);
|
||||
|
||||
STATIC mp_obj_t task_queue_push_sorted(size_t n_args, const mp_obj_t *args) {
|
||||
mp_obj_task_queue_t *self = MP_OBJ_TO_PTR(args[0]);
|
||||
mp_obj_task_t *task = MP_OBJ_TO_PTR(args[1]);
|
||||
task->data = mp_const_none;
|
||||
if (n_args == 2) {
|
||||
task->ph_key = ticks();
|
||||
} else {
|
||||
assert(mp_obj_is_small_int(args[2]));
|
||||
task->ph_key = args[2];
|
||||
}
|
||||
self->heap = (mp_obj_task_t *)mp_pairheap_push(task_lt, &self->heap->pairheap, &task->pairheap);
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(task_queue_push_sorted_obj, 2, 3, task_queue_push_sorted);
|
||||
|
||||
STATIC mp_obj_t task_queue_pop_head(mp_obj_t self_in) {
|
||||
mp_obj_task_queue_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
mp_obj_task_t *head = (mp_obj_task_t *)mp_pairheap_peek(task_lt, &self->heap->pairheap);
|
||||
if (head == NULL) {
|
||||
mp_raise_msg(&mp_type_IndexError, MP_ERROR_TEXT("empty heap"));
|
||||
}
|
||||
self->heap = (mp_obj_task_t *)mp_pairheap_pop(task_lt, &self->heap->pairheap);
|
||||
return MP_OBJ_FROM_PTR(head);
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(task_queue_pop_head_obj, task_queue_pop_head);
|
||||
|
||||
STATIC mp_obj_t task_queue_remove(mp_obj_t self_in, mp_obj_t task_in) {
|
||||
mp_obj_task_queue_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
mp_obj_task_t *task = MP_OBJ_TO_PTR(task_in);
|
||||
self->heap = (mp_obj_task_t *)mp_pairheap_delete(task_lt, &self->heap->pairheap, &task->pairheap);
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_2(task_queue_remove_obj, task_queue_remove);
|
||||
|
||||
STATIC const mp_rom_map_elem_t task_queue_locals_dict_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR_peek), MP_ROM_PTR(&task_queue_peek_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_push_sorted), MP_ROM_PTR(&task_queue_push_sorted_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_push_head), MP_ROM_PTR(&task_queue_push_sorted_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_pop_head), MP_ROM_PTR(&task_queue_pop_head_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_remove), MP_ROM_PTR(&task_queue_remove_obj) },
|
||||
};
|
||||
STATIC MP_DEFINE_CONST_DICT(task_queue_locals_dict, task_queue_locals_dict_table);
|
||||
|
||||
STATIC const mp_obj_type_t task_queue_type = {
|
||||
{ &mp_type_type },
|
||||
.name = MP_QSTR_TaskQueue,
|
||||
.make_new = task_queue_make_new,
|
||||
.locals_dict = (mp_obj_dict_t *)&task_queue_locals_dict,
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
// Task class
|
||||
|
||||
// This is the core uasyncio context with cur_task, _task_queue and CancelledError.
|
||||
STATIC mp_obj_t uasyncio_context = MP_OBJ_NULL;
|
||||
|
||||
STATIC mp_obj_t task_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) {
|
||||
mp_arg_check_num(n_args, kw_args, 1, 2, false);
|
||||
mp_obj_task_t *self = m_new_obj(mp_obj_task_t);
|
||||
self->pairheap.base.type = type;
|
||||
mp_pairheap_init_node(task_lt, &self->pairheap);
|
||||
self->coro = args[0];
|
||||
self->data = mp_const_none;
|
||||
self->waiting = mp_const_none;
|
||||
self->ph_key = MP_OBJ_NEW_SMALL_INT(0);
|
||||
if (n_args == 2) {
|
||||
uasyncio_context = args[1];
|
||||
}
|
||||
return MP_OBJ_FROM_PTR(self);
|
||||
}
|
||||
|
||||
STATIC mp_obj_t task_cancel(mp_obj_t self_in) {
|
||||
mp_obj_task_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
// Check if task is already finished.
|
||||
if (self->coro == mp_const_none) {
|
||||
return mp_const_false;
|
||||
}
|
||||
// Can't cancel self (not supported yet).
|
||||
mp_obj_t cur_task = mp_obj_dict_get(uasyncio_context, MP_OBJ_NEW_QSTR(MP_QSTR_cur_task));
|
||||
if (self_in == cur_task) {
|
||||
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("can't cancel self"));
|
||||
}
|
||||
// If Task waits on another task then forward the cancel to the one it's waiting on.
|
||||
while (mp_obj_is_subclass_fast(MP_OBJ_FROM_PTR(mp_obj_get_type(self->data)), MP_OBJ_FROM_PTR(&task_type))) {
|
||||
self = MP_OBJ_TO_PTR(self->data);
|
||||
}
|
||||
|
||||
mp_obj_t _task_queue = mp_obj_dict_get(uasyncio_context, MP_OBJ_NEW_QSTR(MP_QSTR__task_queue));
|
||||
|
||||
// Reschedule Task as a cancelled task.
|
||||
mp_obj_t dest[3];
|
||||
mp_load_method_maybe(self->data, MP_QSTR_remove, dest);
|
||||
if (dest[0] != MP_OBJ_NULL) {
|
||||
// Not on the main running queue, remove the task from the queue it's on.
|
||||
dest[2] = MP_OBJ_FROM_PTR(self);
|
||||
mp_call_method_n_kw(1, 0, dest);
|
||||
// _task_queue.push_head(self)
|
||||
dest[0] = _task_queue;
|
||||
dest[1] = MP_OBJ_FROM_PTR(self);
|
||||
task_queue_push_sorted(2, dest);
|
||||
} else if (ticks_diff(self->ph_key, ticks()) > 0) {
|
||||
// On the main running queue but scheduled in the future, so bring it forward to now.
|
||||
// _task_queue.remove(self)
|
||||
task_queue_remove(_task_queue, MP_OBJ_FROM_PTR(self));
|
||||
// _task_queue.push_head(self)
|
||||
dest[0] = _task_queue;
|
||||
dest[1] = MP_OBJ_FROM_PTR(self);
|
||||
task_queue_push_sorted(2, dest);
|
||||
}
|
||||
|
||||
self->data = mp_obj_dict_get(uasyncio_context, MP_OBJ_NEW_QSTR(MP_QSTR_CancelledError));
|
||||
|
||||
return mp_const_true;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(task_cancel_obj, task_cancel);
|
||||
|
||||
STATIC void task_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
|
||||
mp_obj_task_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
if (dest[0] == MP_OBJ_NULL) {
|
||||
// Load
|
||||
if (attr == MP_QSTR_coro) {
|
||||
dest[0] = self->coro;
|
||||
} else if (attr == MP_QSTR_data) {
|
||||
dest[0] = self->data;
|
||||
} else if (attr == MP_QSTR_waiting) {
|
||||
if (self->waiting != mp_const_none) {
|
||||
dest[0] = self->waiting;
|
||||
}
|
||||
} else if (attr == MP_QSTR_cancel) {
|
||||
dest[0] = MP_OBJ_FROM_PTR(&task_cancel_obj);
|
||||
dest[1] = self_in;
|
||||
} else if (attr == MP_QSTR_ph_key) {
|
||||
dest[0] = self->ph_key;
|
||||
}
|
||||
} else if (dest[1] != MP_OBJ_NULL) {
|
||||
// Store
|
||||
if (attr == MP_QSTR_coro) {
|
||||
self->coro = dest[1];
|
||||
dest[0] = MP_OBJ_NULL;
|
||||
} else if (attr == MP_QSTR_data) {
|
||||
self->data = dest[1];
|
||||
dest[0] = MP_OBJ_NULL;
|
||||
} else if (attr == MP_QSTR_waiting) {
|
||||
self->waiting = dest[1];
|
||||
dest[0] = MP_OBJ_NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
STATIC mp_obj_t task_getiter(mp_obj_t self_in, mp_obj_iter_buf_t *iter_buf) {
|
||||
(void)iter_buf;
|
||||
mp_obj_task_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
if (self->waiting == mp_const_none) {
|
||||
self->waiting = task_queue_make_new(&task_queue_type, 0, NULL, NULL);
|
||||
}
|
||||
return self_in;
|
||||
}
|
||||
|
||||
STATIC mp_obj_t task_iternext(mp_obj_t self_in) {
|
||||
mp_obj_task_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
if (self->coro == mp_const_none) {
|
||||
// Task finished, raise return value to caller so it can continue.
|
||||
nlr_raise(self->data);
|
||||
} else {
|
||||
// Put calling task on waiting queue.
|
||||
mp_obj_t cur_task = mp_obj_dict_get(uasyncio_context, MP_OBJ_NEW_QSTR(MP_QSTR_cur_task));
|
||||
mp_obj_t args[2] = { self->waiting, cur_task };
|
||||
task_queue_push_sorted(2, args);
|
||||
// Set calling task's data to this task that it waits on, to double-link it.
|
||||
((mp_obj_task_t *)MP_OBJ_TO_PTR(cur_task))->data = self_in;
|
||||
}
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
STATIC const mp_obj_type_t task_type = {
|
||||
{ &mp_type_type },
|
||||
.name = MP_QSTR_Task,
|
||||
.make_new = task_make_new,
|
||||
.attr = task_attr,
|
||||
.getiter = task_getiter,
|
||||
.iternext = task_iternext,
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
// C-level uasyncio module
|
||||
|
||||
STATIC const mp_rom_map_elem_t mp_module_uasyncio_globals_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR__uasyncio) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_TaskQueue), MP_ROM_PTR(&task_queue_type) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_Task), MP_ROM_PTR(&task_type) },
|
||||
};
|
||||
STATIC MP_DEFINE_CONST_DICT(mp_module_uasyncio_globals, mp_module_uasyncio_globals_table);
|
||||
|
||||
const mp_obj_module_t mp_module_uasyncio = {
|
||||
.base = { &mp_type_module },
|
||||
.globals = (mp_obj_dict_t *)&mp_module_uasyncio_globals,
|
||||
};
|
||||
|
||||
#endif // MICROPY_PY_UASYNCIO
|
|
@ -9,17 +9,16 @@
|
|||
|
||||
#include "py/runtime.h"
|
||||
#include "py/binary.h"
|
||||
#include "extmod/modubinascii.h"
|
||||
|
||||
static void check_not_unicode(const mp_obj_t arg) {
|
||||
#if MICROPY_CPYTHON_COMPAT
|
||||
if (mp_obj_is_str(arg)) {
|
||||
mp_raise_TypeError(translate("a bytes-like object is required"));
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("a bytes-like object is required"));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
mp_obj_t mod_binascii_hexlify(size_t n_args, const mp_obj_t *args) {
|
||||
STATIC mp_obj_t mod_binascii_hexlify(size_t n_args, const mp_obj_t *args) {
|
||||
// Second argument is for an extension to allow a separator to be used
|
||||
// between values.
|
||||
const char *sep = NULL;
|
||||
|
@ -59,14 +58,14 @@ mp_obj_t mod_binascii_hexlify(size_t n_args, const mp_obj_t *args) {
|
|||
}
|
||||
return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr);
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_binascii_hexlify_obj, 1, 2, mod_binascii_hexlify);
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_binascii_hexlify_obj, 1, 2, mod_binascii_hexlify);
|
||||
|
||||
mp_obj_t mod_binascii_unhexlify(mp_obj_t data) {
|
||||
STATIC mp_obj_t mod_binascii_unhexlify(mp_obj_t data) {
|
||||
mp_buffer_info_t bufinfo;
|
||||
mp_get_buffer_raise(data, &bufinfo, MP_BUFFER_READ);
|
||||
|
||||
if ((bufinfo.len & 1) != 0) {
|
||||
mp_raise_ValueError(translate("odd-length string"));
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("odd-length string"));
|
||||
}
|
||||
vstr_t vstr;
|
||||
vstr_init_len(&vstr, bufinfo.len / 2);
|
||||
|
@ -77,7 +76,7 @@ mp_obj_t mod_binascii_unhexlify(mp_obj_t data) {
|
|||
if (unichar_isxdigit(hex_ch)) {
|
||||
hex_byte += unichar_xdigit_value(hex_ch);
|
||||
} else {
|
||||
mp_raise_ValueError(translate("non-hex digit found"));
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("non-hex digit found"));
|
||||
}
|
||||
if (i & 1) {
|
||||
hex_byte <<= 4;
|
||||
|
@ -88,7 +87,7 @@ mp_obj_t mod_binascii_unhexlify(mp_obj_t data) {
|
|||
}
|
||||
return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr);
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(mod_binascii_unhexlify_obj, mod_binascii_unhexlify);
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_binascii_unhexlify_obj, mod_binascii_unhexlify);
|
||||
|
||||
// If ch is a character in the base64 alphabet, and is not a pad character, then
|
||||
// the corresponding integer between 0 and 63, inclusively, is returned.
|
||||
|
@ -109,7 +108,7 @@ static int mod_binascii_sextet(byte ch) {
|
|||
}
|
||||
}
|
||||
|
||||
mp_obj_t mod_binascii_a2b_base64(mp_obj_t data) {
|
||||
STATIC mp_obj_t mod_binascii_a2b_base64(mp_obj_t data) {
|
||||
mp_buffer_info_t bufinfo;
|
||||
mp_get_buffer_raise(data, &bufinfo, MP_BUFFER_READ);
|
||||
byte *in = bufinfo.buf;
|
||||
|
@ -145,14 +144,14 @@ mp_obj_t mod_binascii_a2b_base64(mp_obj_t data) {
|
|||
}
|
||||
|
||||
if (nbits) {
|
||||
mp_raise_ValueError(translate("incorrect padding"));
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("incorrect padding"));
|
||||
}
|
||||
|
||||
return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr);
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(mod_binascii_a2b_base64_obj, mod_binascii_a2b_base64);
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_binascii_a2b_base64_obj, mod_binascii_a2b_base64);
|
||||
|
||||
mp_obj_t mod_binascii_b2a_base64(mp_obj_t data) {
|
||||
STATIC mp_obj_t mod_binascii_b2a_base64(mp_obj_t data) {
|
||||
check_not_unicode(data);
|
||||
mp_buffer_info_t bufinfo;
|
||||
mp_get_buffer_raise(data, &bufinfo, MP_BUFFER_READ);
|
||||
|
@ -203,12 +202,12 @@ mp_obj_t mod_binascii_b2a_base64(mp_obj_t data) {
|
|||
*out = '\n';
|
||||
return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr);
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(mod_binascii_b2a_base64_obj, mod_binascii_b2a_base64);
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_binascii_b2a_base64_obj, mod_binascii_b2a_base64);
|
||||
|
||||
#if MICROPY_PY_UBINASCII_CRC32
|
||||
#include "../../lib/uzlib/src/tinf.h"
|
||||
|
||||
mp_obj_t mod_binascii_crc32(size_t n_args, const mp_obj_t *args) {
|
||||
STATIC mp_obj_t mod_binascii_crc32(size_t n_args, const mp_obj_t *args) {
|
||||
mp_buffer_info_t bufinfo;
|
||||
check_not_unicode(args[0]);
|
||||
mp_get_buffer_raise(args[0], &bufinfo, MP_BUFFER_READ);
|
||||
|
@ -216,11 +215,9 @@ mp_obj_t mod_binascii_crc32(size_t n_args, const mp_obj_t *args) {
|
|||
crc = uzlib_crc32(bufinfo.buf, bufinfo.len, crc ^ 0xffffffff);
|
||||
return mp_obj_new_int_from_uint(crc ^ 0xffffffff);
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_binascii_crc32_obj, 1, 2, mod_binascii_crc32);
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_binascii_crc32_obj, 1, 2, mod_binascii_crc32);
|
||||
#endif
|
||||
|
||||
#if MICROPY_PY_UBINASCII
|
||||
|
||||
STATIC const mp_rom_map_elem_t mp_module_binascii_globals_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_binascii) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_hexlify), MP_ROM_PTR(&mod_binascii_hexlify_obj) },
|
||||
|
@ -238,5 +235,3 @@ const mp_obj_module_t mp_module_ubinascii = {
|
|||
.base = { &mp_type_module },
|
||||
.globals = (mp_obj_dict_t *)&mp_module_binascii_globals,
|
||||
};
|
||||
|
||||
#endif // MICROPY_PY_UBINASCII
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
// Copyright (c) 2014 Paul Sokolovsky
|
||||
// SPDX-FileCopyrightText: 2014 MicroPython & CircuitPython contributors (https://github.com/adafruit/circuitpython/graphs/contributors)
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#ifndef MICROPY_INCLUDED_EXTMOD_MODUBINASCII_H
|
||||
#define MICROPY_INCLUDED_EXTMOD_MODUBINASCII_H
|
||||
|
||||
extern mp_obj_t mod_binascii_hexlify(size_t n_args, const mp_obj_t *args);
|
||||
extern mp_obj_t mod_binascii_unhexlify(mp_obj_t data);
|
||||
extern mp_obj_t mod_binascii_a2b_base64(mp_obj_t data);
|
||||
extern mp_obj_t mod_binascii_b2a_base64(mp_obj_t data);
|
||||
extern mp_obj_t mod_binascii_crc32(size_t n_args, const mp_obj_t *args);
|
||||
|
||||
MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mod_binascii_hexlify_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_1(mod_binascii_unhexlify_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_1(mod_binascii_a2b_base64_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_1(mod_binascii_b2a_base64_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mod_binascii_crc32_obj);
|
||||
|
||||
#endif // MICROPY_INCLUDED_EXTMOD_MODUBINASCII_H
|
|
@ -98,7 +98,7 @@ typedef struct _mp_obj_uctypes_struct_t {
|
|||
} mp_obj_uctypes_struct_t;
|
||||
|
||||
STATIC NORETURN void syntax_error(void) {
|
||||
mp_raise_TypeError(translate("syntax error in uctypes descriptor"));
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("syntax error in uctypes descriptor"));
|
||||
}
|
||||
|
||||
STATIC mp_obj_t uctypes_struct_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) {
|
||||
|
@ -118,11 +118,7 @@ STATIC void uctypes_struct_print(const mp_print_t *print, mp_obj_t self_in, mp_p
|
|||
(void)kind;
|
||||
mp_obj_uctypes_struct_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
const char *typen = "unk";
|
||||
if (mp_obj_is_type(self->desc, &mp_type_dict)
|
||||
#if MICROPY_PY_COLLECTIONS_ORDEREDDICT
|
||||
|| mp_obj_is_type(self->desc, &mp_type_ordereddict)
|
||||
#endif
|
||||
) {
|
||||
if (mp_obj_is_dict_or_ordereddict(self->desc)) {
|
||||
typen = "STRUCT";
|
||||
} else if (mp_obj_is_type(self->desc, &mp_type_tuple)) {
|
||||
mp_obj_tuple_t *t = MP_OBJ_TO_PTR(self->desc);
|
||||
|
@ -195,11 +191,7 @@ STATIC mp_uint_t uctypes_struct_agg_size(mp_obj_tuple_t *t, int layout_type, mp_
|
|||
}
|
||||
|
||||
STATIC mp_uint_t uctypes_struct_size(mp_obj_t desc_in, int layout_type, mp_uint_t *max_field_size) {
|
||||
if (!mp_obj_is_type(desc_in, &mp_type_dict)
|
||||
#if MICROPY_PY_COLLECTIONS_ORDEREDDICT
|
||||
&& !mp_obj_is_type(desc_in, &mp_type_ordereddict)
|
||||
#endif
|
||||
) {
|
||||
if (!mp_obj_is_dict_or_ordereddict(desc_in)) {
|
||||
if (mp_obj_is_type(desc_in, &mp_type_tuple)) {
|
||||
return uctypes_struct_agg_size((mp_obj_tuple_t *)MP_OBJ_TO_PTR(desc_in), layout_type, max_field_size);
|
||||
} else if (mp_obj_is_small_int(desc_in)) {
|
||||
|
@ -207,7 +199,7 @@ STATIC mp_uint_t uctypes_struct_size(mp_obj_t desc_in, int layout_type, mp_uint_
|
|||
// but scalar structure field is lowered into native Python int, so all
|
||||
// type info is lost. So, we cannot say if it's scalar type description,
|
||||
// or such lowered scalar.
|
||||
mp_raise_TypeError(translate("Cannot unambiguously get sizeof scalar"));
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("cannot unambiguously get sizeof scalar"));
|
||||
}
|
||||
syntax_error();
|
||||
}
|
||||
|
@ -284,13 +276,13 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(uctypes_struct_sizeof_obj, 1, 2, ucty
|
|||
static inline mp_obj_t get_unaligned(uint val_type, byte *p, int big_endian) {
|
||||
char struct_type = big_endian ? '>' : '<';
|
||||
static const char type2char[16] = "BbHhIiQq------fd";
|
||||
return mp_binary_get_val(struct_type, type2char[val_type], &p);
|
||||
return mp_binary_get_val(struct_type, type2char[val_type], p, &p);
|
||||
}
|
||||
|
||||
static inline void set_unaligned(uint val_type, byte *p, int big_endian, mp_obj_t val) {
|
||||
char struct_type = big_endian ? '>' : '<';
|
||||
static const char type2char[16] = "BbHhIiQq------fd";
|
||||
mp_binary_set_val(struct_type, type2char[val_type], val, &p);
|
||||
mp_binary_set_val(struct_type, type2char[val_type], val, p, &p);
|
||||
}
|
||||
|
||||
static inline mp_uint_t get_aligned_basic(uint val_type, void *p) {
|
||||
|
@ -341,9 +333,9 @@ STATIC mp_obj_t get_aligned(uint val_type, void *p, mp_int_t index) {
|
|||
return mp_obj_new_int_from_ll(((int64_t *)p)[index]);
|
||||
#if MICROPY_PY_BUILTINS_FLOAT
|
||||
case FLOAT32:
|
||||
return mp_obj_new_float(((float *)p)[index]);
|
||||
return mp_obj_new_float_from_f(((float *)p)[index]);
|
||||
case FLOAT64:
|
||||
return mp_obj_new_float(((double *)p)[index]);
|
||||
return mp_obj_new_float_from_d(((double *)p)[index]);
|
||||
#endif
|
||||
default:
|
||||
assert(0);
|
||||
|
@ -354,11 +346,10 @@ STATIC mp_obj_t get_aligned(uint val_type, void *p, mp_int_t index) {
|
|||
STATIC void set_aligned(uint val_type, void *p, mp_int_t index, mp_obj_t val) {
|
||||
#if MICROPY_PY_BUILTINS_FLOAT
|
||||
if (val_type == FLOAT32 || val_type == FLOAT64) {
|
||||
mp_float_t v = mp_obj_get_float(val);
|
||||
if (val_type == FLOAT32) {
|
||||
((float *)p)[index] = v;
|
||||
((float *)p)[index] = mp_obj_get_float_to_f(val);
|
||||
} else {
|
||||
((double *)p)[index] = v;
|
||||
((double *)p)[index] = mp_obj_get_float_to_d(val);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -400,12 +391,8 @@ STATIC void set_aligned(uint val_type, void *p, mp_int_t index, mp_obj_t val) {
|
|||
STATIC mp_obj_t uctypes_struct_attr_op(mp_obj_t self_in, qstr attr, mp_obj_t set_val) {
|
||||
mp_obj_uctypes_struct_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
|
||||
if (!mp_obj_is_type(self->desc, &mp_type_dict)
|
||||
#if MICROPY_PY_COLLECTIONS_ORDEREDDICT
|
||||
&& !mp_obj_is_type(self->desc, &mp_type_ordereddict)
|
||||
#endif
|
||||
) {
|
||||
mp_raise_TypeError(translate("struct: no fields"));
|
||||
if (!mp_obj_is_dict_or_ordereddict(self->desc)) {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("struct: no fields"));
|
||||
}
|
||||
|
||||
mp_obj_t deref = mp_obj_dict_get(self->desc, MP_OBJ_NEW_QSTR(attr));
|
||||
|
@ -530,7 +517,7 @@ STATIC void uctypes_struct_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
|
|||
}
|
||||
|
||||
STATIC mp_obj_t uctypes_struct_subscr(mp_obj_t base_in, mp_obj_t index_in, mp_obj_t value) {
|
||||
mp_obj_uctypes_struct_t *self = mp_instance_cast_to_native_base(base_in, &uctypes_struct_type);
|
||||
mp_obj_uctypes_struct_t *self = mp_obj_cast_to_native_base(base_in, &uctypes_struct_type);
|
||||
|
||||
if (value == MP_OBJ_NULL) {
|
||||
// delete
|
||||
|
@ -538,7 +525,7 @@ STATIC mp_obj_t uctypes_struct_subscr(mp_obj_t base_in, mp_obj_t index_in, mp_ob
|
|||
} else {
|
||||
// load / store
|
||||
if (!mp_obj_is_type(self->desc, &mp_type_tuple)) {
|
||||
mp_raise_TypeError(translate("struct: cannot index"));
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("struct: can't index"));
|
||||
}
|
||||
|
||||
mp_obj_tuple_t *t = MP_OBJ_TO_PTR(self->desc);
|
||||
|
@ -552,7 +539,7 @@ STATIC mp_obj_t uctypes_struct_subscr(mp_obj_t base_in, mp_obj_t index_in, mp_ob
|
|||
uint val_type = GET_TYPE(arr_sz, VAL_TYPE_BITS);
|
||||
arr_sz &= VALUE_MASK(VAL_TYPE_BITS);
|
||||
if (index >= arr_sz) {
|
||||
mp_raise_IndexError_varg(translate("%q index out of range"), MP_QSTR_struct);
|
||||
mp_raise_msg(&mp_type_IndexError, MP_ERROR_TEXT("struct: index out of range"));
|
||||
}
|
||||
|
||||
if (t->len == 2) {
|
||||
|
|
|
@ -89,7 +89,7 @@ STATIC mp_obj_t uhashlib_sha256_digest(mp_obj_t self_in) {
|
|||
static void check_not_unicode(const mp_obj_t arg) {
|
||||
#if MICROPY_CPYTHON_COMPAT
|
||||
if (mp_obj_is_str(arg)) {
|
||||
mp_raise_TypeError(translate("a bytes-like object is required"));
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("a bytes-like object is required"));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -12,14 +12,14 @@
|
|||
|
||||
// the algorithm here is modelled on CPython's heapq.py
|
||||
|
||||
STATIC mp_obj_list_t *get_heap(mp_obj_t heap_in) {
|
||||
STATIC mp_obj_list_t *uheapq_get_heap(mp_obj_t heap_in) {
|
||||
if (!mp_obj_is_type(heap_in, &mp_type_list)) {
|
||||
mp_raise_TypeError(translate("heap must be a list"));
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("heap must be a list"));
|
||||
}
|
||||
return MP_OBJ_TO_PTR(heap_in);
|
||||
}
|
||||
|
||||
STATIC void heap_siftdown(mp_obj_list_t *heap, mp_uint_t start_pos, mp_uint_t pos) {
|
||||
STATIC void uheapq_heap_siftdown(mp_obj_list_t *heap, mp_uint_t start_pos, mp_uint_t pos) {
|
||||
mp_obj_t item = heap->items[pos];
|
||||
while (pos > start_pos) {
|
||||
mp_uint_t parent_pos = (pos - 1) >> 1;
|
||||
|
@ -34,7 +34,7 @@ STATIC void heap_siftdown(mp_obj_list_t *heap, mp_uint_t start_pos, mp_uint_t po
|
|||
heap->items[pos] = item;
|
||||
}
|
||||
|
||||
STATIC void heap_siftup(mp_obj_list_t *heap, mp_uint_t pos) {
|
||||
STATIC void uheapq_heap_siftup(mp_obj_list_t *heap, mp_uint_t pos) {
|
||||
mp_uint_t start_pos = pos;
|
||||
mp_uint_t end_pos = heap->len;
|
||||
mp_obj_t item = heap->items[pos];
|
||||
|
@ -48,42 +48,43 @@ STATIC void heap_siftup(mp_obj_list_t *heap, mp_uint_t pos) {
|
|||
pos = child_pos;
|
||||
}
|
||||
heap->items[pos] = item;
|
||||
heap_siftdown(heap, start_pos, pos);
|
||||
uheapq_heap_siftdown(heap, start_pos, pos);
|
||||
}
|
||||
|
||||
STATIC mp_obj_t mod_uheapq_heappush(mp_obj_t heap_in, mp_obj_t item) {
|
||||
mp_obj_list_t *heap = get_heap(heap_in);
|
||||
mp_obj_list_t *heap = uheapq_get_heap(heap_in);
|
||||
mp_obj_list_append(heap_in, item);
|
||||
heap_siftdown(heap, 0, heap->len - 1);
|
||||
uheapq_heap_siftdown(heap, 0, heap->len - 1);
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_uheapq_heappush_obj, mod_uheapq_heappush);
|
||||
|
||||
STATIC mp_obj_t mod_uheapq_heappop(mp_obj_t heap_in) {
|
||||
mp_obj_list_t *heap = get_heap(heap_in);
|
||||
mp_obj_list_t *heap = uheapq_get_heap(heap_in);
|
||||
if (heap->len == 0) {
|
||||
mp_raise_IndexError(translate("empty heap"));
|
||||
mp_raise_msg(&mp_type_IndexError, MP_ERROR_TEXT("empty heap"));
|
||||
}
|
||||
mp_obj_t item = heap->items[0];
|
||||
heap->len -= 1;
|
||||
heap->items[0] = heap->items[heap->len];
|
||||
heap->items[heap->len] = MP_OBJ_NULL; // so we don't retain a pointer
|
||||
if (heap->len) {
|
||||
heap_siftup(heap, 0);
|
||||
uheapq_heap_siftup(heap, 0);
|
||||
}
|
||||
return item;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_uheapq_heappop_obj, mod_uheapq_heappop);
|
||||
|
||||
STATIC mp_obj_t mod_uheapq_heapify(mp_obj_t heap_in) {
|
||||
mp_obj_list_t *heap = get_heap(heap_in);
|
||||
mp_obj_list_t *heap = uheapq_get_heap(heap_in);
|
||||
for (mp_uint_t i = heap->len / 2; i > 0;) {
|
||||
heap_siftup(heap, --i);
|
||||
uheapq_heap_siftup(heap, --i);
|
||||
}
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_uheapq_heapify_obj, mod_uheapq_heapify);
|
||||
|
||||
#if !MICROPY_ENABLE_DYNRUNTIME
|
||||
STATIC const mp_rom_map_elem_t mp_module_uheapq_globals_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_uheapq) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_heappush), MP_ROM_PTR(&mod_uheapq_heappush_obj) },
|
||||
|
@ -97,5 +98,6 @@ const mp_obj_module_t mp_module_uheapq = {
|
|||
.base = { &mp_type_module },
|
||||
.globals = (mp_obj_dict_t *)&mp_module_uheapq_globals,
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif // MICROPY_PY_UHEAPQ
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// SPDX-FileCopyrightText: 2014 MicroPython & CircuitPython contributors (https://github.com/adafruit/circuitpython/graphs/contributors)
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2014-2016 Damien P. George
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2014-2019 Damien P. George
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
|
@ -135,7 +135,7 @@ STATIC mp_obj_t _mod_ujson_load(mp_obj_t stream_obj, bool return_first_json) {
|
|||
stack.len = 0;
|
||||
stack.items = NULL;
|
||||
mp_obj_t stack_top = MP_OBJ_NULL;
|
||||
mp_obj_type_t *stack_top_type = NULL;
|
||||
const mp_obj_type_t *stack_top_type = NULL;
|
||||
mp_obj_t stack_key = MP_OBJ_NULL;
|
||||
S_NEXT(s);
|
||||
for (;;) {
|
||||
|
@ -339,7 +339,7 @@ success:
|
|||
return stack_top;
|
||||
|
||||
fail:
|
||||
mp_raise_ValueError(translate("syntax error in JSON"));
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("syntax error in JSON"));
|
||||
}
|
||||
|
||||
STATIC mp_obj_t mod_ujson_load(mp_obj_t stream_obj) {
|
||||
|
@ -348,9 +348,9 @@ STATIC mp_obj_t mod_ujson_load(mp_obj_t stream_obj) {
|
|||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_ujson_load_obj, mod_ujson_load);
|
||||
|
||||
STATIC mp_obj_t mod_ujson_loads(mp_obj_t obj) {
|
||||
size_t len;
|
||||
const char *buf = mp_obj_str_get_data(obj, &len);
|
||||
vstr_t vstr = {len, len, (char *)buf, true};
|
||||
mp_buffer_info_t bufinfo;
|
||||
mp_get_buffer_raise(obj, &bufinfo, MP_BUFFER_READ);
|
||||
vstr_t vstr = {bufinfo.len, bufinfo.len, (char *)bufinfo.buf, true};
|
||||
mp_obj_stringio_t sio = {{&mp_type_stringio}, &vstr, 0, MP_OBJ_NULL};
|
||||
return _mod_ujson_load(MP_OBJ_FROM_PTR(&sio), false);
|
||||
}
|
||||
|
|
|
@ -15,8 +15,10 @@
|
|||
// http://www.literatecode.com/yasmarang
|
||||
// Public Domain
|
||||
|
||||
#if !MICROPY_ENABLE_DYNRUNTIME
|
||||
STATIC uint32_t yasmarang_pad = 0xeda4baba, yasmarang_n = 69, yasmarang_d = 233;
|
||||
STATIC uint8_t yasmarang_dat = 0;
|
||||
#endif
|
||||
|
||||
STATIC uint32_t yasmarang(void) {
|
||||
yasmarang_pad += yasmarang_dat + yasmarang_d * yasmarang_n;
|
||||
|
@ -130,7 +132,7 @@ STATIC mp_obj_t mod_urandom_choice(mp_obj_t seq) {
|
|||
if (len > 0) {
|
||||
return mp_obj_subscr(seq, mp_obj_new_int(yasmarang_randbelow(len)), MP_OBJ_SENTINEL);
|
||||
} else {
|
||||
nlr_raise(mp_obj_new_exception(&mp_type_IndexError));
|
||||
mp_raise_type(&mp_type_IndexError);
|
||||
}
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_urandom_choice_obj, mod_urandom_choice);
|
||||
|
@ -139,21 +141,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_urandom_choice_obj, mod_urandom_choice);
|
|||
|
||||
// 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;
|
||||
mp_float_union_t u;
|
||||
u.p.sgn = 0;
|
||||
u.p.exp = (1 << (MP_FLOAT_EXP_BITS - 1)) - 1;
|
||||
if (MP_FLOAT_FRAC_BITS <= 32) {
|
||||
|
@ -188,6 +176,7 @@ STATIC mp_obj_t mod_urandom___init__() {
|
|||
STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_urandom___init___obj, mod_urandom___init__);
|
||||
#endif
|
||||
|
||||
#if !MICROPY_ENABLE_DYNRUNTIME
|
||||
STATIC const mp_rom_map_elem_t mp_module_urandom_globals_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_urandom) },
|
||||
#ifdef MICROPY_PY_URANDOM_SEED_INIT_FUNC
|
||||
|
@ -212,5 +201,6 @@ const mp_obj_module_t mp_module_urandom = {
|
|||
.base = { &mp_type_module },
|
||||
.globals = (mp_obj_dict_t *)&mp_module_urandom_globals,
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif // MICROPY_PY_URANDOM
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
#include "re1.5/re1.5.h"
|
||||
|
||||
#if CIRCUITPY_RE_DEBUG
|
||||
#if MICROPY_PY_URE_DEBUG
|
||||
#define FLAG_DEBUG 0x1000
|
||||
#endif
|
||||
|
||||
|
@ -34,6 +34,10 @@ typedef struct _mp_obj_match_t {
|
|||
const char *caps[0];
|
||||
} mp_obj_match_t;
|
||||
|
||||
STATIC mp_obj_t mod_re_compile(size_t n_args, const mp_obj_t *args);
|
||||
#if !MICROPY_ENABLE_DYNRUNTIME
|
||||
STATIC const mp_obj_type_t re_type;
|
||||
#endif
|
||||
|
||||
STATIC void match_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
|
||||
(void)kind;
|
||||
|
@ -125,6 +129,7 @@ MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(match_end_obj, 1, 2, match_end);
|
|||
|
||||
#endif
|
||||
|
||||
#if !MICROPY_ENABLE_DYNRUNTIME
|
||||
STATIC const mp_rom_map_elem_t match_locals_dict_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR_group), MP_ROM_PTR(&match_group_obj) },
|
||||
#if MICROPY_PY_URE_MATCH_GROUPS
|
||||
|
@ -145,6 +150,7 @@ STATIC const mp_obj_type_t match_type = {
|
|||
.print = match_print,
|
||||
.locals_dict = (void *)&match_locals_dict,
|
||||
};
|
||||
#endif
|
||||
|
||||
STATIC void re_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
|
||||
(void)kind;
|
||||
|
@ -154,15 +160,21 @@ STATIC void re_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t
|
|||
|
||||
STATIC mp_obj_t ure_exec(bool is_anchored, uint n_args, const mp_obj_t *args) {
|
||||
(void)n_args;
|
||||
mp_obj_re_t *self = MP_OBJ_TO_PTR(args[0]);
|
||||
mp_obj_re_t *self;
|
||||
if (mp_obj_is_type(args[0], &re_type)) {
|
||||
self = MP_OBJ_TO_PTR(args[0]);
|
||||
} else {
|
||||
self = MP_OBJ_TO_PTR(mod_re_compile(1, args));
|
||||
}
|
||||
Subject subj;
|
||||
size_t len;
|
||||
subj.begin = mp_obj_str_get_data(args[1], &len);
|
||||
subj.end = subj.begin + len;
|
||||
#if MICROPY_PY_URE_MATCH_SPAN_START_END
|
||||
#if MICROPY_PY_URE_MATCH_SPAN_START_END && !(defined(MICROPY_ENABLE_DYNRUNTIME) && MICROPY_ENABLE_DYNRUNTIME)
|
||||
|
||||
if (n_args > 2) {
|
||||
const mp_obj_type_t *self_type = mp_obj_get_type(args[1]);
|
||||
mp_int_t str_len = MP_OBJ_SMALL_INT_VALUE(mp_obj_len_maybe(args[1]));
|
||||
mp_int_t str_len = MP_OBJ_SMALL_INT_VALUE(mp_obj_len(args[1]));
|
||||
const byte *begin = (const byte *)subj.begin;
|
||||
|
||||
int pos = mp_obj_get_int(args[2]);
|
||||
|
@ -243,7 +255,7 @@ STATIC mp_obj_t re_split(size_t n_args, const mp_obj_t *args) {
|
|||
mp_obj_t s = mp_obj_new_str_of_type(str_type, (const byte *)subj.begin, caps[0] - subj.begin);
|
||||
mp_obj_list_append(retval, s);
|
||||
if (self->re.sub > 0) {
|
||||
mp_raise_NotImplementedError(translate("Splitting with sub-captures"));
|
||||
mp_raise_NotImplementedError(MP_ERROR_TEXT("Splitting with sub-captures"));
|
||||
}
|
||||
subj.begin = caps[1];
|
||||
if (maxsplit > 0 && --maxsplit == 0) {
|
||||
|
@ -261,8 +273,13 @@ MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(re_split_obj, 2, 3, re_split);
|
|||
|
||||
#if MICROPY_PY_URE_SUB
|
||||
|
||||
STATIC mp_obj_t re_sub_helper(mp_obj_t self_in, size_t n_args, const mp_obj_t *args) {
|
||||
mp_obj_re_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
STATIC mp_obj_t re_sub_helper(size_t n_args, const mp_obj_t *args) {
|
||||
mp_obj_re_t *self;
|
||||
if (mp_obj_is_type(args[0], &re_type)) {
|
||||
self = MP_OBJ_TO_PTR(args[0]);
|
||||
} else {
|
||||
self = MP_OBJ_TO_PTR(mod_re_compile(1, args));
|
||||
}
|
||||
mp_obj_t replace = args[1];
|
||||
mp_obj_t where = args[2];
|
||||
mp_int_t count = 0;
|
||||
|
@ -366,13 +383,11 @@ STATIC mp_obj_t re_sub_helper(mp_obj_t self_in, size_t n_args, const mp_obj_t *a
|
|||
return mp_obj_new_str_from_vstr(mp_obj_get_type(where), &vstr_return);
|
||||
}
|
||||
|
||||
STATIC mp_obj_t re_sub(size_t n_args, const mp_obj_t *args) {
|
||||
return re_sub_helper(args[0], n_args, args);
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(re_sub_obj, 3, 5, re_sub);
|
||||
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(re_sub_obj, 3, 5, re_sub_helper);
|
||||
|
||||
#endif
|
||||
|
||||
#if !MICROPY_ENABLE_DYNRUNTIME
|
||||
STATIC const mp_rom_map_elem_t re_locals_dict_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR_match), MP_ROM_PTR(&re_match_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_search), MP_ROM_PTR(&re_search_obj) },
|
||||
|
@ -394,8 +409,10 @@ STATIC const mp_obj_type_t re_type = {
|
|||
.print = re_print,
|
||||
.locals_dict = (void *)&re_locals_dict,
|
||||
};
|
||||
#endif
|
||||
|
||||
STATIC mp_obj_t mod_re_compile(size_t n_args, const mp_obj_t *args) {
|
||||
(void)n_args;
|
||||
const char *re_str = mp_obj_str_get_str(args[0]);
|
||||
int size = re1_5_sizecode(re_str);
|
||||
if (size == -1) {
|
||||
|
@ -403,7 +420,7 @@ STATIC mp_obj_t mod_re_compile(size_t n_args, const mp_obj_t *args) {
|
|||
}
|
||||
mp_obj_re_t *o = m_new_obj_var(mp_obj_re_t, char, size);
|
||||
o->base.type = &re_type;
|
||||
#if CIRCUITPY_RE_DEBUG
|
||||
#if MICROPY_PY_URE_DEBUG
|
||||
int flags = 0;
|
||||
if (n_args > 1) {
|
||||
flags = mp_obj_get_int(args[1]);
|
||||
|
@ -414,9 +431,9 @@ STATIC mp_obj_t mod_re_compile(size_t n_args, const mp_obj_t *args) {
|
|||
int error = re1_5_compilecode(&o->re, re_str);
|
||||
if (error != 0) {
|
||||
error:
|
||||
mp_raise_ValueError(translate("Error in regex"));
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("Error in regex"));
|
||||
}
|
||||
#if CIRCUITPY_RE_DEBUG
|
||||
#if MICROPY_PY_URE_DEBUG
|
||||
if (flags & FLAG_DEBUG) {
|
||||
re1_5_dumpcode(&o->re);
|
||||
}
|
||||
|
@ -425,33 +442,7 @@ STATIC mp_obj_t mod_re_compile(size_t n_args, const mp_obj_t *args) {
|
|||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_re_compile_obj, 1, 2, mod_re_compile);
|
||||
|
||||
STATIC mp_obj_t mod_re_exec(bool is_anchored, uint n_args, const mp_obj_t *args) {
|
||||
(void)n_args;
|
||||
mp_obj_t self = mod_re_compile(1, args);
|
||||
|
||||
const mp_obj_t args2[] = {self, args[1]};
|
||||
mp_obj_t match = ure_exec(is_anchored, 2, args2);
|
||||
return match;
|
||||
}
|
||||
|
||||
STATIC mp_obj_t mod_re_match(size_t n_args, const mp_obj_t *args) {
|
||||
return mod_re_exec(true, n_args, args);
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_re_match_obj, 2, 4, mod_re_match);
|
||||
|
||||
STATIC mp_obj_t mod_re_search(size_t n_args, const mp_obj_t *args) {
|
||||
return mod_re_exec(false, n_args, args);
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_re_search_obj, 2, 4, mod_re_search);
|
||||
|
||||
#if MICROPY_PY_URE_SUB
|
||||
STATIC mp_obj_t mod_re_sub(size_t n_args, const mp_obj_t *args) {
|
||||
mp_obj_t self = mod_re_compile(1, args);
|
||||
return re_sub_helper(self, n_args, args);
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_re_sub_obj, 3, 5, mod_re_sub);
|
||||
#endif
|
||||
|
||||
#if !MICROPY_ENABLE_DYNRUNTIME
|
||||
STATIC const mp_rom_map_elem_t mp_module_re_globals_table[] = {
|
||||
#if CIRCUITPY
|
||||
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_re) },
|
||||
|
@ -459,12 +450,12 @@ STATIC const mp_rom_map_elem_t mp_module_re_globals_table[] = {
|
|||
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_ure) },
|
||||
#endif
|
||||
{ MP_ROM_QSTR(MP_QSTR_compile), MP_ROM_PTR(&mod_re_compile_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_match), MP_ROM_PTR(&mod_re_match_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_search), MP_ROM_PTR(&mod_re_search_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_match), MP_ROM_PTR(&re_match_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_search), MP_ROM_PTR(&re_search_obj) },
|
||||
#if MICROPY_PY_URE_SUB
|
||||
{ MP_ROM_QSTR(MP_QSTR_sub), MP_ROM_PTR(&mod_re_sub_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_sub), MP_ROM_PTR(&re_sub_obj) },
|
||||
#endif
|
||||
#if CIRCUITPY_RE_DEBUG
|
||||
#if MICROPY_PY_URE_DEBUG
|
||||
{ MP_ROM_QSTR(MP_QSTR_DEBUG), MP_ROM_INT(FLAG_DEBUG) },
|
||||
#endif
|
||||
};
|
||||
|
@ -475,13 +466,14 @@ const mp_obj_module_t mp_module_ure = {
|
|||
.base = { &mp_type_module },
|
||||
.globals = (mp_obj_dict_t *)&mp_module_re_globals,
|
||||
};
|
||||
#endif
|
||||
|
||||
// Source files #include'd here to make sure they're compiled in
|
||||
// only if module is enabled by config setting.
|
||||
|
||||
#define re1_5_fatal(x) assert(!x)
|
||||
#include "re1.5/compilecode.c"
|
||||
#if CIRCUITPY_RE_DEBUG
|
||||
#if MICROPY_PY_URE_DEBUG
|
||||
#include "re1.5/dumpcode.c"
|
||||
#endif
|
||||
#include "re1.5/recursiveloop.c"
|
||||
|
|
|
@ -105,7 +105,7 @@ STATIC mp_obj_t select_select(size_t n_args, const mp_obj_t *args) {
|
|||
if (n_args == 4) {
|
||||
if (args[3] != mp_const_none) {
|
||||
#if MICROPY_PY_BUILTINS_FLOAT
|
||||
float timeout_f = mp_obj_get_float(args[3]);
|
||||
float timeout_f = mp_obj_get_float_to_f(args[3]);
|
||||
if (timeout_f >= 0) {
|
||||
timeout = (mp_uint_t)(timeout_f * 1000);
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ typedef struct _mp_obj_utimeq_t {
|
|||
|
||||
STATIC mp_uint_t utimeq_id;
|
||||
|
||||
STATIC mp_obj_utimeq_t *get_heap(mp_obj_t heap_in) {
|
||||
STATIC mp_obj_utimeq_t *utimeq_get_heap(mp_obj_t heap_in) {
|
||||
return MP_OBJ_TO_PTR(heap_in);
|
||||
}
|
||||
|
||||
|
@ -66,7 +66,7 @@ STATIC mp_obj_t utimeq_make_new(const mp_obj_type_t *type, size_t n_args, const
|
|||
return MP_OBJ_FROM_PTR(o);
|
||||
}
|
||||
|
||||
STATIC void heap_siftdown(mp_obj_utimeq_t *heap, mp_uint_t start_pos, mp_uint_t pos) {
|
||||
STATIC void utimeq_heap_siftdown(mp_obj_utimeq_t *heap, mp_uint_t start_pos, mp_uint_t pos) {
|
||||
struct qentry item = heap->items[pos];
|
||||
while (pos > start_pos) {
|
||||
mp_uint_t parent_pos = (pos - 1) >> 1;
|
||||
|
@ -82,7 +82,7 @@ STATIC void heap_siftdown(mp_obj_utimeq_t *heap, mp_uint_t start_pos, mp_uint_t
|
|||
heap->items[pos] = item;
|
||||
}
|
||||
|
||||
STATIC void heap_siftup(mp_obj_utimeq_t *heap, mp_uint_t pos) {
|
||||
STATIC void utimeq_heap_siftup(mp_obj_utimeq_t *heap, mp_uint_t pos) {
|
||||
mp_uint_t start_pos = pos;
|
||||
mp_uint_t end_pos = heap->len;
|
||||
struct qentry item = heap->items[pos];
|
||||
|
@ -99,31 +99,31 @@ STATIC void heap_siftup(mp_obj_utimeq_t *heap, mp_uint_t pos) {
|
|||
pos = child_pos;
|
||||
}
|
||||
heap->items[pos] = item;
|
||||
heap_siftdown(heap, start_pos, pos);
|
||||
utimeq_heap_siftdown(heap, start_pos, pos);
|
||||
}
|
||||
|
||||
STATIC mp_obj_t mod_utimeq_heappush(size_t n_args, const mp_obj_t *args) {
|
||||
(void)n_args;
|
||||
mp_obj_t heap_in = args[0];
|
||||
mp_obj_utimeq_t *heap = get_heap(heap_in);
|
||||
mp_obj_utimeq_t *heap = utimeq_get_heap(heap_in);
|
||||
if (heap->len == heap->alloc) {
|
||||
mp_raise_IndexError(translate("queue overflow"));
|
||||
mp_raise_IndexError(MP_ERROR_TEXT("queue overflow"));
|
||||
}
|
||||
mp_uint_t l = heap->len;
|
||||
heap->items[l].time = MP_OBJ_SMALL_INT_VALUE(args[1]);
|
||||
heap->items[l].id = utimeq_id++;
|
||||
heap->items[l].callback = args[2];
|
||||
heap->items[l].args = args[3];
|
||||
heap_siftdown(heap, 0, heap->len);
|
||||
utimeq_heap_siftdown(heap, 0, heap->len);
|
||||
heap->len++;
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_utimeq_heappush_obj, 4, 4, mod_utimeq_heappush);
|
||||
|
||||
STATIC mp_obj_t mod_utimeq_heappop(mp_obj_t heap_in, mp_obj_t list_ref) {
|
||||
mp_obj_utimeq_t *heap = get_heap(heap_in);
|
||||
mp_obj_utimeq_t *heap = utimeq_get_heap(heap_in);
|
||||
if (heap->len == 0) {
|
||||
mp_raise_IndexError(translate("empty heap"));
|
||||
mp_raise_IndexError(MP_ERROR_TEXT("empty heap"));
|
||||
}
|
||||
mp_obj_list_t *ret = MP_OBJ_TO_PTR(list_ref);
|
||||
if (!mp_obj_is_type(list_ref, &mp_type_list) || ret->len < 3) {
|
||||
|
@ -139,16 +139,16 @@ STATIC mp_obj_t mod_utimeq_heappop(mp_obj_t heap_in, mp_obj_t list_ref) {
|
|||
heap->items[heap->len].callback = MP_OBJ_NULL; // so we don't retain a pointer
|
||||
heap->items[heap->len].args = MP_OBJ_NULL;
|
||||
if (heap->len) {
|
||||
heap_siftup(heap, 0);
|
||||
utimeq_heap_siftup(heap, 0);
|
||||
}
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_utimeq_heappop_obj, mod_utimeq_heappop);
|
||||
|
||||
STATIC mp_obj_t mod_utimeq_peektime(mp_obj_t heap_in) {
|
||||
mp_obj_utimeq_t *heap = get_heap(heap_in);
|
||||
mp_obj_utimeq_t *heap = utimeq_get_heap(heap_in);
|
||||
if (heap->len == 0) {
|
||||
mp_raise_IndexError(translate("empty heap"));
|
||||
mp_raise_IndexError(MP_ERROR_TEXT("empty heap"));
|
||||
}
|
||||
|
||||
struct qentry *item = &heap->items[0];
|
||||
|
@ -158,7 +158,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_utimeq_peektime_obj, mod_utimeq_peektime);
|
|||
|
||||
#if DEBUG
|
||||
STATIC mp_obj_t mod_utimeq_dump(mp_obj_t heap_in) {
|
||||
mp_obj_utimeq_t *heap = get_heap(heap_in);
|
||||
mp_obj_utimeq_t *heap = utimeq_get_heap(heap_in);
|
||||
for (int i = 0; i < heap->len; i++) {
|
||||
printf(UINT_FMT "\t%p\t%p\n", heap->items[i].time,
|
||||
MP_OBJ_TO_PTR(heap->items[i].callback), MP_OBJ_TO_PTR(heap->items[i].args));
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
#ifndef MICROPY_INCLUDED_EXTMOD_MODUWEBSOCKET_H
|
||||
#define MICROPY_INCLUDED_EXTMOD_MODUWEBSOCKET_H
|
||||
|
||||
#define FRAME_OPCODE_MASK 0x0f
|
||||
enum {
|
||||
FRAME_CONT, FRAME_TXT, FRAME_BIN,
|
||||
FRAME_CLOSE = 0x8, FRAME_PING, FRAME_PONG
|
||||
};
|
||||
|
||||
#endif // MICROPY_INCLUDED_EXTMOD_MODUWEBSOCKET_H
|
|
@ -15,7 +15,7 @@
|
|||
#if MICROPY_PY_UZLIB
|
||||
|
||||
#define UZLIB_CONF_PARANOID_CHECKS (1)
|
||||
#include "../../lib/uzlib/src/tinf.h"
|
||||
#include "../lib/uzlib/src/tinf.h"
|
||||
|
||||
#if 0 // print debugging info
|
||||
#define DEBUG_printf DEBUG_printf
|
||||
|
@ -35,7 +35,7 @@ STATIC int read_src_stream(TINF_DATA *data) {
|
|||
p -= offsetof(mp_obj_decompio_t, decomp);
|
||||
mp_obj_decompio_t *self = (mp_obj_decompio_t *)p;
|
||||
|
||||
const mp_stream_p_t *stream = mp_get_stream(self->src_stream);
|
||||
const mp_stream_p_t *stream = mp_get_stream_raise(self->src_stream, MP_STREAM_OP_READ);
|
||||
int err;
|
||||
byte c;
|
||||
mp_uint_t out_sz = stream->read(self->src_stream, &c, 1, &err);
|
||||
|
@ -43,7 +43,7 @@ STATIC int read_src_stream(TINF_DATA *data) {
|
|||
mp_raise_OSError(err);
|
||||
}
|
||||
if (out_sz == 0) {
|
||||
nlr_raise(mp_obj_new_exception(&mp_type_EOFError));
|
||||
mp_raise_type(&mp_type_EOFError);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
@ -74,7 +74,7 @@ STATIC mp_obj_t decompio_make_new(const mp_obj_type_t *type, size_t n_args, cons
|
|||
dict_opt = uzlib_zlib_parse_header(&o->decomp);
|
||||
if (dict_opt < 0) {
|
||||
header_error:
|
||||
mp_raise_ValueError(translate("compression header"));
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("compression header"));
|
||||
}
|
||||
dict_sz = 1 << dict_opt;
|
||||
} else {
|
||||
|
@ -104,6 +104,7 @@ STATIC mp_uint_t decompio_read(mp_obj_t o_in, void *buf, mp_uint_t size, int *er
|
|||
return o->decomp.dest - (byte *)buf;
|
||||
}
|
||||
|
||||
#if !MICROPY_ENABLE_DYNRUNTIME
|
||||
STATIC const mp_rom_map_elem_t decompio_locals_dict_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) },
|
||||
|
@ -111,12 +112,14 @@ STATIC const mp_rom_map_elem_t decompio_locals_dict_table[] = {
|
|||
};
|
||||
|
||||
STATIC MP_DEFINE_CONST_DICT(decompio_locals_dict, decompio_locals_dict_table);
|
||||
#endif
|
||||
|
||||
STATIC const mp_stream_p_t decompio_stream_p = {
|
||||
MP_PROTO_IMPLEMENT(MP_QSTR_protocol_stream)
|
||||
.read = decompio_read,
|
||||
};
|
||||
|
||||
#if !MICROPY_ENABLE_DYNRUNTIME
|
||||
STATIC const mp_obj_type_t decompio_type = {
|
||||
{ &mp_type_type },
|
||||
.name = MP_QSTR_DecompIO,
|
||||
|
@ -124,6 +127,7 @@ STATIC const mp_obj_type_t decompio_type = {
|
|||
.protocol = &decompio_stream_p,
|
||||
.locals_dict = (void *)&decompio_locals_dict,
|
||||
};
|
||||
#endif
|
||||
|
||||
STATIC mp_obj_t mod_uzlib_decompress(size_t n_args, const mp_obj_t *args) {
|
||||
mp_obj_t data = args[0];
|
||||
|
@ -183,6 +187,7 @@ error:
|
|||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_uzlib_decompress_obj, 1, 3, mod_uzlib_decompress);
|
||||
|
||||
#if !MICROPY_ENABLE_DYNRUNTIME
|
||||
STATIC const mp_rom_map_elem_t mp_module_uzlib_globals_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_uzlib) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_decompress), MP_ROM_PTR(&mod_uzlib_decompress_obj) },
|
||||
|
@ -195,15 +200,16 @@ const mp_obj_module_t mp_module_uzlib = {
|
|||
.base = { &mp_type_module },
|
||||
.globals = (mp_obj_dict_t *)&mp_module_uzlib_globals,
|
||||
};
|
||||
#endif
|
||||
|
||||
// Source files #include'd here to make sure they're compiled in
|
||||
// only if module is enabled by config setting.
|
||||
|
||||
#pragma GCC diagnostic ignored "-Wsign-compare"
|
||||
#include "../../lib/uzlib/src/tinflate.c"
|
||||
#include "../../lib/uzlib/src/tinfzlib.c"
|
||||
#include "../../lib/uzlib/src/tinfgzip.c"
|
||||
#include "../../lib/uzlib/src/adler32.c"
|
||||
#include "../../lib/uzlib/src/crc32.c"
|
||||
#include "../lib/uzlib/src/tinflate.c"
|
||||
#include "../lib/uzlib/src/tinfzlib.c"
|
||||
#include "../lib/uzlib/src/tinfgzip.c"
|
||||
#include "../lib/uzlib/src/adler32.c"
|
||||
#include "../lib/uzlib/src/crc32.c"
|
||||
|
||||
#endif // MICROPY_PY_UZLIB
|
||||
|
|
|
@ -1,344 +0,0 @@
|
|||
// Copyright (c) 2016 Paul Sokolovsky
|
||||
// SPDX-FileCopyrightText: 2014 MicroPython & CircuitPython contributors (https://github.com/adafruit/circuitpython/graphs/contributors)
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "py/runtime.h"
|
||||
#include "py/stream.h"
|
||||
#include "py/builtin.h"
|
||||
#ifdef MICROPY_PY_WEBREPL_DELAY
|
||||
#include "py/mphal.h"
|
||||
#endif
|
||||
#include "extmod/moduwebsocket.h"
|
||||
|
||||
#if MICROPY_PY_WEBREPL
|
||||
|
||||
#if 0 // print debugging info
|
||||
#define DEBUG_printf DEBUG_printf
|
||||
#else // don't print debugging info
|
||||
#define DEBUG_printf(...) (void)0
|
||||
#endif
|
||||
|
||||
struct webrepl_file {
|
||||
char sig[2];
|
||||
char type;
|
||||
char flags;
|
||||
uint64_t offset;
|
||||
uint32_t size;
|
||||
uint16_t fname_len;
|
||||
char fname[64];
|
||||
} __attribute__((packed));
|
||||
|
||||
enum { PUT_FILE = 1, GET_FILE, GET_VER };
|
||||
enum { STATE_PASSWD, STATE_NORMAL };
|
||||
|
||||
typedef struct _mp_obj_webrepl_t {
|
||||
mp_obj_base_t base;
|
||||
mp_obj_t sock;
|
||||
byte state;
|
||||
byte hdr_to_recv;
|
||||
uint32_t data_to_recv;
|
||||
struct webrepl_file hdr;
|
||||
mp_obj_t cur_file;
|
||||
} mp_obj_webrepl_t;
|
||||
|
||||
// These get passed to functions which aren't force-l32, so can't be const
|
||||
STATIC char passwd_prompt[] = "Password: ";
|
||||
STATIC char connected_prompt[] = "\r\nWebREPL connected\r\n>>> ";
|
||||
STATIC char denied_prompt[] = "\r\nAccess denied\r\n";
|
||||
|
||||
STATIC char webrepl_passwd[10];
|
||||
|
||||
STATIC void write_webrepl(mp_obj_t websock, const void *buf, size_t len) {
|
||||
const mp_stream_p_t *sock_stream = mp_get_stream(websock);
|
||||
int err;
|
||||
int old_opts = sock_stream->ioctl(websock, MP_STREAM_SET_DATA_OPTS, FRAME_BIN, &err);
|
||||
sock_stream->write(websock, buf, len, &err);
|
||||
sock_stream->ioctl(websock, MP_STREAM_SET_DATA_OPTS, old_opts, &err);
|
||||
}
|
||||
|
||||
#define SSTR(s) s, sizeof(s) - 1
|
||||
STATIC void write_webrepl_str(mp_obj_t websock, const char *str, int sz) {
|
||||
int err;
|
||||
const mp_stream_p_t *sock_stream = mp_get_stream(websock);
|
||||
sock_stream->write(websock, str, sz, &err);
|
||||
}
|
||||
|
||||
STATIC void write_webrepl_resp(mp_obj_t websock, uint16_t code) {
|
||||
char buf[4] = {'W', 'B', code & 0xff, code >> 8};
|
||||
write_webrepl(websock, buf, sizeof(buf));
|
||||
}
|
||||
|
||||
STATIC mp_obj_t webrepl_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
|
||||
mp_arg_check_num(n_args, n_kw, 1, 2, false);
|
||||
mp_get_stream_raise(args[0], MP_STREAM_OP_READ | MP_STREAM_OP_WRITE | MP_STREAM_OP_IOCTL);
|
||||
DEBUG_printf("sizeof(struct webrepl_file) = %lu\n", sizeof(struct webrepl_file));
|
||||
mp_obj_webrepl_t *o = m_new_obj(mp_obj_webrepl_t);
|
||||
o->base.type = type;
|
||||
o->sock = args[0];
|
||||
o->hdr_to_recv = sizeof(struct webrepl_file);
|
||||
o->data_to_recv = 0;
|
||||
o->state = STATE_PASSWD;
|
||||
write_webrepl_str(args[0], SSTR(passwd_prompt));
|
||||
return o;
|
||||
}
|
||||
|
||||
STATIC void check_file_op_finished(mp_obj_webrepl_t *self) {
|
||||
if (self->data_to_recv == 0) {
|
||||
mp_stream_close(self->cur_file);
|
||||
self->hdr_to_recv = sizeof(struct webrepl_file);
|
||||
DEBUG_printf("webrepl: Finished file operation %d\n", self->hdr.type);
|
||||
write_webrepl_resp(self->sock, 0);
|
||||
}
|
||||
}
|
||||
|
||||
STATIC int write_file_chunk(mp_obj_webrepl_t *self) {
|
||||
const mp_stream_p_t *file_stream = mp_get_stream(self->cur_file);
|
||||
byte readbuf[2 + 256];
|
||||
int err;
|
||||
mp_uint_t out_sz = file_stream->read(self->cur_file, readbuf + 2, sizeof(readbuf) - 2, &err);
|
||||
if (out_sz == MP_STREAM_ERROR) {
|
||||
return out_sz;
|
||||
}
|
||||
readbuf[0] = out_sz;
|
||||
readbuf[1] = out_sz >> 8;
|
||||
DEBUG_printf("webrepl: Sending %d bytes of file\n", out_sz);
|
||||
write_webrepl(self->sock, readbuf, 2 + out_sz);
|
||||
return out_sz;
|
||||
}
|
||||
|
||||
STATIC void handle_op(mp_obj_webrepl_t *self) {
|
||||
|
||||
// Handle operations not requiring opened file
|
||||
|
||||
switch (self->hdr.type) {
|
||||
case GET_VER: {
|
||||
static char ver[] = {MICROPY_VERSION_MAJOR, MICROPY_VERSION_MINOR, MICROPY_VERSION_MICRO};
|
||||
write_webrepl(self->sock, ver, sizeof(ver));
|
||||
self->hdr_to_recv = sizeof(struct webrepl_file);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle operations requiring opened file
|
||||
|
||||
mp_obj_t open_args[2] = {
|
||||
mp_obj_new_str(self->hdr.fname, strlen(self->hdr.fname)),
|
||||
MP_OBJ_NEW_QSTR(MP_QSTR_rb)
|
||||
};
|
||||
|
||||
if (self->hdr.type == PUT_FILE) {
|
||||
open_args[1] = MP_OBJ_NEW_QSTR(MP_QSTR_wb);
|
||||
}
|
||||
|
||||
self->cur_file = mp_builtin_open(2, open_args, (mp_map_t *)&mp_const_empty_map);
|
||||
|
||||
#if 0
|
||||
struct mp_stream_seek_t seek = { .offset = self->hdr.offset, .whence = 0 };
|
||||
int err;
|
||||
mp_uint_t res = file_stream->ioctl(self->cur_file, MP_STREAM_SEEK, (uintptr_t)&seek, &err);
|
||||
assert(res != MP_STREAM_ERROR);
|
||||
#endif
|
||||
|
||||
write_webrepl_resp(self->sock, 0);
|
||||
|
||||
if (self->hdr.type == PUT_FILE) {
|
||||
self->data_to_recv = self->hdr.size;
|
||||
check_file_op_finished(self);
|
||||
} else if (self->hdr.type == GET_FILE) {
|
||||
self->data_to_recv = 1;
|
||||
}
|
||||
}
|
||||
|
||||
STATIC mp_uint_t _webrepl_read(mp_obj_t self_in, void *buf, mp_uint_t size, int *errcode);
|
||||
|
||||
STATIC mp_uint_t webrepl_read(mp_obj_t self_in, void *buf, mp_uint_t size, int *errcode) {
|
||||
mp_uint_t out_sz;
|
||||
do {
|
||||
out_sz = _webrepl_read(self_in, buf, size, errcode);
|
||||
} while (out_sz == -2);
|
||||
return out_sz;
|
||||
}
|
||||
|
||||
STATIC mp_uint_t _webrepl_read(mp_obj_t self_in, void *buf, mp_uint_t size, int *errcode) {
|
||||
// We know that os.dupterm always calls with size = 1
|
||||
assert(size == 1);
|
||||
mp_obj_webrepl_t *self = self_in;
|
||||
const mp_stream_p_t *sock_stream = mp_get_stream(self->sock);
|
||||
mp_uint_t out_sz = sock_stream->read(self->sock, buf, size, errcode);
|
||||
// DEBUG_printf("webrepl: Read %d initial bytes from websocket\n", out_sz);
|
||||
if (out_sz == 0 || out_sz == MP_STREAM_ERROR) {
|
||||
return out_sz;
|
||||
}
|
||||
|
||||
if (self->state == STATE_PASSWD) {
|
||||
char c = *(char *)buf;
|
||||
if (c == '\r' || c == '\n') {
|
||||
self->hdr.fname[self->data_to_recv] = 0;
|
||||
DEBUG_printf("webrepl: entered password: %s\n", self->hdr.fname);
|
||||
|
||||
if (strcmp(self->hdr.fname, webrepl_passwd) != 0) {
|
||||
write_webrepl_str(self->sock, SSTR(denied_prompt));
|
||||
return 0;
|
||||
}
|
||||
|
||||
self->state = STATE_NORMAL;
|
||||
self->data_to_recv = 0;
|
||||
write_webrepl_str(self->sock, SSTR(connected_prompt));
|
||||
} else if (self->data_to_recv < 10) {
|
||||
self->hdr.fname[self->data_to_recv++] = c;
|
||||
}
|
||||
return -2;
|
||||
}
|
||||
|
||||
// If last read data belonged to text record (== REPL)
|
||||
int err;
|
||||
if (sock_stream->ioctl(self->sock, MP_STREAM_GET_DATA_OPTS, 0, &err) == 1) {
|
||||
return out_sz;
|
||||
}
|
||||
|
||||
DEBUG_printf("webrepl: received bin data, hdr_to_recv: %d, data_to_recv=%d\n", self->hdr_to_recv, self->data_to_recv);
|
||||
|
||||
if (self->hdr_to_recv != 0) {
|
||||
char *p = (char *)&self->hdr + sizeof(self->hdr) - self->hdr_to_recv;
|
||||
*p++ = *(char *)buf;
|
||||
if (--self->hdr_to_recv != 0) {
|
||||
mp_uint_t hdr_sz = sock_stream->read(self->sock, p, self->hdr_to_recv, errcode);
|
||||
if (hdr_sz == MP_STREAM_ERROR) {
|
||||
return hdr_sz;
|
||||
}
|
||||
self->hdr_to_recv -= hdr_sz;
|
||||
if (self->hdr_to_recv != 0) {
|
||||
return -2;
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG_printf("webrepl: op: %d, file: %s, chunk @%x, sz=%d\n", self->hdr.type, self->hdr.fname, (uint32_t)self->hdr.offset, self->hdr.size);
|
||||
|
||||
handle_op(self);
|
||||
|
||||
return -2;
|
||||
}
|
||||
|
||||
if (self->data_to_recv != 0) {
|
||||
static byte filebuf[512];
|
||||
filebuf[0] = *(byte *)buf;
|
||||
mp_uint_t buf_sz = 1;
|
||||
if (--self->data_to_recv != 0) {
|
||||
size_t to_read = MIN(sizeof(filebuf) - 1, self->data_to_recv);
|
||||
mp_uint_t sz = sock_stream->read(self->sock, filebuf + 1, to_read, errcode);
|
||||
if (sz == MP_STREAM_ERROR) {
|
||||
return sz;
|
||||
}
|
||||
self->data_to_recv -= sz;
|
||||
buf_sz += sz;
|
||||
}
|
||||
|
||||
if (self->hdr.type == PUT_FILE) {
|
||||
DEBUG_printf("webrepl: Writing %lu bytes to file\n", buf_sz);
|
||||
int err;
|
||||
mp_uint_t res = mp_stream_write_exactly(self->cur_file, filebuf, buf_sz, &err);
|
||||
if (err != 0 || res != buf_sz) {
|
||||
assert(0);
|
||||
}
|
||||
} else if (self->hdr.type == GET_FILE) {
|
||||
assert(buf_sz == 1);
|
||||
assert(self->data_to_recv == 0);
|
||||
assert(filebuf[0] == 0);
|
||||
mp_uint_t out_sz = write_file_chunk(self);
|
||||
if (out_sz != 0) {
|
||||
self->data_to_recv = 1;
|
||||
}
|
||||
}
|
||||
|
||||
check_file_op_finished(self);
|
||||
|
||||
#ifdef MICROPY_PY_WEBREPL_DELAY
|
||||
// Some platforms may have broken drivers and easily gets
|
||||
// overloaded with modest traffic WebREPL file transfers
|
||||
// generate. The basic workaround is a crude rate control
|
||||
// done in such way.
|
||||
mp_hal_delay_ms(MICROPY_PY_WEBREPL_DELAY);
|
||||
#endif
|
||||
}
|
||||
|
||||
return -2;
|
||||
}
|
||||
|
||||
STATIC mp_uint_t webrepl_write(mp_obj_t self_in, const void *buf, mp_uint_t size, int *errcode) {
|
||||
mp_obj_webrepl_t *self = self_in;
|
||||
if (self->state == STATE_PASSWD) {
|
||||
// Don't forward output until passwd is entered
|
||||
return size;
|
||||
}
|
||||
const mp_stream_p_t *stream_p = mp_get_stream(self->sock);
|
||||
return stream_p->write(self->sock, buf, size, errcode);
|
||||
}
|
||||
|
||||
STATIC mp_uint_t webrepl_ioctl(mp_obj_t o_in, mp_uint_t request, uintptr_t arg, int *errcode) {
|
||||
mp_obj_webrepl_t *self = MP_OBJ_TO_PTR(o_in);
|
||||
(void)arg;
|
||||
switch (request) {
|
||||
case MP_STREAM_CLOSE:
|
||||
// TODO: This is a place to do cleanup
|
||||
mp_stream_close(self->sock);
|
||||
return 0;
|
||||
|
||||
default:
|
||||
*errcode = MP_EINVAL;
|
||||
return MP_STREAM_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
STATIC mp_obj_t webrepl_set_password(mp_obj_t passwd_in) {
|
||||
size_t len;
|
||||
const char *passwd = mp_obj_str_get_data(passwd_in, &len);
|
||||
if (len > sizeof(webrepl_passwd) - 1) {
|
||||
mp_raise_ValueError(NULL);
|
||||
}
|
||||
strcpy(webrepl_passwd, passwd);
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(webrepl_set_password_obj, webrepl_set_password);
|
||||
|
||||
STATIC const mp_rom_map_elem_t webrepl_locals_dict_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&mp_stream_close_obj) },
|
||||
};
|
||||
STATIC MP_DEFINE_CONST_DICT(webrepl_locals_dict, webrepl_locals_dict_table);
|
||||
|
||||
STATIC const mp_stream_p_t webrepl_stream_p = {
|
||||
MP_PROTO_IMPLEMENT(MP_QSTR_protocol_stream)
|
||||
.read = webrepl_read,
|
||||
.write = webrepl_write,
|
||||
.ioctl = webrepl_ioctl,
|
||||
};
|
||||
|
||||
STATIC const mp_obj_type_t webrepl_type = {
|
||||
{ &mp_type_type },
|
||||
.name = MP_QSTR__webrepl,
|
||||
.make_new = webrepl_make_new,
|
||||
.protocol = &webrepl_stream_p,
|
||||
.locals_dict = (mp_obj_dict_t *)&webrepl_locals_dict,
|
||||
};
|
||||
|
||||
STATIC const mp_rom_map_elem_t webrepl_module_globals_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR__webrepl) },
|
||||
{ MP_ROM_QSTR(MP_QSTR__webrepl), MP_ROM_PTR(&webrepl_type) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_password), MP_ROM_PTR(&webrepl_set_password_obj) },
|
||||
};
|
||||
|
||||
STATIC MP_DEFINE_CONST_DICT(webrepl_module_globals, webrepl_module_globals_table);
|
||||
|
||||
const mp_obj_module_t mp_module_webrepl = {
|
||||
.base = { &mp_type_module },
|
||||
.globals = (mp_obj_dict_t *)&webrepl_module_globals,
|
||||
};
|
||||
|
||||
#endif // MICROPY_PY_WEBREPL
|
|
@ -23,10 +23,10 @@ static char unescape(char c) {
|
|||
return '\n';
|
||||
case 'r':
|
||||
return '\r';
|
||||
case 't':
|
||||
return '\t';
|
||||
case 'v':
|
||||
return '\v';
|
||||
case 'x':
|
||||
return '\\';
|
||||
default:
|
||||
return c;
|
||||
}
|
||||
|
@ -88,17 +88,22 @@ static const char *_compilecode(const char *re, ByteProg *prog, int sizecode)
|
|||
prog->len++;
|
||||
for (cnt = 0; *re != ']'; re++, cnt++) {
|
||||
if (!*re) return NULL;
|
||||
const char *b = re;
|
||||
if (*re == '\\') {
|
||||
re += 1;
|
||||
if (!*re) return NULL; // Trailing backslash
|
||||
EMIT(PC++, unescape(*re));
|
||||
} else {
|
||||
EMIT(PC++, *re);
|
||||
}
|
||||
if (re[1] == '-' && re[2] != ']') {
|
||||
re += 2;
|
||||
} else {
|
||||
re = b;
|
||||
}
|
||||
if (*re == '\\') {
|
||||
re += 1;
|
||||
if (!*re) return NULL; // Trailing backslash
|
||||
EMIT(PC++, unescape(*re));
|
||||
} else {
|
||||
EMIT(PC++, *re);
|
||||
|
@ -250,11 +255,21 @@ int re1_5_compilecode(ByteProg *prog, const char *re)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#if 0
|
||||
#if defined(DEBUG_COMPILECODE)
|
||||
#include <assert.h>
|
||||
void re1_5_fatal(char *x) {
|
||||
fprintf(stderr, "%s\n", x);
|
||||
abort();
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int pc = 0;
|
||||
ByteProg *code = re1_5_compilecode(argv[1]);
|
||||
char *re_str = argv[1];
|
||||
int size = re1_5_sizecode(re_str);
|
||||
ByteProg *code = malloc(sizeof(ByteProg) + size);
|
||||
int ret = re1_5_compilecode(code, re_str);
|
||||
if (ret == 0) {
|
||||
re1_5_dumpcode(code);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
#ifndef _RE1_5_REGEXP__H
|
||||
#define _RE1_5_REGEXP__H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
# MicroPython uasyncio module
|
||||
# MIT license; Copyright (c) 2019 Damien P. George
|
||||
|
||||
from .core import *
|
||||
|
||||
__version__ = (3, 0, 0)
|
||||
|
||||
_attrs = {
|
||||
"wait_for": "funcs",
|
||||
"wait_for_ms": "funcs",
|
||||
"gather": "funcs",
|
||||
"Event": "event",
|
||||
"Lock": "lock",
|
||||
"open_connection": "stream",
|
||||
"start_server": "stream",
|
||||
"StreamReader": "stream",
|
||||
"StreamWriter": "stream",
|
||||
}
|
||||
|
||||
# Lazy loader, effectively does:
|
||||
# global attr
|
||||
# from .mod import attr
|
||||
def __getattr__(attr):
|
||||
mod = _attrs.get(attr, None)
|
||||
if mod is None:
|
||||
raise AttributeError(attr)
|
||||
value = getattr(__import__(mod, None, None, True, 1), attr)
|
||||
globals()[attr] = value
|
||||
return value
|
|
@ -0,0 +1,277 @@
|
|||
# MicroPython uasyncio module
|
||||
# MIT license; Copyright (c) 2019 Damien P. George
|
||||
|
||||
from time import ticks_ms as ticks, ticks_diff, ticks_add
|
||||
import sys, select
|
||||
|
||||
# Import TaskQueue and Task, preferring built-in C code over Python code
|
||||
try:
|
||||
from _uasyncio import TaskQueue, Task
|
||||
except:
|
||||
from .task import TaskQueue, Task
|
||||
|
||||
|
||||
################################################################################
|
||||
# Exceptions
|
||||
|
||||
|
||||
class CancelledError(BaseException):
|
||||
pass
|
||||
|
||||
|
||||
class TimeoutError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
# Used when calling Loop.call_exception_handler
|
||||
_exc_context = {"message": "Task exception wasn't retrieved", "exception": None, "future": None}
|
||||
|
||||
|
||||
################################################################################
|
||||
# Sleep functions
|
||||
|
||||
# "Yield" once, then raise StopIteration
|
||||
class SingletonGenerator:
|
||||
def __init__(self):
|
||||
self.state = None
|
||||
self.exc = StopIteration()
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def __next__(self):
|
||||
if self.state is not None:
|
||||
_task_queue.push_sorted(cur_task, self.state)
|
||||
self.state = None
|
||||
return None
|
||||
else:
|
||||
self.exc.__traceback__ = None
|
||||
raise self.exc
|
||||
|
||||
|
||||
# Pause task execution for the given time (integer in milliseconds, uPy extension)
|
||||
# Use a SingletonGenerator to do it without allocating on the heap
|
||||
def sleep_ms(t, sgen=SingletonGenerator()):
|
||||
assert sgen.state is None
|
||||
sgen.state = ticks_add(ticks(), max(0, t))
|
||||
return sgen
|
||||
|
||||
|
||||
# Pause task execution for the given time (in seconds)
|
||||
def sleep(t):
|
||||
return sleep_ms(int(t * 1000))
|
||||
|
||||
|
||||
################################################################################
|
||||
# Queue and poller for stream IO
|
||||
|
||||
|
||||
class IOQueue:
|
||||
def __init__(self):
|
||||
self.poller = select.poll()
|
||||
self.map = {} # maps id(stream) to [task_waiting_read, task_waiting_write, stream]
|
||||
|
||||
def _enqueue(self, s, idx):
|
||||
if id(s) not in self.map:
|
||||
entry = [None, None, s]
|
||||
entry[idx] = cur_task
|
||||
self.map[id(s)] = entry
|
||||
self.poller.register(s, select.POLLIN if idx == 0 else select.POLLOUT)
|
||||
else:
|
||||
sm = self.map[id(s)]
|
||||
assert sm[idx] is None
|
||||
assert sm[1 - idx] is not None
|
||||
sm[idx] = cur_task
|
||||
self.poller.modify(s, select.POLLIN | select.POLLOUT)
|
||||
# Link task to this IOQueue so it can be removed if needed
|
||||
cur_task.data = self
|
||||
|
||||
def _dequeue(self, s):
|
||||
del self.map[id(s)]
|
||||
self.poller.unregister(s)
|
||||
|
||||
def queue_read(self, s):
|
||||
self._enqueue(s, 0)
|
||||
|
||||
def queue_write(self, s):
|
||||
self._enqueue(s, 1)
|
||||
|
||||
def remove(self, task):
|
||||
while True:
|
||||
del_s = None
|
||||
for k in self.map: # Iterate without allocating on the heap
|
||||
q0, q1, s = self.map[k]
|
||||
if q0 is task or q1 is task:
|
||||
del_s = s
|
||||
break
|
||||
if del_s is not None:
|
||||
self._dequeue(s)
|
||||
else:
|
||||
break
|
||||
|
||||
def wait_io_event(self, dt):
|
||||
for s, ev in self.poller.ipoll(dt):
|
||||
sm = self.map[id(s)]
|
||||
# print('poll', s, sm, ev)
|
||||
if ev & ~select.POLLOUT and sm[0] is not None:
|
||||
# POLLIN or error
|
||||
_task_queue.push_head(sm[0])
|
||||
sm[0] = None
|
||||
if ev & ~select.POLLIN and sm[1] is not None:
|
||||
# POLLOUT or error
|
||||
_task_queue.push_head(sm[1])
|
||||
sm[1] = None
|
||||
if sm[0] is None and sm[1] is None:
|
||||
self._dequeue(s)
|
||||
elif sm[0] is None:
|
||||
self.poller.modify(s, select.POLLOUT)
|
||||
else:
|
||||
self.poller.modify(s, select.POLLIN)
|
||||
|
||||
|
||||
################################################################################
|
||||
# Main run loop
|
||||
|
||||
# Ensure the awaitable is a task
|
||||
def _promote_to_task(aw):
|
||||
return aw if isinstance(aw, Task) else create_task(aw)
|
||||
|
||||
|
||||
# Create and schedule a new task from a coroutine
|
||||
def create_task(coro):
|
||||
if not hasattr(coro, "send"):
|
||||
raise TypeError("coroutine expected")
|
||||
t = Task(coro, globals())
|
||||
_task_queue.push_head(t)
|
||||
return t
|
||||
|
||||
|
||||
# Keep scheduling tasks until there are none left to schedule
|
||||
def run_until_complete(main_task=None):
|
||||
global cur_task
|
||||
excs_all = (CancelledError, Exception) # To prevent heap allocation in loop
|
||||
excs_stop = (CancelledError, StopIteration) # To prevent heap allocation in loop
|
||||
while True:
|
||||
# Wait until the head of _task_queue is ready to run
|
||||
dt = 1
|
||||
while dt > 0:
|
||||
dt = -1
|
||||
t = _task_queue.peek()
|
||||
if t:
|
||||
# A task waiting on _task_queue; "ph_key" is time to schedule task at
|
||||
dt = max(0, ticks_diff(t.ph_key, ticks()))
|
||||
elif not _io_queue.map:
|
||||
# No tasks can be woken so finished running
|
||||
return
|
||||
# print('(poll {})'.format(dt), len(_io_queue.map))
|
||||
_io_queue.wait_io_event(dt)
|
||||
|
||||
# Get next task to run and continue it
|
||||
t = _task_queue.pop_head()
|
||||
cur_task = t
|
||||
try:
|
||||
# Continue running the coroutine, it's responsible for rescheduling itself
|
||||
exc = t.data
|
||||
if not exc:
|
||||
t.coro.send(None)
|
||||
else:
|
||||
t.data = None
|
||||
t.coro.throw(exc)
|
||||
except excs_all as er:
|
||||
# Check the task is not on any event queue
|
||||
assert t.data is None
|
||||
# This task is done, check if it's the main task and then loop should stop
|
||||
if t is main_task:
|
||||
if isinstance(er, StopIteration):
|
||||
return er.value
|
||||
raise er
|
||||
# Save return value of coro to pass up to caller
|
||||
t.data = er
|
||||
# Schedule any other tasks waiting on the completion of this task
|
||||
waiting = False
|
||||
if hasattr(t, "waiting"):
|
||||
while t.waiting.peek():
|
||||
_task_queue.push_head(t.waiting.pop_head())
|
||||
waiting = True
|
||||
t.waiting = None # Free waiting queue head
|
||||
# Print out exception for detached tasks
|
||||
if not waiting and not isinstance(er, excs_stop):
|
||||
_exc_context["exception"] = er
|
||||
_exc_context["future"] = t
|
||||
Loop.call_exception_handler(_exc_context)
|
||||
# Indicate task is done
|
||||
t.coro = None
|
||||
|
||||
|
||||
# Create a new task from a coroutine and run it until it finishes
|
||||
def run(coro):
|
||||
return run_until_complete(create_task(coro))
|
||||
|
||||
|
||||
################################################################################
|
||||
# Event loop wrapper
|
||||
|
||||
|
||||
async def _stopper():
|
||||
pass
|
||||
|
||||
|
||||
_stop_task = None
|
||||
|
||||
|
||||
class Loop:
|
||||
_exc_handler = None
|
||||
|
||||
def create_task(coro):
|
||||
return create_task(coro)
|
||||
|
||||
def run_forever():
|
||||
global _stop_task
|
||||
_stop_task = Task(_stopper(), globals())
|
||||
run_until_complete(_stop_task)
|
||||
# TODO should keep running until .stop() is called, even if there're no tasks left
|
||||
|
||||
def run_until_complete(aw):
|
||||
return run_until_complete(_promote_to_task(aw))
|
||||
|
||||
def stop():
|
||||
global _stop_task
|
||||
if _stop_task is not None:
|
||||
_task_queue.push_head(_stop_task)
|
||||
# If stop() is called again, do nothing
|
||||
_stop_task = None
|
||||
|
||||
def close():
|
||||
pass
|
||||
|
||||
def set_exception_handler(handler):
|
||||
Loop._exc_handler = handler
|
||||
|
||||
def get_exception_handler():
|
||||
return Loop._exc_handler
|
||||
|
||||
def default_exception_handler(loop, context):
|
||||
print(context["message"])
|
||||
print("future:", context["future"], "coro=", context["future"].coro)
|
||||
sys.print_exception(context["exception"])
|
||||
|
||||
def call_exception_handler(context):
|
||||
(Loop._exc_handler or Loop.default_exception_handler)(Loop, context)
|
||||
|
||||
|
||||
# The runq_len and waitq_len arguments are for legacy uasyncio compatibility
|
||||
def get_event_loop(runq_len=0, waitq_len=0):
|
||||
return Loop
|
||||
|
||||
|
||||
def new_event_loop():
|
||||
global _task_queue, _io_queue
|
||||
# TaskQueue of Task instances
|
||||
_task_queue = TaskQueue()
|
||||
# Task queue and poller for stream IO
|
||||
_io_queue = IOQueue()
|
||||
return Loop
|
||||
|
||||
|
||||
# Initialise default event loop
|
||||
new_event_loop()
|
|
@ -0,0 +1,31 @@
|
|||
# MicroPython uasyncio module
|
||||
# MIT license; Copyright (c) 2019-2020 Damien P. George
|
||||
|
||||
from . import core
|
||||
|
||||
# Event class for primitive events that can be waited on, set, and cleared
|
||||
class Event:
|
||||
def __init__(self):
|
||||
self.state = False # False=unset; True=set
|
||||
self.waiting = core.TaskQueue() # Queue of Tasks waiting on completion of this event
|
||||
|
||||
def is_set(self):
|
||||
return self.state
|
||||
|
||||
def set(self):
|
||||
# Event becomes set, schedule any tasks waiting on it
|
||||
while self.waiting.peek():
|
||||
core._task_queue.push_head(self.waiting.pop_head())
|
||||
self.state = True
|
||||
|
||||
def clear(self):
|
||||
self.state = False
|
||||
|
||||
async def wait(self):
|
||||
if not self.state:
|
||||
# Event not set, put the calling task on the event's waiting queue
|
||||
self.waiting.push_head(core.cur_task)
|
||||
# Set calling task's data to the event's queue so it can be removed if needed
|
||||
core.cur_task.data = self.waiting
|
||||
yield
|
||||
return True
|
|
@ -0,0 +1,54 @@
|
|||
# MicroPython uasyncio module
|
||||
# MIT license; Copyright (c) 2019-2020 Damien P. George
|
||||
|
||||
from . import core
|
||||
|
||||
|
||||
async def wait_for(aw, timeout, sleep=core.sleep):
|
||||
aw = core._promote_to_task(aw)
|
||||
if timeout is None:
|
||||
return await aw
|
||||
|
||||
def cancel(aw, timeout, sleep):
|
||||
await sleep(timeout)
|
||||
aw.cancel()
|
||||
|
||||
cancel_task = core.create_task(cancel(aw, timeout, sleep))
|
||||
try:
|
||||
ret = await aw
|
||||
except core.CancelledError:
|
||||
# Ignore CancelledError from aw, it's probably due to timeout
|
||||
pass
|
||||
finally:
|
||||
# Cancel the "cancel" task if it's still active (optimisation instead of cancel_task.cancel())
|
||||
if cancel_task.coro is not None:
|
||||
core._task_queue.remove(cancel_task)
|
||||
if cancel_task.coro is None:
|
||||
# Cancel task ran to completion, ie there was a timeout
|
||||
raise core.TimeoutError
|
||||
return ret
|
||||
|
||||
|
||||
def wait_for_ms(aw, timeout):
|
||||
return wait_for(aw, timeout, core.sleep_ms)
|
||||
|
||||
|
||||
async def gather(*aws, return_exceptions=False):
|
||||
ts = [core._promote_to_task(aw) for aw in aws]
|
||||
for i in range(len(ts)):
|
||||
try:
|
||||
# TODO handle cancel of gather itself
|
||||
# if ts[i].coro:
|
||||
# iter(ts[i]).waiting.push_head(cur_task)
|
||||
# try:
|
||||
# yield
|
||||
# except CancelledError as er:
|
||||
# # cancel all waiting tasks
|
||||
# raise er
|
||||
ts[i] = await ts[i]
|
||||
except Exception as er:
|
||||
if return_exceptions:
|
||||
ts[i] = er
|
||||
else:
|
||||
raise er
|
||||
return ts
|
|
@ -0,0 +1,53 @@
|
|||
# MicroPython uasyncio module
|
||||
# MIT license; Copyright (c) 2019-2020 Damien P. George
|
||||
|
||||
from . import core
|
||||
|
||||
# Lock class for primitive mutex capability
|
||||
class Lock:
|
||||
def __init__(self):
|
||||
# The state can take the following values:
|
||||
# - 0: unlocked
|
||||
# - 1: locked
|
||||
# - <Task>: unlocked but this task has been scheduled to acquire the lock next
|
||||
self.state = 0
|
||||
# Queue of Tasks waiting to acquire this Lock
|
||||
self.waiting = core.TaskQueue()
|
||||
|
||||
def locked(self):
|
||||
return self.state == 1
|
||||
|
||||
def release(self):
|
||||
if self.state != 1:
|
||||
raise RuntimeError("Lock not acquired")
|
||||
if self.waiting.peek():
|
||||
# Task(s) waiting on lock, schedule next Task
|
||||
self.state = self.waiting.pop_head()
|
||||
core._task_queue.push_head(self.state)
|
||||
else:
|
||||
# No Task waiting so unlock
|
||||
self.state = 0
|
||||
|
||||
async def acquire(self):
|
||||
if self.state != 0:
|
||||
# Lock unavailable, put the calling Task on the waiting queue
|
||||
self.waiting.push_head(core.cur_task)
|
||||
# Set calling task's data to the lock's queue so it can be removed if needed
|
||||
core.cur_task.data = self.waiting
|
||||
try:
|
||||
yield
|
||||
except core.CancelledError as er:
|
||||
if self.state == core.cur_task:
|
||||
# Cancelled while pending on resume, schedule next waiting Task
|
||||
self.state = 1
|
||||
self.release()
|
||||
raise er
|
||||
# Lock available, set it as locked
|
||||
self.state = 1
|
||||
return True
|
||||
|
||||
async def __aenter__(self):
|
||||
return await self.acquire()
|
||||
|
||||
async def __aexit__(self, exc_type, exc, tb):
|
||||
return self.release()
|
|
@ -0,0 +1,13 @@
|
|||
# This list of frozen files doesn't include task.py because that's provided by the C module.
|
||||
freeze(
|
||||
"..",
|
||||
(
|
||||
"uasyncio/__init__.py",
|
||||
"uasyncio/core.py",
|
||||
"uasyncio/event.py",
|
||||
"uasyncio/funcs.py",
|
||||
"uasyncio/lock.py",
|
||||
"uasyncio/stream.py",
|
||||
),
|
||||
opt=3,
|
||||
)
|
|
@ -0,0 +1,158 @@
|
|||
# MicroPython uasyncio module
|
||||
# MIT license; Copyright (c) 2019-2020 Damien P. George
|
||||
|
||||
from . import core
|
||||
|
||||
|
||||
class Stream:
|
||||
def __init__(self, s, e={}):
|
||||
self.s = s
|
||||
self.e = e
|
||||
self.out_buf = b""
|
||||
|
||||
def get_extra_info(self, v):
|
||||
return self.e[v]
|
||||
|
||||
async def __aenter__(self):
|
||||
return self
|
||||
|
||||
async def __aexit__(self, exc_type, exc, tb):
|
||||
await self.close()
|
||||
|
||||
def close(self):
|
||||
pass
|
||||
|
||||
async def wait_closed(self):
|
||||
# TODO yield?
|
||||
self.s.close()
|
||||
|
||||
async def read(self, n):
|
||||
yield core._io_queue.queue_read(self.s)
|
||||
return self.s.read(n)
|
||||
|
||||
async def readexactly(self, n):
|
||||
r = b""
|
||||
while n:
|
||||
yield core._io_queue.queue_read(self.s)
|
||||
r2 = self.s.read(n)
|
||||
if r2 is not None:
|
||||
if not len(r2):
|
||||
raise EOFError
|
||||
r += r2
|
||||
n -= len(r2)
|
||||
return r
|
||||
|
||||
async def readline(self):
|
||||
l = b""
|
||||
while True:
|
||||
yield core._io_queue.queue_read(self.s)
|
||||
l2 = self.s.readline() # may do multiple reads but won't block
|
||||
l += l2
|
||||
if not l2 or l[-1] == 10: # \n (check l in case l2 is str)
|
||||
return l
|
||||
|
||||
def write(self, buf):
|
||||
self.out_buf += buf
|
||||
|
||||
async def drain(self):
|
||||
mv = memoryview(self.out_buf)
|
||||
off = 0
|
||||
while off < len(mv):
|
||||
yield core._io_queue.queue_write(self.s)
|
||||
ret = self.s.write(mv[off:])
|
||||
if ret is not None:
|
||||
off += ret
|
||||
self.out_buf = b""
|
||||
|
||||
|
||||
# Stream can be used for both reading and writing to save code size
|
||||
StreamReader = Stream
|
||||
StreamWriter = Stream
|
||||
|
||||
|
||||
# Create a TCP stream connection to a remote host
|
||||
async def open_connection(host, port):
|
||||
from uerrno import EINPROGRESS
|
||||
import usocket as socket
|
||||
|
||||
ai = socket.getaddrinfo(host, port)[0] # TODO this is blocking!
|
||||
s = socket.socket()
|
||||
s.setblocking(False)
|
||||
ss = Stream(s)
|
||||
try:
|
||||
s.connect(ai[-1])
|
||||
except OSError as er:
|
||||
if er.args[0] != EINPROGRESS:
|
||||
raise er
|
||||
yield core._io_queue.queue_write(s)
|
||||
return ss, ss
|
||||
|
||||
|
||||
# Class representing a TCP stream server, can be closed and used in "async with"
|
||||
class Server:
|
||||
async def __aenter__(self):
|
||||
return self
|
||||
|
||||
async def __aexit__(self, exc_type, exc, tb):
|
||||
self.close()
|
||||
await self.wait_closed()
|
||||
|
||||
def close(self):
|
||||
self.task.cancel()
|
||||
|
||||
async def wait_closed(self):
|
||||
await self.task
|
||||
|
||||
async def _serve(self, cb, host, port, backlog):
|
||||
import usocket as socket
|
||||
|
||||
ai = socket.getaddrinfo(host, port)[0] # TODO this is blocking!
|
||||
s = socket.socket()
|
||||
s.setblocking(False)
|
||||
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
s.bind(ai[-1])
|
||||
s.listen(backlog)
|
||||
self.task = core.cur_task
|
||||
# Accept incoming connections
|
||||
while True:
|
||||
try:
|
||||
yield core._io_queue.queue_read(s)
|
||||
except core.CancelledError:
|
||||
# Shutdown server
|
||||
s.close()
|
||||
return
|
||||
try:
|
||||
s2, addr = s.accept()
|
||||
except:
|
||||
# Ignore a failed accept
|
||||
continue
|
||||
s2.setblocking(False)
|
||||
s2s = Stream(s2, {"peername": addr})
|
||||
core.create_task(cb(s2s, s2s))
|
||||
|
||||
|
||||
# Helper function to start a TCP stream server, running as a new task
|
||||
# TODO could use an accept-callback on socket read activity instead of creating a task
|
||||
async def start_server(cb, host, port, backlog=5):
|
||||
s = Server()
|
||||
core.create_task(s._serve(cb, host, port, backlog))
|
||||
return s
|
||||
|
||||
|
||||
################################################################################
|
||||
# Legacy uasyncio compatibility
|
||||
|
||||
|
||||
async def stream_awrite(self, buf, off=0, sz=-1):
|
||||
if off != 0 or sz != -1:
|
||||
buf = memoryview(buf)
|
||||
if sz == -1:
|
||||
sz = len(buf)
|
||||
buf = buf[off : off + sz]
|
||||
self.write(buf)
|
||||
await self.drain()
|
||||
|
||||
|
||||
Stream.aclose = Stream.wait_closed
|
||||
Stream.awrite = stream_awrite
|
||||
Stream.awritestr = stream_awrite # TODO explicitly convert to bytes?
|
|
@ -0,0 +1,168 @@
|
|||
# MicroPython uasyncio module
|
||||
# MIT license; Copyright (c) 2019-2020 Damien P. George
|
||||
|
||||
# This file contains the core TaskQueue based on a pairing heap, and the core Task class.
|
||||
# They can optionally be replaced by C implementations.
|
||||
|
||||
from . import core
|
||||
|
||||
|
||||
# pairing-heap meld of 2 heaps; O(1)
|
||||
def ph_meld(h1, h2):
|
||||
if h1 is None:
|
||||
return h2
|
||||
if h2 is None:
|
||||
return h1
|
||||
lt = core.ticks_diff(h1.ph_key, h2.ph_key) < 0
|
||||
if lt:
|
||||
if h1.ph_child is None:
|
||||
h1.ph_child = h2
|
||||
else:
|
||||
h1.ph_child_last.ph_next = h2
|
||||
h1.ph_child_last = h2
|
||||
h2.ph_next = None
|
||||
h2.ph_rightmost_parent = h1
|
||||
return h1
|
||||
else:
|
||||
h1.ph_next = h2.ph_child
|
||||
h2.ph_child = h1
|
||||
if h1.ph_next is None:
|
||||
h2.ph_child_last = h1
|
||||
h1.ph_rightmost_parent = h2
|
||||
return h2
|
||||
|
||||
|
||||
# pairing-heap pairing operation; amortised O(log N)
|
||||
def ph_pairing(child):
|
||||
heap = None
|
||||
while child is not None:
|
||||
n1 = child
|
||||
child = child.ph_next
|
||||
n1.ph_next = None
|
||||
if child is not None:
|
||||
n2 = child
|
||||
child = child.ph_next
|
||||
n2.ph_next = None
|
||||
n1 = ph_meld(n1, n2)
|
||||
heap = ph_meld(heap, n1)
|
||||
return heap
|
||||
|
||||
|
||||
# pairing-heap delete of a node; stable, amortised O(log N)
|
||||
def ph_delete(heap, node):
|
||||
if node is heap:
|
||||
child = heap.ph_child
|
||||
node.ph_child = None
|
||||
return ph_pairing(child)
|
||||
# Find parent of node
|
||||
parent = node
|
||||
while parent.ph_next is not None:
|
||||
parent = parent.ph_next
|
||||
parent = parent.ph_rightmost_parent
|
||||
# Replace node with pairing of its children
|
||||
if node is parent.ph_child and node.ph_child is None:
|
||||
parent.ph_child = node.ph_next
|
||||
node.ph_next = None
|
||||
return heap
|
||||
elif node is parent.ph_child:
|
||||
child = node.ph_child
|
||||
next = node.ph_next
|
||||
node.ph_child = None
|
||||
node.ph_next = None
|
||||
node = ph_pairing(child)
|
||||
parent.ph_child = node
|
||||
else:
|
||||
n = parent.ph_child
|
||||
while node is not n.ph_next:
|
||||
n = n.ph_next
|
||||
child = node.ph_child
|
||||
next = node.ph_next
|
||||
node.ph_child = None
|
||||
node.ph_next = None
|
||||
node = ph_pairing(child)
|
||||
if node is None:
|
||||
node = n
|
||||
else:
|
||||
n.ph_next = node
|
||||
node.ph_next = next
|
||||
if next is None:
|
||||
node.ph_rightmost_parent = parent
|
||||
parent.ph_child_last = node
|
||||
return heap
|
||||
|
||||
|
||||
# TaskQueue class based on the above pairing-heap functions.
|
||||
class TaskQueue:
|
||||
def __init__(self):
|
||||
self.heap = None
|
||||
|
||||
def peek(self):
|
||||
return self.heap
|
||||
|
||||
def push_sorted(self, v, key):
|
||||
v.data = None
|
||||
v.ph_key = key
|
||||
v.ph_child = None
|
||||
v.ph_next = None
|
||||
self.heap = ph_meld(v, self.heap)
|
||||
|
||||
def push_head(self, v):
|
||||
self.push_sorted(v, core.ticks())
|
||||
|
||||
def pop_head(self):
|
||||
v = self.heap
|
||||
self.heap = ph_pairing(self.heap.ph_child)
|
||||
return v
|
||||
|
||||
def remove(self, v):
|
||||
self.heap = ph_delete(self.heap, v)
|
||||
|
||||
|
||||
# Task class representing a coroutine, can be waited on and cancelled.
|
||||
class Task:
|
||||
def __init__(self, coro, globals=None):
|
||||
self.coro = coro # Coroutine of this Task
|
||||
self.data = None # General data for queue it is waiting on
|
||||
self.ph_key = 0 # Pairing heap
|
||||
self.ph_child = None # Paring heap
|
||||
self.ph_child_last = None # Paring heap
|
||||
self.ph_next = None # Paring heap
|
||||
self.ph_rightmost_parent = None # Paring heap
|
||||
|
||||
def __iter__(self):
|
||||
if not hasattr(self, "waiting"):
|
||||
# Lazily allocated head of linked list of Tasks waiting on completion of this task.
|
||||
self.waiting = TaskQueue()
|
||||
return self
|
||||
|
||||
def __next__(self):
|
||||
if not self.coro:
|
||||
# Task finished, raise return value to caller so it can continue.
|
||||
raise self.data
|
||||
else:
|
||||
# Put calling task on waiting queue.
|
||||
self.waiting.push_head(core.cur_task)
|
||||
# Set calling task's data to this task that it waits on, to double-link it.
|
||||
core.cur_task.data = self
|
||||
|
||||
def cancel(self):
|
||||
# Check if task is already finished.
|
||||
if self.coro is None:
|
||||
return False
|
||||
# Can't cancel self (not supported yet).
|
||||
if self is core.cur_task:
|
||||
raise RuntimeError("can't cancel self")
|
||||
# If Task waits on another task then forward the cancel to the one it's waiting on.
|
||||
while isinstance(self.data, Task):
|
||||
self = self.data
|
||||
# Reschedule Task as a cancelled task.
|
||||
if hasattr(self.data, "remove"):
|
||||
# Not on the main running queue, remove the task from the queue it's on.
|
||||
self.data.remove(self)
|
||||
core._task_queue.push_head(self)
|
||||
elif core.ticks_diff(self.ph_key, core.ticks()) > 0:
|
||||
# On the main running queue but scheduled in the future, so bring it forward to now.
|
||||
core._task_queue.remove(self)
|
||||
core._task_queue.push_head(self)
|
||||
self.data = core.CancelledError
|
||||
return True
|
|
@ -1,142 +0,0 @@
|
|||
// Copyright (c) 2016 Paul Sokolovsky
|
||||
// SPDX-FileCopyrightText: 2014 MicroPython & CircuitPython contributors (https://github.com/adafruit/circuitpython/graphs/contributors)
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2017 Damien P. George
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <string.h>
|
||||
#include "py/mpconfig.h"
|
||||
|
||||
#include "py/runtime.h"
|
||||
#include "py/objtuple.h"
|
||||
#include "py/objarray.h"
|
||||
#include "py/stream.h"
|
||||
#include "extmod/misc.h"
|
||||
#include "lib/utils/interrupt_char.h"
|
||||
|
||||
#include "supervisor/shared/translate.h"
|
||||
|
||||
#if MICROPY_PY_OS_DUPTERM
|
||||
|
||||
void mp_uos_deactivate(size_t dupterm_idx, const char *msg, mp_obj_t exc) {
|
||||
mp_obj_t term = MP_STATE_VM(dupterm_objs[dupterm_idx]);
|
||||
MP_STATE_VM(dupterm_objs[dupterm_idx]) = MP_OBJ_NULL;
|
||||
mp_printf(&mp_plat_print, msg);
|
||||
if (exc != MP_OBJ_NULL) {
|
||||
mp_obj_print_exception(&mp_plat_print, exc);
|
||||
}
|
||||
nlr_buf_t nlr;
|
||||
if (nlr_push(&nlr) == 0) {
|
||||
mp_stream_close(term);
|
||||
nlr_pop();
|
||||
} else {
|
||||
// Ignore any errors during stream closing
|
||||
}
|
||||
}
|
||||
|
||||
int mp_uos_dupterm_rx_chr(void) {
|
||||
for (size_t idx = 0; idx < MICROPY_PY_OS_DUPTERM; ++idx) {
|
||||
if (MP_STATE_VM(dupterm_objs[idx]) == MP_OBJ_NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
#if MICROPY_PY_UOS_DUPTERM_BUILTIN_STREAM
|
||||
if (mp_uos_dupterm_is_builtin_stream(MP_STATE_VM(dupterm_objs[idx]))) {
|
||||
byte buf[1];
|
||||
int errcode = 0;
|
||||
const mp_stream_p_t *stream_p = mp_get_stream(MP_STATE_VM(dupterm_objs[idx]));
|
||||
mp_uint_t out_sz = stream_p->read(MP_STATE_VM(dupterm_objs[idx]), buf, 1, &errcode);
|
||||
if (errcode == 0 && out_sz != 0) {
|
||||
return buf[0];
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
nlr_buf_t nlr;
|
||||
if (nlr_push(&nlr) == 0) {
|
||||
byte buf[1];
|
||||
int errcode;
|
||||
const mp_stream_p_t *stream_p = mp_get_stream(MP_STATE_VM(dupterm_objs[idx]));
|
||||
mp_uint_t out_sz = stream_p->read(MP_STATE_VM(dupterm_objs[idx]), buf, 1, &errcode);
|
||||
if (out_sz == 0) {
|
||||
nlr_pop();
|
||||
mp_uos_deactivate(idx, "dupterm: EOF received, deactivating\n", MP_OBJ_NULL);
|
||||
} else if (out_sz == MP_STREAM_ERROR) {
|
||||
// errcode is valid
|
||||
if (mp_is_nonblocking_error(errcode)) {
|
||||
nlr_pop();
|
||||
} else {
|
||||
mp_raise_OSError(errcode);
|
||||
}
|
||||
} else {
|
||||
// read 1 byte
|
||||
nlr_pop();
|
||||
if (buf[0] == mp_interrupt_char) {
|
||||
// Signal keyboard interrupt to be raised as soon as the VM resumes
|
||||
mp_keyboard_interrupt();
|
||||
return -2;
|
||||
}
|
||||
return buf[0];
|
||||
}
|
||||
} else {
|
||||
mp_uos_deactivate(idx, "dupterm: Exception in read() method, deactivating: ", MP_OBJ_FROM_PTR(nlr.ret_val));
|
||||
}
|
||||
}
|
||||
|
||||
// No chars available
|
||||
return -1;
|
||||
}
|
||||
|
||||
void mp_uos_dupterm_tx_strn(const char *str, size_t len) {
|
||||
for (size_t idx = 0; idx < MICROPY_PY_OS_DUPTERM; ++idx) {
|
||||
if (MP_STATE_VM(dupterm_objs[idx]) == MP_OBJ_NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
#if MICROPY_PY_UOS_DUPTERM_BUILTIN_STREAM
|
||||
if (mp_uos_dupterm_is_builtin_stream(MP_STATE_VM(dupterm_objs[idx]))) {
|
||||
int errcode = 0;
|
||||
const mp_stream_p_t *stream_p = mp_get_stream(MP_STATE_VM(dupterm_objs[idx]));
|
||||
stream_p->write(MP_STATE_VM(dupterm_objs[idx]), str, len, &errcode);
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
nlr_buf_t nlr;
|
||||
if (nlr_push(&nlr) == 0) {
|
||||
mp_stream_write(MP_STATE_VM(dupterm_objs[idx]), str, len, MP_STREAM_RW_WRITE);
|
||||
nlr_pop();
|
||||
} else {
|
||||
mp_uos_deactivate(idx, "dupterm: Exception in write() method, deactivating: ", MP_OBJ_FROM_PTR(nlr.ret_val));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
STATIC mp_obj_t mp_uos_dupterm(size_t n_args, const mp_obj_t *args) {
|
||||
mp_int_t idx = 0;
|
||||
if (n_args == 2) {
|
||||
idx = mp_obj_get_int(args[1]);
|
||||
}
|
||||
|
||||
if (idx < 0 || idx >= MICROPY_PY_OS_DUPTERM) {
|
||||
mp_raise_ValueError(translate("invalid dupterm index"));
|
||||
}
|
||||
|
||||
mp_obj_t previous_obj = MP_STATE_VM(dupterm_objs[idx]);
|
||||
if (previous_obj == MP_OBJ_NULL) {
|
||||
previous_obj = mp_const_none;
|
||||
}
|
||||
if (args[0] == mp_const_none) {
|
||||
MP_STATE_VM(dupterm_objs[idx]) = MP_OBJ_NULL;
|
||||
} else {
|
||||
mp_get_stream_raise(args[0], MP_STREAM_OP_READ | MP_STREAM_OP_WRITE | MP_STREAM_OP_IOCTL);
|
||||
MP_STATE_VM(dupterm_objs[idx]) = args[0];
|
||||
}
|
||||
|
||||
return previous_obj;
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_uos_dupterm_obj, 1, 2, mp_uos_dupterm);
|
||||
|
||||
#endif
|
79
extmod/vfs.c
79
extmod/vfs.c
|
@ -17,6 +17,10 @@
|
|||
#include "extmod/vfs_fat.h"
|
||||
#endif
|
||||
|
||||
#if MICROPY_VFS_LFS1 || MICROPY_VFS_LFS2
|
||||
#include "extmod/vfs_lfs.h"
|
||||
#endif
|
||||
|
||||
#if defined(MICROPY_VFS_POSIX) && MICROPY_VFS_POSIX
|
||||
#include "extmod/vfs_posix.h"
|
||||
#endif
|
||||
|
@ -136,11 +140,49 @@ mp_import_stat_t mp_vfs_import_stat(const char *path) {
|
|||
}
|
||||
}
|
||||
|
||||
STATIC mp_obj_t mp_vfs_autodetect(mp_obj_t bdev_obj) {
|
||||
#if MICROPY_VFS_LFS1 || MICROPY_VFS_LFS2
|
||||
nlr_buf_t nlr;
|
||||
if (nlr_push(&nlr) == 0) {
|
||||
mp_obj_t vfs = MP_OBJ_NULL;
|
||||
mp_vfs_blockdev_t blockdev;
|
||||
mp_vfs_blockdev_init(&blockdev, bdev_obj);
|
||||
uint8_t buf[44];
|
||||
mp_vfs_blockdev_read_ext(&blockdev, 0, 8, sizeof(buf), buf);
|
||||
#if MICROPY_VFS_LFS1
|
||||
if (memcmp(&buf[32], "littlefs", 8) == 0) {
|
||||
// LFS1
|
||||
vfs = mp_type_vfs_lfs1.make_new(&mp_type_vfs_lfs1, 1, &bdev_obj, NULL);
|
||||
nlr_pop();
|
||||
return vfs;
|
||||
}
|
||||
#endif
|
||||
#if MICROPY_VFS_LFS2
|
||||
if (memcmp(&buf[0], "littlefs", 8) == 0) {
|
||||
// LFS2
|
||||
vfs = mp_type_vfs_lfs2.make_new(&mp_type_vfs_lfs2, 1, &bdev_obj, NULL);
|
||||
nlr_pop();
|
||||
return vfs;
|
||||
}
|
||||
#endif
|
||||
nlr_pop();
|
||||
} else {
|
||||
// Ignore exception (eg block device doesn't support extended readblocks)
|
||||
}
|
||||
#endif
|
||||
|
||||
#if MICROPY_VFS_FAT
|
||||
return mp_fat_vfs_type.make_new(&mp_fat_vfs_type, 1, &bdev_obj, NULL);
|
||||
#endif
|
||||
|
||||
return bdev_obj;
|
||||
}
|
||||
|
||||
mp_obj_t mp_vfs_mount(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
enum { ARG_readonly, ARG_mkfs };
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_readonly, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&mp_const_false_obj)} },
|
||||
{ MP_QSTR_mkfs, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&mp_const_false_obj)} },
|
||||
{ MP_QSTR_readonly, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_FALSE} },
|
||||
{ MP_QSTR_mkfs, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_FALSE} },
|
||||
};
|
||||
|
||||
// parse args
|
||||
|
@ -158,10 +200,7 @@ mp_obj_t mp_vfs_mount(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args
|
|||
if (dest[0] == MP_OBJ_NULL) {
|
||||
// Input object has no mount method, assume it's a block device and try to
|
||||
// auto-detect the filesystem and create the corresponding VFS entity.
|
||||
// (At the moment we only support FAT filesystems.)
|
||||
#if MICROPY_VFS_FAT
|
||||
vfs_obj = mp_fat_vfs_type.make_new(&mp_fat_vfs_type, 1, &vfs_obj, NULL);
|
||||
#endif
|
||||
vfs_obj = mp_vfs_autodetect(vfs_obj);
|
||||
}
|
||||
|
||||
// create new object
|
||||
|
@ -238,10 +277,10 @@ MP_DEFINE_CONST_FUN_OBJ_1(mp_vfs_umount_obj, mp_vfs_umount);
|
|||
mp_obj_t mp_vfs_open(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
enum { ARG_file, ARG_mode, ARG_encoding };
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_file, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_rom_obj = MP_ROM_PTR(&mp_const_none_obj)} },
|
||||
{ MP_QSTR_file, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_rom_obj = MP_ROM_NONE} },
|
||||
{ MP_QSTR_mode, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_QSTR(MP_QSTR_r)} },
|
||||
{ MP_QSTR_buffering, MP_ARG_INT, {.u_int = -1} },
|
||||
{ MP_QSTR_encoding, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&mp_const_none_obj)} },
|
||||
{ MP_QSTR_encoding, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
|
||||
};
|
||||
|
||||
// parse args
|
||||
|
@ -263,7 +302,6 @@ MP_DEFINE_CONST_FUN_OBJ_KW(mp_vfs_open_obj, 0, mp_vfs_open);
|
|||
mp_obj_t mp_vfs_chdir(mp_obj_t path_in) {
|
||||
mp_obj_t path_out;
|
||||
mp_vfs_mount_t *vfs = lookup_path(path_in, &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
|
||||
|
@ -275,9 +313,11 @@ mp_obj_t mp_vfs_chdir(mp_obj_t path_in) {
|
|||
break;
|
||||
}
|
||||
}
|
||||
vfs = MP_VFS_ROOT;
|
||||
} else {
|
||||
mp_vfs_proxy_call(vfs, MP_QSTR_chdir, 1, &path_out);
|
||||
}
|
||||
MP_STATE_VM(vfs_cur) = vfs;
|
||||
return mp_const_none;
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(mp_vfs_chdir_obj, mp_vfs_chdir);
|
||||
|
@ -454,4 +494,25 @@ mp_obj_t mp_vfs_statvfs(mp_obj_t path_in) {
|
|||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(mp_vfs_statvfs_obj, mp_vfs_statvfs);
|
||||
|
||||
// This is a C-level helper function for ports to use if needed.
|
||||
int mp_vfs_mount_and_chdir_protected(mp_obj_t bdev, mp_obj_t mount_point) {
|
||||
nlr_buf_t nlr;
|
||||
mp_int_t ret = -MP_EIO;
|
||||
if (nlr_push(&nlr) == 0) {
|
||||
mp_obj_t args[] = { bdev, mount_point };
|
||||
mp_vfs_mount(2, args, (mp_map_t *)&mp_const_empty_map);
|
||||
mp_vfs_chdir(mount_point);
|
||||
ret = 0; // success
|
||||
nlr_pop();
|
||||
} else {
|
||||
mp_obj_base_t *exc = nlr.ret_val;
|
||||
if (mp_obj_is_subclass_fast(MP_OBJ_FROM_PTR(exc->type), MP_OBJ_FROM_PTR(&mp_type_OSError))) {
|
||||
mp_obj_t v = mp_obj_exception_get_value(MP_OBJ_FROM_PTR(exc));
|
||||
mp_obj_get_int_maybe(v, &ret); // get errno value
|
||||
ret = -ret;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif // MICROPY_VFS
|
||||
|
|
44
extmod/vfs.h
44
extmod/vfs.h
|
@ -19,12 +19,24 @@
|
|||
#define MP_S_IFDIR (0x4000)
|
||||
#define MP_S_IFREG (0x8000)
|
||||
|
||||
// these are the values for mp_vfs_blockdev_t.flags
|
||||
#define MP_BLOCKDEV_FLAG_NATIVE (0x0001) // readblocks[2]/writeblocks[2] contain native func
|
||||
#define MP_BLOCKDEV_FLAG_FREE_OBJ (0x0002) // fs_user_mount_t obj should be freed on umount
|
||||
#define MP_BLOCKDEV_FLAG_HAVE_IOCTL (0x0004) // new protocol with ioctl
|
||||
#define MP_BLOCKDEV_FLAG_NO_FILESYSTEM (0x0008) // the block device has no filesystem on it
|
||||
// Device is writable over USB and read-only to MicroPython.
|
||||
#define MP_BLOCKDEV_FLAG_USB_WRITABLE (0x0010)
|
||||
// Bit set when the above flag is checked before opening a file for write.
|
||||
#define MP_BLOCKDEV_FLAG_CONCURRENT_WRITE_PROTECTED (0x0020)
|
||||
|
||||
// constants for block protocol ioctl
|
||||
#define BP_IOCTL_INIT (1)
|
||||
#define BP_IOCTL_DEINIT (2)
|
||||
#define BP_IOCTL_SYNC (3)
|
||||
#define BP_IOCTL_SEC_COUNT (4)
|
||||
#define BP_IOCTL_SEC_SIZE (5)
|
||||
#define MP_BLOCKDEV_IOCTL_INIT (1)
|
||||
#define MP_BLOCKDEV_IOCTL_DEINIT (2)
|
||||
#define MP_BLOCKDEV_IOCTL_SYNC (3)
|
||||
#define MP_BLOCKDEV_IOCTL_BLOCK_COUNT (4)
|
||||
#define MP_BLOCKDEV_IOCTL_BLOCK_SIZE (5)
|
||||
#define MP_BLOCKDEV_IOCTL_BLOCK_ERASE (6)
|
||||
|
||||
|
||||
// At the moment the VFS protocol just has import_stat, but could be extended to other methods
|
||||
typedef struct _mp_vfs_proto_t {
|
||||
|
@ -32,6 +44,21 @@ typedef struct _mp_vfs_proto_t {
|
|||
mp_import_stat_t (*import_stat)(void *self, const char *path);
|
||||
} mp_vfs_proto_t;
|
||||
|
||||
typedef struct _mp_vfs_blockdev_t {
|
||||
uint16_t flags;
|
||||
size_t block_size;
|
||||
mp_obj_t readblocks[5];
|
||||
mp_obj_t writeblocks[5];
|
||||
// new protocol uses just ioctl, old uses sync (optional) and count
|
||||
union {
|
||||
mp_obj_t ioctl[4];
|
||||
struct {
|
||||
mp_obj_t sync[2];
|
||||
mp_obj_t count[2];
|
||||
} old;
|
||||
} u;
|
||||
} mp_vfs_blockdev_t;
|
||||
|
||||
typedef struct _mp_vfs_mount_t {
|
||||
const char *str; // mount point with leading /
|
||||
size_t len;
|
||||
|
@ -51,6 +78,12 @@ typedef struct _mp_vfs_ilistdir_it_t {
|
|||
} mp_vfs_ilistdir_it_t;
|
||||
|
||||
mp_obj_t mp_vfs_ilistdir_it_iternext(mp_obj_t self_in);
|
||||
void mp_vfs_blockdev_init(mp_vfs_blockdev_t *self, mp_obj_t bdev);
|
||||
int mp_vfs_blockdev_read(mp_vfs_blockdev_t *self, size_t block_num, size_t num_blocks, uint8_t *buf);
|
||||
int mp_vfs_blockdev_read_ext(mp_vfs_blockdev_t *self, size_t block_num, size_t block_off, size_t len, uint8_t *buf);
|
||||
int mp_vfs_blockdev_write(mp_vfs_blockdev_t *self, size_t block_num, size_t num_blocks, const uint8_t *buf);
|
||||
int mp_vfs_blockdev_write_ext(mp_vfs_blockdev_t *self, size_t block_num, size_t block_off, size_t len, const uint8_t *buf);
|
||||
mp_obj_t mp_vfs_blockdev_ioctl(mp_vfs_blockdev_t *self, uintptr_t cmd, uintptr_t arg);
|
||||
|
||||
mp_vfs_mount_t *mp_vfs_lookup_path(const char *path, const char **path_out);
|
||||
mp_import_stat_t mp_vfs_import_stat(const char *path);
|
||||
|
@ -68,6 +101,7 @@ mp_obj_t mp_vfs_rmdir(mp_obj_t path_in);
|
|||
mp_obj_t mp_vfs_stat(mp_obj_t path_in);
|
||||
mp_obj_t mp_vfs_statvfs(mp_obj_t path_in);
|
||||
|
||||
int mp_vfs_mount_and_chdir_protected(mp_obj_t bdev, mp_obj_t mount_point);
|
||||
mp_obj_t mp_vfs_ilistdir_it_iternext(mp_obj_t self_in);
|
||||
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(mp_vfs_mount_obj);
|
||||
|
|
|
@ -0,0 +1,143 @@
|
|||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013-2019 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/runtime.h"
|
||||
#include "py/binary.h"
|
||||
#include "py/objarray.h"
|
||||
#include "py/mperrno.h"
|
||||
#include "extmod/vfs.h"
|
||||
|
||||
#if MICROPY_VFS
|
||||
|
||||
void mp_vfs_blockdev_init(mp_vfs_blockdev_t *self, mp_obj_t bdev) {
|
||||
mp_load_method(bdev, MP_QSTR_readblocks, self->readblocks);
|
||||
mp_load_method_maybe(bdev, MP_QSTR_writeblocks, self->writeblocks);
|
||||
mp_load_method_maybe(bdev, MP_QSTR_ioctl, self->u.ioctl);
|
||||
if (self->u.ioctl[0] != MP_OBJ_NULL) {
|
||||
// Device supports new block protocol, so indicate it
|
||||
self->flags |= MP_BLOCKDEV_FLAG_HAVE_IOCTL;
|
||||
} else {
|
||||
// No ioctl method, so assume the device uses the old block protocol
|
||||
mp_load_method_maybe(bdev, MP_QSTR_sync, self->u.old.sync);
|
||||
mp_load_method(bdev, MP_QSTR_count, self->u.old.count);
|
||||
}
|
||||
}
|
||||
|
||||
int mp_vfs_blockdev_read(mp_vfs_blockdev_t *self, size_t block_num, size_t num_blocks, uint8_t *buf) {
|
||||
if (self->flags & MP_BLOCKDEV_FLAG_NATIVE) {
|
||||
mp_uint_t (*f)(uint8_t *, uint32_t, uint32_t) = (void *)(uintptr_t)self->readblocks[2];
|
||||
return f(buf, block_num, num_blocks);
|
||||
} else {
|
||||
mp_obj_array_t ar = {{&mp_type_bytearray}, BYTEARRAY_TYPECODE, 0, num_blocks *self->block_size, buf};
|
||||
self->readblocks[2] = MP_OBJ_NEW_SMALL_INT(block_num);
|
||||
self->readblocks[3] = MP_OBJ_FROM_PTR(&ar);
|
||||
mp_call_method_n_kw(2, 0, self->readblocks);
|
||||
// TODO handle error return
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int mp_vfs_blockdev_read_ext(mp_vfs_blockdev_t *self, size_t block_num, size_t block_off, size_t len, uint8_t *buf) {
|
||||
mp_obj_array_t ar = {{&mp_type_bytearray}, BYTEARRAY_TYPECODE, 0, len, buf};
|
||||
self->readblocks[2] = MP_OBJ_NEW_SMALL_INT(block_num);
|
||||
self->readblocks[3] = MP_OBJ_FROM_PTR(&ar);
|
||||
self->readblocks[4] = MP_OBJ_NEW_SMALL_INT(block_off);
|
||||
mp_obj_t ret = mp_call_method_n_kw(3, 0, self->readblocks);
|
||||
if (ret == mp_const_none) {
|
||||
return 0;
|
||||
} else {
|
||||
return MP_OBJ_SMALL_INT_VALUE(ret);
|
||||
}
|
||||
}
|
||||
|
||||
int mp_vfs_blockdev_write(mp_vfs_blockdev_t *self, size_t block_num, size_t num_blocks, const uint8_t *buf) {
|
||||
if (self->writeblocks[0] == MP_OBJ_NULL) {
|
||||
// read-only block device
|
||||
return -MP_EROFS;
|
||||
}
|
||||
|
||||
if (self->flags & MP_BLOCKDEV_FLAG_NATIVE) {
|
||||
mp_uint_t (*f)(const uint8_t *, uint32_t, uint32_t) = (void *)(uintptr_t)self->writeblocks[2];
|
||||
return f(buf, block_num, num_blocks);
|
||||
} else {
|
||||
mp_obj_array_t ar = {{&mp_type_bytearray}, BYTEARRAY_TYPECODE, 0, num_blocks *self->block_size, (void *)buf};
|
||||
self->writeblocks[2] = MP_OBJ_NEW_SMALL_INT(block_num);
|
||||
self->writeblocks[3] = MP_OBJ_FROM_PTR(&ar);
|
||||
mp_call_method_n_kw(2, 0, self->writeblocks);
|
||||
// TODO handle error return
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int mp_vfs_blockdev_write_ext(mp_vfs_blockdev_t *self, size_t block_num, size_t block_off, size_t len, const uint8_t *buf) {
|
||||
if (self->writeblocks[0] == MP_OBJ_NULL) {
|
||||
// read-only block device
|
||||
return -MP_EROFS;
|
||||
}
|
||||
|
||||
mp_obj_array_t ar = {{&mp_type_bytearray}, BYTEARRAY_TYPECODE, 0, len, (void *)buf};
|
||||
self->writeblocks[2] = MP_OBJ_NEW_SMALL_INT(block_num);
|
||||
self->writeblocks[3] = MP_OBJ_FROM_PTR(&ar);
|
||||
self->writeblocks[4] = MP_OBJ_NEW_SMALL_INT(block_off);
|
||||
mp_obj_t ret = mp_call_method_n_kw(3, 0, self->writeblocks);
|
||||
if (ret == mp_const_none) {
|
||||
return 0;
|
||||
} else {
|
||||
return MP_OBJ_SMALL_INT_VALUE(ret);
|
||||
}
|
||||
}
|
||||
|
||||
mp_obj_t mp_vfs_blockdev_ioctl(mp_vfs_blockdev_t *self, uintptr_t cmd, uintptr_t arg) {
|
||||
if (self->flags & MP_BLOCKDEV_FLAG_HAVE_IOCTL) {
|
||||
// New protocol with ioctl
|
||||
self->u.ioctl[2] = MP_OBJ_NEW_SMALL_INT(cmd);
|
||||
self->u.ioctl[3] = MP_OBJ_NEW_SMALL_INT(arg);
|
||||
return mp_call_method_n_kw(2, 0, self->u.ioctl);
|
||||
} else {
|
||||
// Old protocol with sync and count
|
||||
switch (cmd) {
|
||||
case MP_BLOCKDEV_IOCTL_SYNC:
|
||||
if (self->u.old.sync[0] != MP_OBJ_NULL) {
|
||||
mp_call_method_n_kw(0, 0, self->u.old.sync);
|
||||
}
|
||||
break;
|
||||
|
||||
case MP_BLOCKDEV_IOCTL_BLOCK_COUNT:
|
||||
return mp_call_method_n_kw(0, 0, self->u.old.count);
|
||||
|
||||
case MP_BLOCKDEV_IOCTL_BLOCK_SIZE:
|
||||
// Old protocol has fixed sector size of 512 bytes
|
||||
break;
|
||||
|
||||
case MP_BLOCKDEV_IOCTL_INIT:
|
||||
// Old protocol doesn't have init
|
||||
break;
|
||||
}
|
||||
return mp_const_none;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // MICROPY_VFS
|
|
@ -12,6 +12,7 @@
|
|||
#endif
|
||||
|
||||
#include <string.h>
|
||||
#include "py/obj.h"
|
||||
#include "py/objproperty.h"
|
||||
#include "py/runtime.h"
|
||||
#include "py/mperrno.h"
|
||||
|
@ -29,7 +30,7 @@
|
|||
|
||||
#define mp_obj_fat_vfs_t fs_user_mount_t
|
||||
|
||||
mp_import_stat_t fat_vfs_import_stat(void *vfs_in, const char *path) {
|
||||
STATIC mp_import_stat_t fat_vfs_import_stat(void *vfs_in, const char *path) {
|
||||
fs_user_mount_t *vfs = vfs_in;
|
||||
FILINFO fno;
|
||||
assert(vfs != NULL);
|
||||
|
@ -50,27 +51,18 @@ STATIC mp_obj_t fat_vfs_make_new(const mp_obj_type_t *type, size_t n_args, const
|
|||
// create new object
|
||||
fs_user_mount_t *vfs = m_new_obj(fs_user_mount_t);
|
||||
vfs->base.type = type;
|
||||
vfs->flags = FSUSER_FREE_OBJ;
|
||||
vfs->fatfs.drv = vfs;
|
||||
|
||||
// load block protocol methods
|
||||
mp_load_method(args[0], MP_QSTR_readblocks, vfs->readblocks);
|
||||
mp_load_method_maybe(args[0], MP_QSTR_writeblocks, vfs->writeblocks);
|
||||
mp_load_method_maybe(args[0], MP_QSTR_ioctl, vfs->u.ioctl);
|
||||
if (vfs->u.ioctl[0] != MP_OBJ_NULL) {
|
||||
// device supports new block protocol, so indicate it
|
||||
vfs->flags |= FSUSER_HAVE_IOCTL;
|
||||
} else {
|
||||
// no ioctl method, so assume the device uses the old block protocol
|
||||
mp_load_method_maybe(args[0], MP_QSTR_sync, vfs->u.old.sync);
|
||||
mp_load_method(args[0], MP_QSTR_count, vfs->u.old.count);
|
||||
}
|
||||
// Initialise underlying block device
|
||||
vfs->blockdev.flags = MP_BLOCKDEV_FLAG_FREE_OBJ;
|
||||
vfs->blockdev.block_size = FF_MIN_SS; // default, will be populated by call to MP_BLOCKDEV_IOCTL_BLOCK_SIZE
|
||||
mp_vfs_blockdev_init(&vfs->blockdev, args[0]);
|
||||
|
||||
// mount the block device so the VFS methods can be used
|
||||
FRESULT res = f_mount(&vfs->fatfs);
|
||||
if (res == FR_NO_FILESYSTEM) {
|
||||
// don't error out if no filesystem, to let mkfs()/mount() create one if wanted
|
||||
vfs->flags |= FSUSER_NO_FILESYSTEM;
|
||||
vfs->blockdev.flags |= MP_BLOCKDEV_FLAG_NO_FILESYSTEM;
|
||||
} else if (res != FR_OK) {
|
||||
mp_raise_OSError(fresult_to_errno_table[res]);
|
||||
}
|
||||
|
@ -394,11 +386,11 @@ STATIC mp_obj_t vfs_fat_mount(mp_obj_t self_in, mp_obj_t readonly, mp_obj_t mkfs
|
|||
// 1. readonly=True keyword argument
|
||||
// 2. nonexistent writeblocks method (then writeblocks[0] == MP_OBJ_NULL already)
|
||||
if (mp_obj_is_true(readonly)) {
|
||||
self->writeblocks[0] = MP_OBJ_NULL;
|
||||
self->blockdev.writeblocks[0] = MP_OBJ_NULL;
|
||||
}
|
||||
|
||||
// check if we need to make the filesystem
|
||||
FRESULT res = (self->flags & FSUSER_NO_FILESYSTEM) ? FR_NO_FILESYSTEM : FR_OK;
|
||||
FRESULT res = (self->blockdev.flags & MP_BLOCKDEV_FLAG_NO_FILESYSTEM) ? FR_NO_FILESYSTEM : FR_OK;
|
||||
if (res == FR_NO_FILESYSTEM && mp_obj_is_true(mkfs)) {
|
||||
uint8_t working_buf[FF_MAX_SS];
|
||||
res = f_mkfs(&self->fatfs, FM_FAT | FM_SFD, 0, working_buf, sizeof(working_buf));
|
||||
|
@ -406,7 +398,7 @@ STATIC mp_obj_t vfs_fat_mount(mp_obj_t self_in, mp_obj_t readonly, mp_obj_t mkfs
|
|||
if (res != FR_OK) {
|
||||
mp_raise_OSError(fresult_to_errno_table[res]);
|
||||
}
|
||||
self->flags &= ~FSUSER_NO_FILESYSTEM;
|
||||
self->blockdev.flags &= ~MP_BLOCKDEV_FLAG_NO_FILESYSTEM;
|
||||
|
||||
return mp_const_none;
|
||||
}
|
||||
|
@ -438,7 +430,7 @@ STATIC mp_obj_t vfs_fat_setlabel(mp_obj_t self_in, mp_obj_t label_in) {
|
|||
FRESULT res = f_setlabel(&self->fatfs, label_str);
|
||||
if (res != FR_OK) {
|
||||
if (res == FR_WRITE_PROTECTED) {
|
||||
mp_raise_msg(&mp_type_OSError, translate("Read-only filesystem"));
|
||||
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Read-only filesystem"));
|
||||
}
|
||||
mp_raise_OSError(fresult_to_errno_table[res]);
|
||||
}
|
||||
|
@ -449,7 +441,7 @@ STATIC const mp_obj_property_t fat_vfs_label_obj = {
|
|||
.base.type = &mp_type_property,
|
||||
.proxy = {(mp_obj_t)&fat_vfs_getlabel_obj,
|
||||
(mp_obj_t)&fat_vfs_setlabel_obj,
|
||||
(mp_obj_t)&mp_const_none_obj},
|
||||
MP_ROM_NONE},
|
||||
};
|
||||
#endif
|
||||
|
||||
|
|
|
@ -6,55 +6,26 @@
|
|||
#ifndef MICROPY_INCLUDED_EXTMOD_VFS_FAT_H
|
||||
#define MICROPY_INCLUDED_EXTMOD_VFS_FAT_H
|
||||
|
||||
#include "py/lexer.h"
|
||||
#include "py/obj.h"
|
||||
#include "lib/oofatfs/ff.h"
|
||||
#include "extmod/vfs.h"
|
||||
|
||||
// these are the values for fs_user_mount_t.flags
|
||||
#define FSUSER_NATIVE (0x0001) // readblocks[2]/writeblocks[2] contain native func
|
||||
#define FSUSER_FREE_OBJ (0x0002) // fs_user_mount_t obj should be freed on umount
|
||||
#define FSUSER_HAVE_IOCTL (0x0004) // new protocol with ioctl
|
||||
#define FSUSER_NO_FILESYSTEM (0x0008) // the block device has no filesystem on it
|
||||
// Device is writable over USB and read-only to MicroPython.
|
||||
#define FSUSER_USB_WRITABLE (0x0010)
|
||||
// Bit set when the above flag is checked before opening a file for write.
|
||||
#define FSUSER_CONCURRENT_WRITE_PROTECTED (0x0020)
|
||||
|
||||
typedef struct _fs_user_mount_t {
|
||||
mp_obj_base_t base;
|
||||
uint16_t flags;
|
||||
mp_obj_t readblocks[4];
|
||||
mp_obj_t writeblocks[4];
|
||||
// new protocol uses just ioctl, old uses sync (optional) and count
|
||||
union {
|
||||
mp_obj_t ioctl[4];
|
||||
struct {
|
||||
mp_obj_t sync[2];
|
||||
mp_obj_t count[2];
|
||||
} old;
|
||||
} u;
|
||||
mp_vfs_blockdev_t blockdev;
|
||||
FATFS fatfs;
|
||||
} fs_user_mount_t;
|
||||
|
||||
typedef struct _pyb_file_obj_t {
|
||||
mp_obj_base_t base;
|
||||
FIL fp;
|
||||
} pyb_file_obj_t;
|
||||
|
||||
extern const byte fresult_to_errno_table[20];
|
||||
extern const mp_obj_type_t mp_fat_vfs_type;
|
||||
extern const mp_obj_type_t mp_type_vfs_fat_fileio;
|
||||
extern const mp_obj_type_t mp_type_vfs_fat_textio;
|
||||
|
||||
mp_import_stat_t fat_vfs_import_stat(void *vfs, const char *path);
|
||||
|
||||
MP_DECLARE_CONST_FUN_OBJ_3(fat_vfs_open_obj);
|
||||
|
||||
mp_obj_t fat_vfs_ilistdir2(struct _fs_user_mount_t *vfs, const char *path, bool is_str_type);
|
||||
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(fsuser_mount_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_1(fsuser_umount_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(fsuser_mkfs_obj);
|
||||
typedef struct _pyb_file_obj_t {
|
||||
mp_obj_base_t base;
|
||||
FIL fp;
|
||||
} pyb_file_obj_t;
|
||||
|
||||
#endif // MICROPY_INCLUDED_EXTMOD_VFS_FAT_H
|
||||
|
|
|
@ -14,16 +14,11 @@
|
|||
#include "py/runtime.h"
|
||||
#include "py/binary.h"
|
||||
#include "py/objarray.h"
|
||||
#include "py/mperrno.h"
|
||||
#include "lib/oofatfs/ff.h"
|
||||
#include "lib/oofatfs/diskio.h"
|
||||
#include "extmod/vfs_fat.h"
|
||||
|
||||
#if FF_MAX_SS == FF_MIN_SS
|
||||
#define SECSIZE(fs) (FF_MIN_SS)
|
||||
#else
|
||||
#define SECSIZE(fs) ((fs)->ssize)
|
||||
#endif
|
||||
|
||||
typedef void *bdev_t;
|
||||
STATIC fs_user_mount_t *disk_get_device(void *bdev) {
|
||||
return (fs_user_mount_t *)bdev;
|
||||
|
@ -44,29 +39,9 @@ DRESULT disk_read(
|
|||
return RES_PARERR;
|
||||
}
|
||||
|
||||
if (vfs->flags & FSUSER_NATIVE) {
|
||||
mp_uint_t (*f)(uint8_t *, uint32_t, uint32_t) = (void *)(uintptr_t)vfs->readblocks[2];
|
||||
if (f(buff, sector, count) != 0) {
|
||||
return RES_ERROR;
|
||||
}
|
||||
} else {
|
||||
mp_obj_array_t ar = {{&mp_type_bytearray}, BYTEARRAY_TYPECODE, 0, count *SECSIZE(&vfs->fatfs), buff};
|
||||
vfs->readblocks[2] = MP_OBJ_NEW_SMALL_INT(sector);
|
||||
vfs->readblocks[3] = MP_OBJ_FROM_PTR(&ar);
|
||||
nlr_buf_t nlr;
|
||||
if (nlr_push(&nlr) == 0) {
|
||||
mp_obj_t ret = mp_call_method_n_kw(2, 0, vfs->readblocks);
|
||||
nlr_pop();
|
||||
if (ret != mp_const_none && MP_OBJ_SMALL_INT_VALUE(ret) != 0) {
|
||||
return RES_ERROR;
|
||||
}
|
||||
} else {
|
||||
// Exception thrown by readblocks or something it calls.
|
||||
return RES_ERROR;
|
||||
}
|
||||
}
|
||||
int ret = mp_vfs_blockdev_read(&vfs->blockdev, sector, count, buff);
|
||||
|
||||
return RES_OK;
|
||||
return ret == 0 ? RES_OK : RES_ERROR;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
@ -84,34 +59,14 @@ DRESULT disk_write(
|
|||
return RES_PARERR;
|
||||
}
|
||||
|
||||
if (vfs->writeblocks[0] == MP_OBJ_NULL) {
|
||||
int ret = mp_vfs_blockdev_write(&vfs->blockdev, sector, count, buff);
|
||||
|
||||
if (ret == -MP_EROFS) {
|
||||
// read-only block device
|
||||
return RES_WRPRT;
|
||||
}
|
||||
|
||||
if (vfs->flags & FSUSER_NATIVE) {
|
||||
mp_uint_t (*f)(const uint8_t *, uint32_t, uint32_t) = (void *)(uintptr_t)vfs->writeblocks[2];
|
||||
if (f(buff, sector, count) != 0) {
|
||||
return RES_ERROR;
|
||||
}
|
||||
} else {
|
||||
mp_obj_array_t ar = {{&mp_type_bytearray}, BYTEARRAY_TYPECODE, 0, count *SECSIZE(&vfs->fatfs), (void *)buff};
|
||||
vfs->writeblocks[2] = MP_OBJ_NEW_SMALL_INT(sector);
|
||||
vfs->writeblocks[3] = MP_OBJ_FROM_PTR(&ar);
|
||||
nlr_buf_t nlr;
|
||||
if (nlr_push(&nlr) == 0) {
|
||||
mp_obj_t ret = mp_call_method_n_kw(2, 0, vfs->writeblocks);
|
||||
nlr_pop();
|
||||
if (ret != mp_const_none && MP_OBJ_SMALL_INT_VALUE(ret) != 0) {
|
||||
return RES_ERROR;
|
||||
}
|
||||
} else {
|
||||
// Exception thrown by writeblocks or something it calls.
|
||||
return RES_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
return RES_OK;
|
||||
return ret == 0 ? RES_OK : RES_ERROR;
|
||||
}
|
||||
|
||||
|
||||
|
@ -130,55 +85,16 @@ DRESULT disk_ioctl(
|
|||
}
|
||||
|
||||
// First part: call the relevant method of the underlying block device
|
||||
mp_int_t out_value = 0;
|
||||
if (vfs->flags & FSUSER_HAVE_IOCTL) {
|
||||
// new protocol with ioctl
|
||||
static const uint8_t op_map[8] = {
|
||||
[CTRL_SYNC] = BP_IOCTL_SYNC,
|
||||
[GET_SECTOR_COUNT] = BP_IOCTL_SEC_COUNT,
|
||||
[GET_SECTOR_SIZE] = BP_IOCTL_SEC_SIZE,
|
||||
[IOCTL_INIT] = BP_IOCTL_INIT,
|
||||
[CTRL_SYNC] = MP_BLOCKDEV_IOCTL_SYNC,
|
||||
[GET_SECTOR_COUNT] = MP_BLOCKDEV_IOCTL_BLOCK_COUNT,
|
||||
[GET_SECTOR_SIZE] = MP_BLOCKDEV_IOCTL_BLOCK_SIZE,
|
||||
[IOCTL_INIT] = MP_BLOCKDEV_IOCTL_INIT,
|
||||
};
|
||||
uint8_t bp_op = op_map[cmd & 7];
|
||||
mp_obj_t ret = mp_const_none;
|
||||
if (bp_op != 0) {
|
||||
if (vfs->flags & FSUSER_NATIVE) {
|
||||
bool (*f)(size_t, mp_int_t *) = (void *)(uintptr_t)vfs->u.ioctl[2];
|
||||
if (!f(bp_op, (mp_int_t *)&out_value)) {
|
||||
return RES_ERROR;
|
||||
}
|
||||
} else {
|
||||
vfs->u.ioctl[2] = MP_OBJ_NEW_SMALL_INT(bp_op);
|
||||
vfs->u.ioctl[3] = MP_OBJ_NEW_SMALL_INT(0); // unused
|
||||
mp_obj_t ret = mp_call_method_n_kw(2, 0, vfs->u.ioctl);
|
||||
if (ret != mp_const_none) {
|
||||
out_value = mp_obj_get_int(ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// old protocol with sync and count
|
||||
switch (cmd) {
|
||||
case CTRL_SYNC:
|
||||
if (vfs->u.old.sync[0] != MP_OBJ_NULL) {
|
||||
mp_call_method_n_kw(0, 0, vfs->u.old.sync);
|
||||
}
|
||||
break;
|
||||
|
||||
case GET_SECTOR_COUNT: {
|
||||
mp_obj_t ret = mp_call_method_n_kw(0, 0, vfs->u.old.count);
|
||||
if (ret != mp_const_none) {
|
||||
out_value = mp_obj_get_int(ret);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case GET_SECTOR_SIZE:
|
||||
// old protocol has fixed sector size of 512 bytes
|
||||
break;
|
||||
|
||||
case IOCTL_INIT:
|
||||
// old protocol doesn't have init
|
||||
break;
|
||||
}
|
||||
ret = mp_vfs_blockdev_ioctl(&vfs->blockdev, bp_op, 0);
|
||||
}
|
||||
|
||||
// Second part: convert the result for return
|
||||
|
@ -187,21 +103,19 @@ DRESULT disk_ioctl(
|
|||
return RES_OK;
|
||||
|
||||
case GET_SECTOR_COUNT: {
|
||||
*((DWORD *)buff) = out_value;
|
||||
*((DWORD *)buff) = mp_obj_get_int(ret);
|
||||
return RES_OK;
|
||||
}
|
||||
|
||||
case GET_SECTOR_SIZE: {
|
||||
if (out_value == 0) {
|
||||
if (ret == mp_const_none) {
|
||||
// Default sector size
|
||||
*((WORD *)buff) = 512;
|
||||
} else {
|
||||
*((WORD *)buff) = out_value;
|
||||
*((WORD *)buff) = mp_obj_get_int(ret);
|
||||
}
|
||||
#if FF_MAX_SS != FF_MIN_SS
|
||||
// need to store ssize because we use it in disk_read/disk_write
|
||||
vfs->fatfs.ssize = *((WORD *)buff);
|
||||
#endif
|
||||
vfs->blockdev.block_size = *((WORD *)buff);
|
||||
return RES_OK;
|
||||
}
|
||||
|
||||
|
@ -212,10 +126,10 @@ DRESULT disk_ioctl(
|
|||
case IOCTL_INIT:
|
||||
case IOCTL_STATUS: {
|
||||
DSTATUS stat;
|
||||
if (out_value != 0) {
|
||||
if (ret != mp_const_none && MP_OBJ_SMALL_INT_VALUE(ret) != 0) {
|
||||
// error initialising
|
||||
stat = STA_NOINIT;
|
||||
} else if (vfs->writeblocks[0] == MP_OBJ_NULL) {
|
||||
} else if (vfs->blockdev.writeblocks[0] == MP_OBJ_NULL) {
|
||||
stat = STA_PROTECT;
|
||||
} else {
|
||||
stat = 0;
|
||||
|
|
|
@ -130,9 +130,9 @@ STATIC mp_uint_t file_obj_ioctl(mp_obj_t o_in, mp_uint_t request, uintptr_t arg,
|
|||
// Note: encoding is ignored for now; it's also not a valid kwarg for CPython's FileIO,
|
||||
// but by adding it here we can use one single mp_arg_t array for open() and FileIO's constructor
|
||||
STATIC const mp_arg_t file_open_args[] = {
|
||||
{ MP_QSTR_file, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_rom_obj = MP_ROM_PTR(&mp_const_none_obj)} },
|
||||
{ MP_QSTR_file, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_rom_obj = MP_ROM_NONE} },
|
||||
{ MP_QSTR_mode, MP_ARG_OBJ, {.u_obj = MP_OBJ_NEW_QSTR(MP_QSTR_r)} },
|
||||
{ MP_QSTR_encoding, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_rom_obj = MP_ROM_PTR(&mp_const_none_obj)} },
|
||||
{ MP_QSTR_encoding, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_rom_obj = MP_ROM_NONE} },
|
||||
};
|
||||
#define FILE_OPEN_NUM_ARGS MP_ARRAY_SIZE(file_open_args)
|
||||
|
||||
|
@ -183,12 +183,14 @@ STATIC mp_obj_t file_open(fs_user_mount_t *vfs, const mp_obj_type_t *type, mp_ar
|
|||
}
|
||||
// If we're reading, turn on fast seek.
|
||||
if (mode == FA_READ) {
|
||||
// one call to determine how much space we need.
|
||||
// One call to determine how much space we need.
|
||||
DWORD temp_table[2];
|
||||
temp_table[0] = 2;
|
||||
o->fp.cltbl = temp_table;
|
||||
f_lseek(&o->fp, CREATE_LINKMAP);
|
||||
DWORD size = (temp_table[0] + 1) * 2;
|
||||
|
||||
// Now allocate the size and construct the map.
|
||||
o->fp.cltbl = m_malloc_maybe(size * sizeof(DWORD), false);
|
||||
if (o->fp.cltbl != NULL) {
|
||||
o->fp.cltbl[0] = size;
|
||||
|
@ -215,7 +217,7 @@ STATIC mp_obj_t file_obj_make_new(const mp_obj_type_t *type, size_t n_args, cons
|
|||
|
||||
// TODO gc hook to close the file if not already closed
|
||||
|
||||
STATIC const mp_rom_map_elem_t rawfile_locals_dict_table[] = {
|
||||
STATIC const mp_rom_map_elem_t vfs_fat_rawfile_locals_dict_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj) },
|
||||
|
@ -230,10 +232,10 @@ STATIC const mp_rom_map_elem_t rawfile_locals_dict_table[] = {
|
|||
{ MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&file_obj___exit___obj) },
|
||||
};
|
||||
|
||||
STATIC MP_DEFINE_CONST_DICT(rawfile_locals_dict, rawfile_locals_dict_table);
|
||||
STATIC MP_DEFINE_CONST_DICT(vfs_fat_rawfile_locals_dict, vfs_fat_rawfile_locals_dict_table);
|
||||
|
||||
#if MICROPY_PY_IO_FILEIO
|
||||
STATIC const mp_stream_p_t fileio_stream_p = {
|
||||
STATIC const mp_stream_p_t vfs_fat_fileio_stream_p = {
|
||||
MP_PROTO_IMPLEMENT(MP_QSTR_protocol_stream)
|
||||
.read = file_obj_read,
|
||||
.write = file_obj_write,
|
||||
|
@ -247,12 +249,12 @@ const mp_obj_type_t mp_type_vfs_fat_fileio = {
|
|||
.make_new = file_obj_make_new,
|
||||
.getiter = mp_identity_getiter,
|
||||
.iternext = mp_stream_unbuffered_iter,
|
||||
.protocol = &fileio_stream_p,
|
||||
.locals_dict = (mp_obj_dict_t *)&rawfile_locals_dict,
|
||||
.protocol = &vfs_fat_fileio_stream_p,
|
||||
.locals_dict = (mp_obj_dict_t *)&vfs_fat_rawfile_locals_dict,
|
||||
};
|
||||
#endif
|
||||
|
||||
STATIC const mp_stream_p_t textio_stream_p = {
|
||||
STATIC const mp_stream_p_t vfs_fat_textio_stream_p = {
|
||||
MP_PROTO_IMPLEMENT(MP_QSTR_protocol_stream)
|
||||
.read = file_obj_read,
|
||||
.write = file_obj_write,
|
||||
|
@ -267,8 +269,8 @@ const mp_obj_type_t mp_type_vfs_fat_textio = {
|
|||
.make_new = file_obj_make_new,
|
||||
.getiter = mp_identity_getiter,
|
||||
.iternext = mp_stream_unbuffered_iter,
|
||||
.protocol = &textio_stream_p,
|
||||
.locals_dict = (mp_obj_dict_t *)&rawfile_locals_dict,
|
||||
.protocol = &vfs_fat_textio_stream_p,
|
||||
.locals_dict = (mp_obj_dict_t *)&vfs_fat_rawfile_locals_dict,
|
||||
};
|
||||
|
||||
// Factory function for I/O stream classes
|
||||
|
|
|
@ -0,0 +1,142 @@
|
|||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019-2020 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/runtime.h"
|
||||
#include "py/mphal.h"
|
||||
#include "extmod/vfs.h"
|
||||
#include "extmod/vfs_lfs.h"
|
||||
|
||||
#if MICROPY_VFS && (MICROPY_VFS_LFS1 || MICROPY_VFS_LFS2)
|
||||
|
||||
enum { LFS_MAKE_ARG_bdev, LFS_MAKE_ARG_readsize, LFS_MAKE_ARG_progsize, LFS_MAKE_ARG_lookahead, LFS_MAKE_ARG_mtime };
|
||||
|
||||
static const mp_arg_t lfs_make_allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ },
|
||||
{ MP_QSTR_readsize, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 32} },
|
||||
{ MP_QSTR_progsize, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 32} },
|
||||
{ MP_QSTR_lookahead, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 32} },
|
||||
{ MP_QSTR_mtime, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = true} },
|
||||
};
|
||||
|
||||
#if MICROPY_VFS_LFS1
|
||||
|
||||
#include "lib/littlefs/lfs1.h"
|
||||
|
||||
#define LFS_BUILD_VERSION (1)
|
||||
#define LFSx_MACRO(s) LFS1##s
|
||||
#define LFSx_API(s) lfs1_##s
|
||||
#define MP_VFS_LFSx(s) mp_vfs_lfs1_##s
|
||||
#define MP_OBJ_VFS_LFSx mp_obj_vfs_lfs1_t
|
||||
#define MP_OBJ_VFS_LFSx_FILE mp_obj_vfs_lfs1_file_t
|
||||
#define MP_TYPE_VFS_LFSx mp_type_vfs_lfs1
|
||||
#define MP_TYPE_VFS_LFSx_(s) mp_type_vfs_lfs1##s
|
||||
|
||||
typedef struct _mp_obj_vfs_lfs1_t {
|
||||
mp_obj_base_t base;
|
||||
mp_vfs_blockdev_t blockdev;
|
||||
vstr_t cur_dir;
|
||||
struct lfs1_config config;
|
||||
lfs1_t lfs;
|
||||
} mp_obj_vfs_lfs1_t;
|
||||
|
||||
typedef struct _mp_obj_vfs_lfs1_file_t {
|
||||
mp_obj_base_t base;
|
||||
mp_obj_vfs_lfs1_t *vfs;
|
||||
lfs1_file_t file;
|
||||
struct lfs1_file_config cfg;
|
||||
uint8_t file_buffer[0];
|
||||
} mp_obj_vfs_lfs1_file_t;
|
||||
|
||||
const char *mp_vfs_lfs1_make_path(mp_obj_vfs_lfs1_t *self, mp_obj_t path_in);
|
||||
mp_obj_t mp_vfs_lfs1_file_open(mp_obj_t self_in, mp_obj_t path_in, mp_obj_t mode_in);
|
||||
|
||||
#include "extmod/vfs_lfsx.c"
|
||||
#include "extmod/vfs_lfsx_file.c"
|
||||
|
||||
#undef LFS_BUILD_VERSION
|
||||
#undef LFSx_MACRO
|
||||
#undef LFSx_API
|
||||
#undef MP_VFS_LFSx
|
||||
#undef MP_OBJ_VFS_LFSx
|
||||
#undef MP_OBJ_VFS_LFSx_FILE
|
||||
#undef MP_TYPE_VFS_LFSx
|
||||
#undef MP_TYPE_VFS_LFSx_
|
||||
|
||||
#endif // MICROPY_VFS_LFS1
|
||||
|
||||
#if MICROPY_VFS_LFS2
|
||||
|
||||
#include "lib/littlefs/lfs2.h"
|
||||
|
||||
#define LFS_BUILD_VERSION (2)
|
||||
#define LFSx_MACRO(s) LFS2##s
|
||||
#define LFSx_API(s) lfs2_##s
|
||||
#define MP_VFS_LFSx(s) mp_vfs_lfs2_##s
|
||||
#define MP_OBJ_VFS_LFSx mp_obj_vfs_lfs2_t
|
||||
#define MP_OBJ_VFS_LFSx_FILE mp_obj_vfs_lfs2_file_t
|
||||
#define MP_TYPE_VFS_LFSx mp_type_vfs_lfs2
|
||||
#define MP_TYPE_VFS_LFSx_(s) mp_type_vfs_lfs2##s
|
||||
|
||||
// Attribute ids for lfs2_attr.type.
|
||||
#define LFS_ATTR_MTIME (1) // 64-bit little endian, nanoseconds since 1970/1/1
|
||||
|
||||
typedef struct _mp_obj_vfs_lfs2_t {
|
||||
mp_obj_base_t base;
|
||||
mp_vfs_blockdev_t blockdev;
|
||||
bool enable_mtime;
|
||||
vstr_t cur_dir;
|
||||
struct lfs2_config config;
|
||||
lfs2_t lfs;
|
||||
} mp_obj_vfs_lfs2_t;
|
||||
|
||||
typedef struct _mp_obj_vfs_lfs2_file_t {
|
||||
mp_obj_base_t base;
|
||||
mp_obj_vfs_lfs2_t *vfs;
|
||||
uint8_t mtime[8];
|
||||
lfs2_file_t file;
|
||||
struct lfs2_file_config cfg;
|
||||
struct lfs2_attr attrs[1];
|
||||
uint8_t file_buffer[0];
|
||||
} mp_obj_vfs_lfs2_file_t;
|
||||
|
||||
const char *mp_vfs_lfs2_make_path(mp_obj_vfs_lfs2_t *self, mp_obj_t path_in);
|
||||
mp_obj_t mp_vfs_lfs2_file_open(mp_obj_t self_in, mp_obj_t path_in, mp_obj_t mode_in);
|
||||
|
||||
STATIC void lfs_get_mtime(uint8_t buf[8]) {
|
||||
uint64_t ns = mp_hal_time_ns();
|
||||
// Store "ns" to "buf" in little-endian format (essentially htole64).
|
||||
for (size_t i = 0; i < 8; ++i) {
|
||||
buf[i] = ns;
|
||||
ns >>= 8;
|
||||
}
|
||||
}
|
||||
|
||||
#include "extmod/vfs_lfsx.c"
|
||||
#include "extmod/vfs_lfsx_file.c"
|
||||
|
||||
#endif // MICROPY_VFS_LFS2
|
||||
|
||||
#endif // MICROPY_VFS && (MICROPY_VFS_LFS1 || MICROPY_VFS_LFS2)
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 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.
|
||||
*/
|
||||
#ifndef MICROPY_INCLUDED_EXTMOD_VFS_LFS_H
|
||||
#define MICROPY_INCLUDED_EXTMOD_VFS_LFS_H
|
||||
|
||||
#include "py/obj.h"
|
||||
|
||||
extern const mp_obj_type_t mp_type_vfs_lfs1;
|
||||
extern const mp_obj_type_t mp_type_vfs_lfs1_fileio;
|
||||
extern const mp_obj_type_t mp_type_vfs_lfs1_textio;
|
||||
|
||||
extern const mp_obj_type_t mp_type_vfs_lfs2;
|
||||
extern const mp_obj_type_t mp_type_vfs_lfs2_fileio;
|
||||
extern const mp_obj_type_t mp_type_vfs_lfs2_textio;
|
||||
|
||||
#endif // MICROPY_INCLUDED_EXTMOD_VFS_LFS_H
|
|
@ -0,0 +1,491 @@
|
|||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019-2020 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 <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "py/runtime.h"
|
||||
#include "py/stream.h"
|
||||
#include "py/binary.h"
|
||||
#include "py/objarray.h"
|
||||
#include "py/objstr.h"
|
||||
#include "py/mperrno.h"
|
||||
#include "extmod/vfs.h"
|
||||
#include "lib/timeutils/timeutils.h"
|
||||
|
||||
STATIC int MP_VFS_LFSx(dev_ioctl)(const struct LFSx_API (config) * c, int cmd, int arg, bool must_return_int) {
|
||||
mp_obj_t ret = mp_vfs_blockdev_ioctl(c->context, cmd, arg);
|
||||
int ret_i = 0;
|
||||
if (must_return_int || ret != mp_const_none) {
|
||||
ret_i = mp_obj_get_int(ret);
|
||||
}
|
||||
return ret_i;
|
||||
}
|
||||
|
||||
STATIC int MP_VFS_LFSx(dev_read)(const struct LFSx_API (config) * c, LFSx_API(block_t) block, LFSx_API(off_t) off, void *buffer, LFSx_API(size_t) size) {
|
||||
return mp_vfs_blockdev_read_ext(c->context, block, off, size, buffer);
|
||||
}
|
||||
|
||||
STATIC int MP_VFS_LFSx(dev_prog)(const struct LFSx_API (config) * c, LFSx_API(block_t) block, LFSx_API(off_t) off, const void *buffer, LFSx_API(size_t) size) {
|
||||
return mp_vfs_blockdev_write_ext(c->context, block, off, size, buffer);
|
||||
}
|
||||
|
||||
STATIC int MP_VFS_LFSx(dev_erase)(const struct LFSx_API (config) * c, LFSx_API(block_t) block) {
|
||||
return MP_VFS_LFSx(dev_ioctl)(c, MP_BLOCKDEV_IOCTL_BLOCK_ERASE, block, true);
|
||||
}
|
||||
|
||||
STATIC int MP_VFS_LFSx(dev_sync)(const struct LFSx_API (config) * c) {
|
||||
return MP_VFS_LFSx(dev_ioctl)(c, MP_BLOCKDEV_IOCTL_SYNC, 0, false);
|
||||
}
|
||||
|
||||
STATIC void MP_VFS_LFSx(init_config)(MP_OBJ_VFS_LFSx * self, mp_obj_t bdev, size_t read_size, size_t prog_size, size_t lookahead) {
|
||||
self->blockdev.flags = MP_BLOCKDEV_FLAG_FREE_OBJ;
|
||||
mp_vfs_blockdev_init(&self->blockdev, bdev);
|
||||
|
||||
struct LFSx_API (config) * config = &self->config;
|
||||
memset(config, 0, sizeof(*config));
|
||||
|
||||
config->context = &self->blockdev;
|
||||
|
||||
config->read = MP_VFS_LFSx(dev_read);
|
||||
config->prog = MP_VFS_LFSx(dev_prog);
|
||||
config->erase = MP_VFS_LFSx(dev_erase);
|
||||
config->sync = MP_VFS_LFSx(dev_sync);
|
||||
|
||||
MP_VFS_LFSx(dev_ioctl)(config, MP_BLOCKDEV_IOCTL_INIT, 1, false); // initialise block device
|
||||
int bs = MP_VFS_LFSx(dev_ioctl)(config, MP_BLOCKDEV_IOCTL_BLOCK_SIZE, 0, true); // get block size
|
||||
int bc = MP_VFS_LFSx(dev_ioctl)(config, MP_BLOCKDEV_IOCTL_BLOCK_COUNT, 0, true); // get block count
|
||||
self->blockdev.block_size = bs;
|
||||
|
||||
config->read_size = read_size;
|
||||
config->prog_size = prog_size;
|
||||
config->block_size = bs;
|
||||
config->block_count = bc;
|
||||
|
||||
#if LFS_BUILD_VERSION == 1
|
||||
config->lookahead = lookahead;
|
||||
config->read_buffer = m_new(uint8_t, config->read_size);
|
||||
config->prog_buffer = m_new(uint8_t, config->prog_size);
|
||||
config->lookahead_buffer = m_new(uint8_t, config->lookahead / 8);
|
||||
#else
|
||||
config->block_cycles = 100;
|
||||
config->cache_size = 4 * MAX(read_size, prog_size);
|
||||
config->lookahead_size = lookahead;
|
||||
config->read_buffer = m_new(uint8_t, config->cache_size);
|
||||
config->prog_buffer = m_new(uint8_t, config->cache_size);
|
||||
config->lookahead_buffer = m_new(uint8_t, config->lookahead_size);
|
||||
#endif
|
||||
}
|
||||
|
||||
const char *MP_VFS_LFSx(make_path)(MP_OBJ_VFS_LFSx * self, mp_obj_t path_in) {
|
||||
const char *path = mp_obj_str_get_str(path_in);
|
||||
if (path[0] != '/') {
|
||||
size_t l = vstr_len(&self->cur_dir);
|
||||
if (l > 0) {
|
||||
vstr_add_str(&self->cur_dir, path);
|
||||
path = vstr_null_terminated_str(&self->cur_dir);
|
||||
self->cur_dir.len = l;
|
||||
}
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
STATIC mp_obj_t MP_VFS_LFSx(make_new)(const mp_obj_type_t * type, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(lfs_make_allowed_args)];
|
||||
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(lfs_make_allowed_args), lfs_make_allowed_args, args);
|
||||
|
||||
MP_OBJ_VFS_LFSx *self = m_new0(MP_OBJ_VFS_LFSx, 1);
|
||||
self->base.type = type;
|
||||
vstr_init(&self->cur_dir, 16);
|
||||
vstr_add_byte(&self->cur_dir, '/');
|
||||
#if LFS_BUILD_VERSION == 2
|
||||
self->enable_mtime = args[LFS_MAKE_ARG_mtime].u_bool;
|
||||
#endif
|
||||
MP_VFS_LFSx(init_config)(self, args[LFS_MAKE_ARG_bdev].u_obj,
|
||||
args[LFS_MAKE_ARG_readsize].u_int, args[LFS_MAKE_ARG_progsize].u_int, args[LFS_MAKE_ARG_lookahead].u_int);
|
||||
int ret = LFSx_API(mount)(&self->lfs, &self->config);
|
||||
if (ret < 0) {
|
||||
mp_raise_OSError(-ret);
|
||||
}
|
||||
return MP_OBJ_FROM_PTR(self);
|
||||
}
|
||||
|
||||
STATIC mp_obj_t MP_VFS_LFSx(mkfs)(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(lfs_make_allowed_args)];
|
||||
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(lfs_make_allowed_args), lfs_make_allowed_args, args);
|
||||
|
||||
MP_OBJ_VFS_LFSx self;
|
||||
MP_VFS_LFSx(init_config)(&self, args[LFS_MAKE_ARG_bdev].u_obj,
|
||||
args[LFS_MAKE_ARG_readsize].u_int, args[LFS_MAKE_ARG_progsize].u_int, args[LFS_MAKE_ARG_lookahead].u_int);
|
||||
int ret = LFSx_API(format)(&self.lfs, &self.config);
|
||||
if (ret < 0) {
|
||||
mp_raise_OSError(-ret);
|
||||
}
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(MP_VFS_LFSx(mkfs_fun_obj), 0, MP_VFS_LFSx(mkfs));
|
||||
STATIC MP_DEFINE_CONST_STATICMETHOD_OBJ(MP_VFS_LFSx(mkfs_obj), MP_ROM_PTR(&MP_VFS_LFSx(mkfs_fun_obj)));
|
||||
|
||||
// Implementation of mp_vfs_lfs_file_open is provided in vfs_lfsx_file.c
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_3(MP_VFS_LFSx(open_obj), MP_VFS_LFSx(file_open));
|
||||
|
||||
typedef struct MP_VFS_LFSx (_ilistdir_it_t) {
|
||||
mp_obj_base_t base;
|
||||
mp_fun_1_t iternext;
|
||||
bool is_str;
|
||||
MP_OBJ_VFS_LFSx *vfs;
|
||||
LFSx_API(dir_t) dir;
|
||||
} MP_VFS_LFSx(ilistdir_it_t);
|
||||
|
||||
STATIC mp_obj_t MP_VFS_LFSx(ilistdir_it_iternext)(mp_obj_t self_in) {
|
||||
MP_VFS_LFSx(ilistdir_it_t) * self = MP_OBJ_TO_PTR(self_in);
|
||||
|
||||
struct LFSx_API (info) info;
|
||||
for (;;) {
|
||||
int ret = LFSx_API(dir_read)(&self->vfs->lfs, &self->dir, &info);
|
||||
if (ret == 0) {
|
||||
LFSx_API(dir_close)(&self->vfs->lfs, &self->dir);
|
||||
return MP_OBJ_STOP_ITERATION;
|
||||
}
|
||||
if (!(info.name[0] == '.' && (info.name[1] == '\0'
|
||||
|| (info.name[1] == '.' && info.name[2] == '\0')))) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// make 4-tuple with info about this entry
|
||||
mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(4, NULL));
|
||||
if (self->is_str) {
|
||||
t->items[0] = mp_obj_new_str(info.name, strlen(info.name));
|
||||
} else {
|
||||
t->items[0] = mp_obj_new_bytes((const byte *)info.name, strlen(info.name));
|
||||
}
|
||||
t->items[1] = MP_OBJ_NEW_SMALL_INT(info.type == LFSx_MACRO(_TYPE_REG) ? MP_S_IFREG : MP_S_IFDIR);
|
||||
t->items[2] = MP_OBJ_NEW_SMALL_INT(0); // no inode number
|
||||
t->items[3] = MP_OBJ_NEW_SMALL_INT(info.size);
|
||||
|
||||
return MP_OBJ_FROM_PTR(t);
|
||||
}
|
||||
|
||||
STATIC mp_obj_t MP_VFS_LFSx(ilistdir_func)(size_t n_args, const mp_obj_t *args) {
|
||||
MP_OBJ_VFS_LFSx *self = MP_OBJ_TO_PTR(args[0]);
|
||||
bool is_str_type = true;
|
||||
const char *path;
|
||||
if (n_args == 2) {
|
||||
if (mp_obj_get_type(args[1]) == &mp_type_bytes) {
|
||||
is_str_type = false;
|
||||
}
|
||||
path = MP_VFS_LFSx(make_path)(self, args[1]);
|
||||
} else {
|
||||
path = vstr_null_terminated_str(&self->cur_dir);
|
||||
}
|
||||
|
||||
MP_VFS_LFSx(ilistdir_it_t) * iter = m_new_obj(MP_VFS_LFSx(ilistdir_it_t));
|
||||
iter->base.type = &mp_type_polymorph_iter;
|
||||
iter->iternext = MP_VFS_LFSx(ilistdir_it_iternext);
|
||||
iter->is_str = is_str_type;
|
||||
iter->vfs = self;
|
||||
int ret = LFSx_API(dir_open)(&self->lfs, &iter->dir, path);
|
||||
if (ret < 0) {
|
||||
mp_raise_OSError(-ret);
|
||||
}
|
||||
return MP_OBJ_FROM_PTR(iter);
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(MP_VFS_LFSx(ilistdir_obj), 1, 2, MP_VFS_LFSx(ilistdir_func));
|
||||
|
||||
STATIC mp_obj_t MP_VFS_LFSx(remove)(mp_obj_t self_in, mp_obj_t path_in) {
|
||||
MP_OBJ_VFS_LFSx *self = MP_OBJ_TO_PTR(self_in);
|
||||
const char *path = MP_VFS_LFSx(make_path)(self, path_in);
|
||||
int ret = LFSx_API(remove)(&self->lfs, path);
|
||||
if (ret < 0) {
|
||||
mp_raise_OSError(-ret);
|
||||
}
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_2(MP_VFS_LFSx(remove_obj), MP_VFS_LFSx(remove));
|
||||
|
||||
STATIC mp_obj_t MP_VFS_LFSx(rmdir)(mp_obj_t self_in, mp_obj_t path_in) {
|
||||
MP_OBJ_VFS_LFSx *self = MP_OBJ_TO_PTR(self_in);
|
||||
const char *path = MP_VFS_LFSx(make_path)(self, path_in);
|
||||
int ret = LFSx_API(remove)(&self->lfs, path);
|
||||
if (ret < 0) {
|
||||
mp_raise_OSError(-ret);
|
||||
}
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_2(MP_VFS_LFSx(rmdir_obj), MP_VFS_LFSx(rmdir));
|
||||
|
||||
STATIC mp_obj_t MP_VFS_LFSx(rename)(mp_obj_t self_in, mp_obj_t path_old_in, mp_obj_t path_new_in) {
|
||||
MP_OBJ_VFS_LFSx *self = MP_OBJ_TO_PTR(self_in);
|
||||
const char *path_old = MP_VFS_LFSx(make_path)(self, path_old_in);
|
||||
const char *path = mp_obj_str_get_str(path_new_in);
|
||||
vstr_t path_new;
|
||||
vstr_init(&path_new, vstr_len(&self->cur_dir));
|
||||
if (path[0] != '/') {
|
||||
vstr_add_strn(&path_new, vstr_str(&self->cur_dir), vstr_len(&self->cur_dir));
|
||||
}
|
||||
vstr_add_str(&path_new, path);
|
||||
int ret = LFSx_API(rename)(&self->lfs, path_old, vstr_null_terminated_str(&path_new));
|
||||
vstr_clear(&path_new);
|
||||
if (ret < 0) {
|
||||
mp_raise_OSError(-ret);
|
||||
}
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_3(MP_VFS_LFSx(rename_obj), MP_VFS_LFSx(rename));
|
||||
|
||||
STATIC mp_obj_t MP_VFS_LFSx(mkdir)(mp_obj_t self_in, mp_obj_t path_o) {
|
||||
MP_OBJ_VFS_LFSx *self = MP_OBJ_TO_PTR(self_in);
|
||||
const char *path = MP_VFS_LFSx(make_path)(self, path_o);
|
||||
int ret = LFSx_API(mkdir)(&self->lfs, path);
|
||||
if (ret < 0) {
|
||||
mp_raise_OSError(-ret);
|
||||
}
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_2(MP_VFS_LFSx(mkdir_obj), MP_VFS_LFSx(mkdir));
|
||||
|
||||
STATIC mp_obj_t MP_VFS_LFSx(chdir)(mp_obj_t self_in, mp_obj_t path_in) {
|
||||
MP_OBJ_VFS_LFSx *self = MP_OBJ_TO_PTR(self_in);
|
||||
|
||||
// Check path exists
|
||||
const char *path = MP_VFS_LFSx(make_path)(self, path_in);
|
||||
if (path[1] != '\0') {
|
||||
// Not at root, check it exists
|
||||
struct LFSx_API (info) info;
|
||||
int ret = LFSx_API(stat)(&self->lfs, path, &info);
|
||||
if (ret < 0 || info.type != LFSx_MACRO(_TYPE_DIR)) {
|
||||
mp_raise_OSError(-MP_ENOENT);
|
||||
}
|
||||
}
|
||||
|
||||
// Update cur_dir with new path
|
||||
if (path == vstr_str(&self->cur_dir)) {
|
||||
self->cur_dir.len = strlen(path);
|
||||
} else {
|
||||
vstr_reset(&self->cur_dir);
|
||||
vstr_add_str(&self->cur_dir, path);
|
||||
}
|
||||
|
||||
// If not at root add trailing / to make it easy to build paths
|
||||
// and then normalise the path
|
||||
if (vstr_len(&self->cur_dir) != 1) {
|
||||
vstr_add_byte(&self->cur_dir, '/');
|
||||
|
||||
#define CWD_LEN (vstr_len(&self->cur_dir))
|
||||
size_t to = 1;
|
||||
size_t from = 1;
|
||||
char *cwd = vstr_str(&self->cur_dir);
|
||||
while (from < CWD_LEN) {
|
||||
for (; cwd[from] == '/' && from < CWD_LEN; ++from) {
|
||||
// Scan for the start
|
||||
}
|
||||
if (from > to) {
|
||||
// Found excessive slash chars, squeeze them out
|
||||
vstr_cut_out_bytes(&self->cur_dir, to, from - to);
|
||||
from = to;
|
||||
}
|
||||
for (; cwd[from] != '/' && from < CWD_LEN; ++from) {
|
||||
// Scan for the next /
|
||||
}
|
||||
if ((from - to) == 1 && cwd[to] == '.') {
|
||||
// './', ignore
|
||||
vstr_cut_out_bytes(&self->cur_dir, to, ++from - to);
|
||||
from = to;
|
||||
} else if ((from - to) == 2 && cwd[to] == '.' && cwd[to + 1] == '.') {
|
||||
// '../', skip back
|
||||
if (to > 1) {
|
||||
// Only skip back if not at the tip
|
||||
for (--to; to > 1 && cwd[to - 1] != '/'; --to) {
|
||||
// Skip back
|
||||
}
|
||||
}
|
||||
vstr_cut_out_bytes(&self->cur_dir, to, ++from - to);
|
||||
from = to;
|
||||
} else {
|
||||
// Normal element, keep it and just move the offset
|
||||
to = ++from;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_2(MP_VFS_LFSx(chdir_obj), MP_VFS_LFSx(chdir));
|
||||
|
||||
STATIC mp_obj_t MP_VFS_LFSx(getcwd)(mp_obj_t self_in) {
|
||||
MP_OBJ_VFS_LFSx *self = MP_OBJ_TO_PTR(self_in);
|
||||
if (vstr_len(&self->cur_dir) == 1) {
|
||||
return MP_OBJ_NEW_QSTR(MP_QSTR__slash_);
|
||||
} else {
|
||||
// don't include trailing /
|
||||
return mp_obj_new_str(self->cur_dir.buf, self->cur_dir.len - 1);
|
||||
}
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(MP_VFS_LFSx(getcwd_obj), MP_VFS_LFSx(getcwd));
|
||||
|
||||
STATIC mp_obj_t MP_VFS_LFSx(stat)(mp_obj_t self_in, mp_obj_t path_in) {
|
||||
MP_OBJ_VFS_LFSx *self = MP_OBJ_TO_PTR(self_in);
|
||||
const char *path = MP_VFS_LFSx(make_path)(self, path_in);
|
||||
struct LFSx_API (info) info;
|
||||
int ret = LFSx_API(stat)(&self->lfs, path, &info);
|
||||
if (ret < 0) {
|
||||
mp_raise_OSError(-ret);
|
||||
}
|
||||
|
||||
mp_uint_t mtime = 0;
|
||||
#if LFS_BUILD_VERSION == 2
|
||||
uint8_t mtime_buf[8];
|
||||
lfs2_ssize_t sz = lfs2_getattr(&self->lfs, path, LFS_ATTR_MTIME, &mtime_buf, sizeof(mtime_buf));
|
||||
if (sz == sizeof(mtime_buf)) {
|
||||
uint64_t ns = 0;
|
||||
for (size_t i = sizeof(mtime_buf); i > 0; --i) {
|
||||
ns = ns << 8 | mtime_buf[i - 1];
|
||||
}
|
||||
mtime = timeutils_seconds_since_2000_from_nanoseconds_since_1970(ns);
|
||||
#if MICROPY_EPOCH_IS_1970
|
||||
mtime += TIMEUTILS_SECONDS_1970_TO_2000;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(10, NULL));
|
||||
t->items[0] = MP_OBJ_NEW_SMALL_INT(info.type == LFSx_MACRO(_TYPE_REG) ? MP_S_IFREG : MP_S_IFDIR); // st_mode
|
||||
t->items[1] = MP_OBJ_NEW_SMALL_INT(0); // st_ino
|
||||
t->items[2] = MP_OBJ_NEW_SMALL_INT(0); // st_dev
|
||||
t->items[3] = MP_OBJ_NEW_SMALL_INT(0); // st_nlink
|
||||
t->items[4] = MP_OBJ_NEW_SMALL_INT(0); // st_uid
|
||||
t->items[5] = MP_OBJ_NEW_SMALL_INT(0); // st_gid
|
||||
t->items[6] = mp_obj_new_int_from_uint(info.size); // st_size
|
||||
t->items[7] = mp_obj_new_int_from_uint(mtime); // st_atime
|
||||
t->items[8] = mp_obj_new_int_from_uint(mtime); // st_mtime
|
||||
t->items[9] = mp_obj_new_int_from_uint(mtime); // st_ctime
|
||||
|
||||
return MP_OBJ_FROM_PTR(t);
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_2(MP_VFS_LFSx(stat_obj), MP_VFS_LFSx(stat));
|
||||
|
||||
STATIC int LFSx_API(traverse_cb)(void *data, LFSx_API(block_t) bl) {
|
||||
(void)bl;
|
||||
uint32_t *n = (uint32_t *)data;
|
||||
*n += 1;
|
||||
return LFSx_MACRO(_ERR_OK);
|
||||
}
|
||||
|
||||
STATIC mp_obj_t MP_VFS_LFSx(statvfs)(mp_obj_t self_in, mp_obj_t path_in) {
|
||||
(void)path_in;
|
||||
MP_OBJ_VFS_LFSx *self = MP_OBJ_TO_PTR(self_in);
|
||||
uint32_t n_used_blocks = 0;
|
||||
#if LFS_BUILD_VERSION == 1
|
||||
int ret = LFSx_API(traverse)(&self->lfs, LFSx_API(traverse_cb), &n_used_blocks);
|
||||
#else
|
||||
int ret = LFSx_API(fs_traverse)(&self->lfs, LFSx_API(traverse_cb), &n_used_blocks);
|
||||
#endif
|
||||
if (ret < 0) {
|
||||
mp_raise_OSError(-ret);
|
||||
}
|
||||
|
||||
mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(10, NULL));
|
||||
t->items[0] = MP_OBJ_NEW_SMALL_INT(self->lfs.cfg->block_size); // f_bsize
|
||||
t->items[1] = t->items[0]; // f_frsize
|
||||
t->items[2] = MP_OBJ_NEW_SMALL_INT(self->lfs.cfg->block_count); // f_blocks
|
||||
t->items[3] = MP_OBJ_NEW_SMALL_INT(self->lfs.cfg->block_count - n_used_blocks); // f_bfree
|
||||
t->items[4] = t->items[3]; // f_bavail
|
||||
t->items[5] = MP_OBJ_NEW_SMALL_INT(0); // f_files
|
||||
t->items[6] = MP_OBJ_NEW_SMALL_INT(0); // f_ffree
|
||||
t->items[7] = MP_OBJ_NEW_SMALL_INT(0); // f_favail
|
||||
t->items[8] = MP_OBJ_NEW_SMALL_INT(0); // f_flags
|
||||
t->items[9] = MP_OBJ_NEW_SMALL_INT(LFSx_MACRO(_NAME_MAX)); // f_namemax
|
||||
|
||||
return MP_OBJ_FROM_PTR(t);
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_2(MP_VFS_LFSx(statvfs_obj), MP_VFS_LFSx(statvfs));
|
||||
|
||||
STATIC mp_obj_t MP_VFS_LFSx(mount)(mp_obj_t self_in, mp_obj_t readonly, mp_obj_t mkfs) {
|
||||
(void)self_in;
|
||||
(void)readonly;
|
||||
(void)mkfs;
|
||||
// already called LFSx_API(mount) in MP_VFS_LFSx(make_new)
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_3(MP_VFS_LFSx(mount_obj), MP_VFS_LFSx(mount));
|
||||
|
||||
STATIC mp_obj_t MP_VFS_LFSx(umount)(mp_obj_t self_in) {
|
||||
MP_OBJ_VFS_LFSx *self = MP_OBJ_TO_PTR(self_in);
|
||||
// LFS unmount never fails
|
||||
LFSx_API(unmount)(&self->lfs);
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(MP_VFS_LFSx(umount_obj), MP_VFS_LFSx(umount));
|
||||
|
||||
STATIC const mp_rom_map_elem_t MP_VFS_LFSx(locals_dict_table)[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR_mkfs), MP_ROM_PTR(&MP_VFS_LFSx(mkfs_obj)) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_open), MP_ROM_PTR(&MP_VFS_LFSx(open_obj)) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_ilistdir), MP_ROM_PTR(&MP_VFS_LFSx(ilistdir_obj)) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_mkdir), MP_ROM_PTR(&MP_VFS_LFSx(mkdir_obj)) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_rmdir), MP_ROM_PTR(&MP_VFS_LFSx(rmdir_obj)) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_chdir), MP_ROM_PTR(&MP_VFS_LFSx(chdir_obj)) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_getcwd), MP_ROM_PTR(&MP_VFS_LFSx(getcwd_obj)) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_remove), MP_ROM_PTR(&MP_VFS_LFSx(remove_obj)) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_rename), MP_ROM_PTR(&MP_VFS_LFSx(rename_obj)) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_stat), MP_ROM_PTR(&MP_VFS_LFSx(stat_obj)) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_statvfs), MP_ROM_PTR(&MP_VFS_LFSx(statvfs_obj)) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_mount), MP_ROM_PTR(&MP_VFS_LFSx(mount_obj)) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_umount), MP_ROM_PTR(&MP_VFS_LFSx(umount_obj)) },
|
||||
};
|
||||
STATIC MP_DEFINE_CONST_DICT(MP_VFS_LFSx(locals_dict), MP_VFS_LFSx(locals_dict_table));
|
||||
|
||||
STATIC mp_import_stat_t MP_VFS_LFSx(import_stat)(void *self_in, const char *path) {
|
||||
MP_OBJ_VFS_LFSx *self = self_in;
|
||||
struct LFSx_API (info) info;
|
||||
mp_obj_str_t path_obj = { { &mp_type_str }, 0, 0, (const byte *)path };
|
||||
path = MP_VFS_LFSx(make_path)(self, MP_OBJ_FROM_PTR(&path_obj));
|
||||
int ret = LFSx_API(stat)(&self->lfs, path, &info);
|
||||
if (ret == 0) {
|
||||
if (info.type == LFSx_MACRO(_TYPE_REG)) {
|
||||
return MP_IMPORT_STAT_FILE;
|
||||
} else {
|
||||
return MP_IMPORT_STAT_DIR;
|
||||
}
|
||||
}
|
||||
return MP_IMPORT_STAT_NO_EXIST;
|
||||
}
|
||||
|
||||
STATIC const mp_vfs_proto_t MP_VFS_LFSx(proto) = {
|
||||
.import_stat = MP_VFS_LFSx(import_stat),
|
||||
};
|
||||
|
||||
const mp_obj_type_t MP_TYPE_VFS_LFSx = {
|
||||
{ &mp_type_type },
|
||||
#if LFS_BUILD_VERSION == 1
|
||||
.name = MP_QSTR_VfsLfs1,
|
||||
#else
|
||||
.name = MP_QSTR_VfsLfs2,
|
||||
#endif
|
||||
.make_new = MP_VFS_LFSx(make_new),
|
||||
.protocol = &MP_VFS_LFSx(proto),
|
||||
.locals_dict = (mp_obj_dict_t *)&MP_VFS_LFSx(locals_dict),
|
||||
};
|
|
@ -0,0 +1,254 @@
|
|||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019-2020 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 <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "py/runtime.h"
|
||||
#include "py/stream.h"
|
||||
#include "py/mperrno.h"
|
||||
#include "extmod/vfs.h"
|
||||
|
||||
STATIC void MP_VFS_LFSx(check_open)(MP_OBJ_VFS_LFSx_FILE * self) {
|
||||
if (self->vfs == NULL) {
|
||||
mp_raise_ValueError(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
STATIC void MP_VFS_LFSx(file_print)(const mp_print_t * print, mp_obj_t self_in, mp_print_kind_t kind) {
|
||||
(void)self_in;
|
||||
(void)kind;
|
||||
mp_printf(print, "<io.%s>", mp_obj_get_type_str(self_in));
|
||||
}
|
||||
|
||||
mp_obj_t MP_VFS_LFSx(file_open)(mp_obj_t self_in, mp_obj_t path_in, mp_obj_t mode_in) {
|
||||
MP_OBJ_VFS_LFSx *self = MP_OBJ_TO_PTR(self_in);
|
||||
|
||||
int flags = 0;
|
||||
const mp_obj_type_t *type = &MP_TYPE_VFS_LFSx_(_textio);
|
||||
const char *mode_str = mp_obj_str_get_str(mode_in);
|
||||
for (; *mode_str; ++mode_str) {
|
||||
int new_flags = 0;
|
||||
switch (*mode_str) {
|
||||
case 'r':
|
||||
new_flags = LFSx_MACRO(_O_RDONLY);
|
||||
break;
|
||||
case 'w':
|
||||
new_flags = LFSx_MACRO(_O_WRONLY) | LFSx_MACRO(_O_CREAT) | LFSx_MACRO(_O_TRUNC);
|
||||
break;
|
||||
case 'x':
|
||||
new_flags = LFSx_MACRO(_O_WRONLY) | LFSx_MACRO(_O_CREAT) | LFSx_MACRO(_O_EXCL);
|
||||
break;
|
||||
case 'a':
|
||||
new_flags = LFSx_MACRO(_O_WRONLY) | LFSx_MACRO(_O_CREAT) | LFSx_MACRO(_O_APPEND);
|
||||
break;
|
||||
case '+':
|
||||
flags |= LFSx_MACRO(_O_RDWR);
|
||||
break;
|
||||
#if MICROPY_PY_IO_FILEIO
|
||||
case 'b':
|
||||
type = &MP_TYPE_VFS_LFSx_(_fileio);
|
||||
break;
|
||||
#endif
|
||||
case 't':
|
||||
type = &MP_TYPE_VFS_LFSx_(_textio);
|
||||
break;
|
||||
}
|
||||
if (new_flags) {
|
||||
if (flags) {
|
||||
mp_raise_ValueError(NULL);
|
||||
}
|
||||
flags = new_flags;
|
||||
}
|
||||
}
|
||||
if (flags == 0) {
|
||||
flags = LFSx_MACRO(_O_RDONLY);
|
||||
}
|
||||
|
||||
#if LFS_BUILD_VERSION == 1
|
||||
MP_OBJ_VFS_LFSx_FILE *o = m_new_obj_var_with_finaliser(MP_OBJ_VFS_LFSx_FILE, uint8_t, self->lfs.cfg->prog_size);
|
||||
#else
|
||||
MP_OBJ_VFS_LFSx_FILE *o = m_new_obj_var_with_finaliser(MP_OBJ_VFS_LFSx_FILE, uint8_t, self->lfs.cfg->cache_size);
|
||||
#endif
|
||||
o->base.type = type;
|
||||
o->vfs = self;
|
||||
#if !MICROPY_GC_CONSERVATIVE_CLEAR
|
||||
memset(&o->file, 0, sizeof(o->file));
|
||||
memset(&o->cfg, 0, sizeof(o->cfg));
|
||||
#endif
|
||||
o->cfg.buffer = &o->file_buffer[0];
|
||||
|
||||
#if LFS_BUILD_VERSION == 2
|
||||
if (self->enable_mtime) {
|
||||
lfs_get_mtime(&o->mtime[0]);
|
||||
o->attrs[0].type = LFS_ATTR_MTIME;
|
||||
o->attrs[0].buffer = &o->mtime[0];
|
||||
o->attrs[0].size = sizeof(o->mtime);
|
||||
o->cfg.attrs = &o->attrs[0];
|
||||
o->cfg.attr_count = MP_ARRAY_SIZE(o->attrs);
|
||||
}
|
||||
#endif
|
||||
|
||||
const char *path = MP_VFS_LFSx(make_path)(self, path_in);
|
||||
int ret = LFSx_API(file_opencfg)(&self->lfs, &o->file, path, flags, &o->cfg);
|
||||
if (ret < 0) {
|
||||
o->vfs = NULL;
|
||||
mp_raise_OSError(-ret);
|
||||
}
|
||||
|
||||
return MP_OBJ_FROM_PTR(o);
|
||||
}
|
||||
|
||||
STATIC mp_obj_t MP_VFS_LFSx(file___exit__)(size_t n_args, const mp_obj_t *args) {
|
||||
(void)n_args;
|
||||
return mp_stream_close(args[0]);
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(MP_VFS_LFSx(file___exit___obj), 4, 4, MP_VFS_LFSx(file___exit__));
|
||||
|
||||
STATIC mp_uint_t MP_VFS_LFSx(file_read)(mp_obj_t self_in, void *buf, mp_uint_t size, int *errcode) {
|
||||
MP_OBJ_VFS_LFSx_FILE *self = MP_OBJ_TO_PTR(self_in);
|
||||
MP_VFS_LFSx(check_open)(self);
|
||||
LFSx_API(ssize_t) sz = LFSx_API(file_read)(&self->vfs->lfs, &self->file, buf, size);
|
||||
if (sz < 0) {
|
||||
*errcode = -sz;
|
||||
return MP_STREAM_ERROR;
|
||||
}
|
||||
return sz;
|
||||
}
|
||||
|
||||
STATIC mp_uint_t MP_VFS_LFSx(file_write)(mp_obj_t self_in, const void *buf, mp_uint_t size, int *errcode) {
|
||||
MP_OBJ_VFS_LFSx_FILE *self = MP_OBJ_TO_PTR(self_in);
|
||||
MP_VFS_LFSx(check_open)(self);
|
||||
#if LFS_BUILD_VERSION == 2
|
||||
if (self->vfs->enable_mtime) {
|
||||
lfs_get_mtime(&self->mtime[0]);
|
||||
}
|
||||
#endif
|
||||
LFSx_API(ssize_t) sz = LFSx_API(file_write)(&self->vfs->lfs, &self->file, buf, size);
|
||||
if (sz < 0) {
|
||||
*errcode = -sz;
|
||||
return MP_STREAM_ERROR;
|
||||
}
|
||||
return sz;
|
||||
}
|
||||
|
||||
STATIC mp_uint_t MP_VFS_LFSx(file_ioctl)(mp_obj_t self_in, mp_uint_t request, uintptr_t arg, int *errcode) {
|
||||
MP_OBJ_VFS_LFSx_FILE *self = MP_OBJ_TO_PTR(self_in);
|
||||
|
||||
if (request != MP_STREAM_CLOSE) {
|
||||
MP_VFS_LFSx(check_open)(self);
|
||||
}
|
||||
|
||||
if (request == MP_STREAM_SEEK) {
|
||||
struct mp_stream_seek_t *s = (struct mp_stream_seek_t *)(uintptr_t)arg;
|
||||
int res = LFSx_API(file_seek)(&self->vfs->lfs, &self->file, s->offset, s->whence);
|
||||
if (res < 0) {
|
||||
*errcode = -res;
|
||||
return MP_STREAM_ERROR;
|
||||
}
|
||||
res = LFSx_API(file_tell)(&self->vfs->lfs, &self->file);
|
||||
if (res < 0) {
|
||||
*errcode = -res;
|
||||
return MP_STREAM_ERROR;
|
||||
}
|
||||
s->offset = res;
|
||||
return 0;
|
||||
} else if (request == MP_STREAM_FLUSH) {
|
||||
int res = LFSx_API(file_sync)(&self->vfs->lfs, &self->file);
|
||||
if (res < 0) {
|
||||
*errcode = -res;
|
||||
return MP_STREAM_ERROR;
|
||||
}
|
||||
return 0;
|
||||
} else if (request == MP_STREAM_CLOSE) {
|
||||
if (self->vfs == NULL) {
|
||||
return 0;
|
||||
}
|
||||
int res = LFSx_API(file_close)(&self->vfs->lfs, &self->file);
|
||||
self->vfs = NULL; // indicate a closed file
|
||||
if (res < 0) {
|
||||
*errcode = -res;
|
||||
return MP_STREAM_ERROR;
|
||||
}
|
||||
return 0;
|
||||
} else {
|
||||
*errcode = MP_EINVAL;
|
||||
return MP_STREAM_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
STATIC const mp_rom_map_elem_t MP_VFS_LFSx(file_locals_dict_table)[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_readlines), MP_ROM_PTR(&mp_stream_unbuffered_readlines_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_flush), MP_ROM_PTR(&mp_stream_flush_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&mp_stream_close_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_seek), MP_ROM_PTR(&mp_stream_seek_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_tell), MP_ROM_PTR(&mp_stream_tell_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&mp_stream_close_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&mp_identity_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&MP_VFS_LFSx(file___exit___obj)) },
|
||||
};
|
||||
STATIC MP_DEFINE_CONST_DICT(MP_VFS_LFSx(file_locals_dict), MP_VFS_LFSx(file_locals_dict_table));
|
||||
|
||||
#if MICROPY_PY_IO_FILEIO
|
||||
STATIC const mp_stream_p_t MP_VFS_LFSx(fileio_stream_p) = {
|
||||
MP_PROTO_IMPLEMENT(MP_QSTR_protocol_stream)
|
||||
.read = MP_VFS_LFSx(file_read),
|
||||
.write = MP_VFS_LFSx(file_write),
|
||||
.ioctl = MP_VFS_LFSx(file_ioctl),
|
||||
};
|
||||
|
||||
const mp_obj_type_t MP_TYPE_VFS_LFSx_(_fileio) = {
|
||||
{ &mp_type_type },
|
||||
.name = MP_QSTR_FileIO,
|
||||
.print = MP_VFS_LFSx(file_print),
|
||||
.getiter = mp_identity_getiter,
|
||||
.iternext = mp_stream_unbuffered_iter,
|
||||
.protocol = &MP_VFS_LFSx(fileio_stream_p),
|
||||
.locals_dict = (mp_obj_dict_t *)&MP_VFS_LFSx(file_locals_dict),
|
||||
};
|
||||
#endif
|
||||
|
||||
STATIC const mp_stream_p_t MP_VFS_LFSx(textio_stream_p) = {
|
||||
MP_PROTO_IMPLEMENT(MP_QSTR_protocol_stream)
|
||||
.read = MP_VFS_LFSx(file_read),
|
||||
.write = MP_VFS_LFSx(file_write),
|
||||
.ioctl = MP_VFS_LFSx(file_ioctl),
|
||||
.is_text = true,
|
||||
};
|
||||
|
||||
const mp_obj_type_t MP_TYPE_VFS_LFSx_(_textio) = {
|
||||
{ &mp_type_type },
|
||||
.name = MP_QSTR_TextIOWrapper,
|
||||
.print = MP_VFS_LFSx(file_print),
|
||||
.getiter = mp_identity_getiter,
|
||||
.iternext = mp_stream_unbuffered_iter,
|
||||
.protocol = &MP_VFS_LFSx(textio_stream_p),
|
||||
.locals_dict = (mp_obj_dict_t *)&MP_VFS_LFSx(file_locals_dict),
|
||||
};
|
|
@ -5,6 +5,8 @@
|
|||
|
||||
#include "py/runtime.h"
|
||||
#include "py/mperrno.h"
|
||||
#include "py/mphal.h"
|
||||
#include "py/mpthread.h"
|
||||
#include "extmod/vfs.h"
|
||||
#include "extmod/vfs_posix.h"
|
||||
|
||||
|
@ -149,12 +151,15 @@ STATIC mp_obj_t vfs_posix_ilistdir_it_iternext(mp_obj_t self_in) {
|
|||
}
|
||||
|
||||
for (;;) {
|
||||
MP_THREAD_GIL_EXIT();
|
||||
struct dirent *dirent = readdir(self->dir);
|
||||
if (dirent == NULL) {
|
||||
closedir(self->dir);
|
||||
MP_THREAD_GIL_ENTER();
|
||||
self->dir = NULL;
|
||||
return MP_OBJ_STOP_ITERATION;
|
||||
}
|
||||
MP_THREAD_GIL_ENTER();
|
||||
const char *fn = dirent->d_name;
|
||||
|
||||
if (fn[0] == '.' && (fn[1] == 0 || fn[1] == '.')) {
|
||||
|
@ -208,7 +213,9 @@ STATIC mp_obj_t vfs_posix_ilistdir(mp_obj_t self_in, mp_obj_t path_in) {
|
|||
if (path[0] == '\0') {
|
||||
path = ".";
|
||||
}
|
||||
MP_THREAD_GIL_EXIT();
|
||||
iter->dir = opendir(path);
|
||||
MP_THREAD_GIL_ENTER();
|
||||
if (iter->dir == NULL) {
|
||||
mp_raise_OSError(errno);
|
||||
}
|
||||
|
@ -224,7 +231,10 @@ typedef struct _mp_obj_listdir_t {
|
|||
|
||||
STATIC mp_obj_t vfs_posix_mkdir(mp_obj_t self_in, mp_obj_t path_in) {
|
||||
mp_obj_vfs_posix_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
int ret = mkdir(vfs_posix_get_path_str(self, path_in), 0777);
|
||||
const char *path = vfs_posix_get_path_str(self, path_in);
|
||||
MP_THREAD_GIL_EXIT();
|
||||
int ret = mkdir(path, 0777);
|
||||
MP_THREAD_GIL_ENTER();
|
||||
if (ret != 0) {
|
||||
mp_raise_OSError(errno);
|
||||
}
|
||||
|
@ -241,7 +251,9 @@ STATIC mp_obj_t vfs_posix_rename(mp_obj_t self_in, mp_obj_t old_path_in, mp_obj_
|
|||
mp_obj_vfs_posix_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
const char *old_path = vfs_posix_get_path_str(self, old_path_in);
|
||||
const char *new_path = vfs_posix_get_path_str(self, new_path_in);
|
||||
MP_THREAD_GIL_EXIT();
|
||||
int ret = rename(old_path, new_path);
|
||||
MP_THREAD_GIL_ENTER();
|
||||
if (ret != 0) {
|
||||
mp_raise_OSError(errno);
|
||||
}
|
||||
|
@ -257,21 +269,20 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_2(vfs_posix_rmdir_obj, vfs_posix_rmdir);
|
|||
STATIC mp_obj_t vfs_posix_stat(mp_obj_t self_in, mp_obj_t path_in) {
|
||||
mp_obj_vfs_posix_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
struct stat sb;
|
||||
int ret = stat(vfs_posix_get_path_str(self, path_in), &sb);
|
||||
if (ret != 0) {
|
||||
mp_raise_OSError(errno);
|
||||
}
|
||||
const char *path = vfs_posix_get_path_str(self, path_in);
|
||||
int ret;
|
||||
MP_HAL_RETRY_SYSCALL(ret, stat(path, &sb), mp_raise_OSError(err));
|
||||
mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(10, NULL));
|
||||
t->items[0] = MP_OBJ_NEW_SMALL_INT(sb.st_mode);
|
||||
t->items[1] = MP_OBJ_NEW_SMALL_INT(sb.st_ino);
|
||||
t->items[2] = MP_OBJ_NEW_SMALL_INT(sb.st_dev);
|
||||
t->items[3] = MP_OBJ_NEW_SMALL_INT(sb.st_nlink);
|
||||
t->items[4] = MP_OBJ_NEW_SMALL_INT(sb.st_uid);
|
||||
t->items[5] = MP_OBJ_NEW_SMALL_INT(sb.st_gid);
|
||||
t->items[6] = MP_OBJ_NEW_SMALL_INT(sb.st_size);
|
||||
t->items[7] = MP_OBJ_NEW_SMALL_INT(sb.st_atime);
|
||||
t->items[8] = MP_OBJ_NEW_SMALL_INT(sb.st_mtime);
|
||||
t->items[9] = MP_OBJ_NEW_SMALL_INT(sb.st_ctime);
|
||||
t->items[1] = mp_obj_new_int_from_uint(sb.st_ino);
|
||||
t->items[2] = mp_obj_new_int_from_uint(sb.st_dev);
|
||||
t->items[3] = mp_obj_new_int_from_uint(sb.st_nlink);
|
||||
t->items[4] = mp_obj_new_int_from_uint(sb.st_uid);
|
||||
t->items[5] = mp_obj_new_int_from_uint(sb.st_gid);
|
||||
t->items[6] = mp_obj_new_int_from_uint(sb.st_size);
|
||||
t->items[7] = mp_obj_new_int_from_uint(sb.st_atime);
|
||||
t->items[8] = mp_obj_new_int_from_uint(sb.st_mtime);
|
||||
t->items[9] = mp_obj_new_int_from_uint(sb.st_ctime);
|
||||
return MP_OBJ_FROM_PTR(t);
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_2(vfs_posix_stat_obj, vfs_posix_stat);
|
||||
|
@ -300,10 +311,8 @@ STATIC mp_obj_t vfs_posix_statvfs(mp_obj_t self_in, mp_obj_t path_in) {
|
|||
mp_obj_vfs_posix_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
STRUCT_STATVFS sb;
|
||||
const char *path = vfs_posix_get_path_str(self, path_in);
|
||||
int ret = STATVFS(path, &sb);
|
||||
if (ret != 0) {
|
||||
mp_raise_OSError(errno);
|
||||
}
|
||||
int ret;
|
||||
MP_HAL_RETRY_SYSCALL(ret, STATVFS(path, &sb), mp_raise_OSError(err));
|
||||
mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(10, NULL));
|
||||
t->items[0] = MP_OBJ_NEW_SMALL_INT(sb.f_bsize);
|
||||
t->items[1] = MP_OBJ_NEW_SMALL_INT(sb.f_frsize);
|
||||
|
|
|
@ -3,14 +3,17 @@
|
|||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include "py/mphal.h"
|
||||
#include "py/mpthread.h"
|
||||
#include "py/runtime.h"
|
||||
#include "py/stream.h"
|
||||
#include "extmod/vfs_posix.h"
|
||||
#include "supervisor/shared/translate.h"
|
||||
|
||||
#if defined(MICROPY_VFS_POSIX) && MICROPY_VFS_POSIX
|
||||
#if (defined(MICROPY_VFS_POSIX) && MICROPY_VFS_POSIX) || (defined(MICROPY_VFS_POSIX_FILE) && MICROPY_VFS_POSIX_FILE)
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#define fsync _commit
|
||||
|
@ -24,7 +27,7 @@ typedef struct _mp_obj_vfs_posix_file_t {
|
|||
#ifdef MICROPY_CPYTHON_COMPAT
|
||||
STATIC void check_fd_is_open(const mp_obj_vfs_posix_file_t *o) {
|
||||
if (o->fd < 0) {
|
||||
mp_raise_ValueError(translate("I/O operation on closed file"));
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("I/O operation on closed file"));
|
||||
}
|
||||
}
|
||||
#else
|
||||
|
@ -80,17 +83,15 @@ mp_obj_t mp_vfs_posix_file_open(const mp_obj_type_t *type, mp_obj_t file_in, mp_
|
|||
}
|
||||
|
||||
const char *fname = mp_obj_str_get_str(fid);
|
||||
int fd = open(fname, mode_x | mode_rw, 0644);
|
||||
if (fd == -1) {
|
||||
mp_raise_OSError(errno);
|
||||
}
|
||||
int fd;
|
||||
MP_HAL_RETRY_SYSCALL(fd, open(fname, mode_x | mode_rw, 0644), mp_raise_OSError(err));
|
||||
o->fd = fd;
|
||||
return MP_OBJ_FROM_PTR(o);
|
||||
}
|
||||
|
||||
STATIC mp_obj_t vfs_posix_file_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) {
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_file, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_rom_obj = MP_ROM_PTR(&mp_const_none_obj)} },
|
||||
{ MP_QSTR_file, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_rom_obj = MP_ROM_NONE} },
|
||||
{ MP_QSTR_mode, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_QSTR(MP_QSTR_r)} },
|
||||
};
|
||||
|
||||
|
@ -115,52 +116,49 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(vfs_posix_file___exit___obj, 4, 4, vf
|
|||
STATIC mp_uint_t vfs_posix_file_read(mp_obj_t o_in, void *buf, mp_uint_t size, int *errcode) {
|
||||
mp_obj_vfs_posix_file_t *o = MP_OBJ_TO_PTR(o_in);
|
||||
check_fd_is_open(o);
|
||||
mp_int_t r = read(o->fd, buf, size);
|
||||
if (r == -1) {
|
||||
*errcode = errno;
|
||||
ssize_t r;
|
||||
MP_HAL_RETRY_SYSCALL(r, read(o->fd, buf, size), {
|
||||
*errcode = err;
|
||||
return MP_STREAM_ERROR;
|
||||
}
|
||||
return r;
|
||||
});
|
||||
return (mp_uint_t)r;
|
||||
}
|
||||
|
||||
STATIC mp_uint_t vfs_posix_file_write(mp_obj_t o_in, const void *buf, mp_uint_t size, int *errcode) {
|
||||
mp_obj_vfs_posix_file_t *o = MP_OBJ_TO_PTR(o_in);
|
||||
check_fd_is_open(o);
|
||||
#if MICROPY_PY_OS_DUPTERM
|
||||
if (o->fd <= STDERR_FILENO) {
|
||||
mp_hal_stdout_tx_strn(buf, size);
|
||||
return size;
|
||||
}
|
||||
#endif
|
||||
mp_int_t r = write(o->fd, buf, size);
|
||||
while (r == -1 && errno == EINTR) {
|
||||
if (MP_STATE_VM(mp_pending_exception) != MP_OBJ_NULL) {
|
||||
mp_obj_t obj = MP_STATE_VM(mp_pending_exception);
|
||||
MP_STATE_VM(mp_pending_exception) = MP_OBJ_NULL;
|
||||
nlr_raise(obj);
|
||||
}
|
||||
r = write(o->fd, buf, size);
|
||||
}
|
||||
if (r == -1) {
|
||||
*errcode = errno;
|
||||
ssize_t r;
|
||||
MP_HAL_RETRY_SYSCALL(r, write(o->fd, buf, size), {
|
||||
*errcode = err;
|
||||
return MP_STREAM_ERROR;
|
||||
}
|
||||
return r;
|
||||
});
|
||||
return (mp_uint_t)r;
|
||||
}
|
||||
|
||||
STATIC mp_uint_t vfs_posix_file_ioctl(mp_obj_t o_in, mp_uint_t request, uintptr_t arg, int *errcode) {
|
||||
mp_obj_vfs_posix_file_t *o = MP_OBJ_TO_PTR(o_in);
|
||||
check_fd_is_open(o);
|
||||
switch (request) {
|
||||
case MP_STREAM_FLUSH:
|
||||
if (fsync(o->fd) < 0) {
|
||||
*errcode = errno;
|
||||
return MP_STREAM_ERROR;
|
||||
}
|
||||
case MP_STREAM_FLUSH: {
|
||||
int ret;
|
||||
MP_HAL_RETRY_SYSCALL(ret, fsync(o->fd), {
|
||||
if (err == EINVAL
|
||||
&& (o->fd == STDIN_FILENO || o->fd == STDOUT_FILENO || o->fd == STDERR_FILENO)) {
|
||||
// fsync(stdin/stdout/stderr) may fail with EINVAL, but don't propagate that
|
||||
// error out. Because data is not buffered by us, and stdin/out/err.flush()
|
||||
// should just be a no-op.
|
||||
return 0;
|
||||
}
|
||||
*errcode = err;
|
||||
return MP_STREAM_ERROR;
|
||||
});
|
||||
return 0;
|
||||
}
|
||||
case MP_STREAM_SEEK: {
|
||||
struct mp_stream_seek_t *s = (struct mp_stream_seek_t *)arg;
|
||||
MP_THREAD_GIL_EXIT();
|
||||
off_t off = lseek(o->fd, s->offset, s->whence);
|
||||
MP_THREAD_GIL_ENTER();
|
||||
if (off == (off_t)-1) {
|
||||
*errcode = errno;
|
||||
return MP_STREAM_ERROR;
|
||||
|
@ -169,18 +167,22 @@ STATIC mp_uint_t vfs_posix_file_ioctl(mp_obj_t o_in, mp_uint_t request, uintptr_
|
|||
return 0;
|
||||
}
|
||||
case MP_STREAM_CLOSE:
|
||||
MP_THREAD_GIL_EXIT();
|
||||
close(o->fd);
|
||||
MP_THREAD_GIL_ENTER();
|
||||
#ifdef MICROPY_CPYTHON_COMPAT
|
||||
o->fd = -1;
|
||||
#endif
|
||||
return 0;
|
||||
case MP_STREAM_GET_FILENO:
|
||||
return o->fd;
|
||||
default:
|
||||
*errcode = EINVAL;
|
||||
return MP_STREAM_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
STATIC const mp_rom_map_elem_t rawfile_locals_dict_table[] = {
|
||||
STATIC const mp_rom_map_elem_t vfs_posix_rawfile_locals_dict_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR_fileno), MP_ROM_PTR(&vfs_posix_file_fileno_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) },
|
||||
|
@ -195,10 +197,10 @@ STATIC const mp_rom_map_elem_t rawfile_locals_dict_table[] = {
|
|||
{ MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&vfs_posix_file___exit___obj) },
|
||||
};
|
||||
|
||||
STATIC MP_DEFINE_CONST_DICT(rawfile_locals_dict, rawfile_locals_dict_table);
|
||||
STATIC MP_DEFINE_CONST_DICT(vfs_posix_rawfile_locals_dict, vfs_posix_rawfile_locals_dict_table);
|
||||
|
||||
#if MICROPY_PY_IO_FILEIO
|
||||
STATIC const mp_stream_p_t fileio_stream_p = {
|
||||
STATIC const mp_stream_p_t vfs_posix_fileio_stream_p = {
|
||||
MP_PROTO_IMPLEMENT(MP_QSTR_protocol_stream)
|
||||
.read = vfs_posix_file_read,
|
||||
.write = vfs_posix_file_write,
|
||||
|
@ -212,12 +214,12 @@ const mp_obj_type_t mp_type_vfs_posix_fileio = {
|
|||
.make_new = vfs_posix_file_make_new,
|
||||
.getiter = mp_identity_getiter,
|
||||
.iternext = mp_stream_unbuffered_iter,
|
||||
.protocol = &fileio_stream_p,
|
||||
.locals_dict = (mp_obj_dict_t *)&rawfile_locals_dict,
|
||||
.protocol = &vfs_posix_fileio_stream_p,
|
||||
.locals_dict = (mp_obj_dict_t *)&vfs_posix_rawfile_locals_dict,
|
||||
};
|
||||
#endif
|
||||
|
||||
STATIC const mp_stream_p_t textio_stream_p = {
|
||||
STATIC const mp_stream_p_t vfs_posix_textio_stream_p = {
|
||||
MP_PROTO_IMPLEMENT(MP_QSTR_protocol_stream)
|
||||
.read = vfs_posix_file_read,
|
||||
.write = vfs_posix_file_write,
|
||||
|
@ -232,12 +234,12 @@ const mp_obj_type_t mp_type_vfs_posix_textio = {
|
|||
.make_new = vfs_posix_file_make_new,
|
||||
.getiter = mp_identity_getiter,
|
||||
.iternext = mp_stream_unbuffered_iter,
|
||||
.protocol = &textio_stream_p,
|
||||
.locals_dict = (mp_obj_dict_t *)&rawfile_locals_dict,
|
||||
.protocol = &vfs_posix_textio_stream_p,
|
||||
.locals_dict = (mp_obj_dict_t *)&vfs_posix_rawfile_locals_dict,
|
||||
};
|
||||
|
||||
const mp_obj_vfs_posix_file_t mp_sys_stdin_obj = {{&mp_type_textio}, STDIN_FILENO};
|
||||
const mp_obj_vfs_posix_file_t mp_sys_stdout_obj = {{&mp_type_textio}, STDOUT_FILENO};
|
||||
const mp_obj_vfs_posix_file_t mp_sys_stderr_obj = {{&mp_type_textio}, STDERR_FILENO};
|
||||
|
||||
#endif // MICROPY_VFS_POSIX
|
||||
#endif // MICROPY_VFS_POSIX || MICROPY_VFS_POSIX_FILE
|
||||
|
|
|
@ -50,8 +50,11 @@ STATIC void mp_reader_vfs_close(void *data) {
|
|||
|
||||
void mp_reader_new_file(mp_reader_t *reader, const char *filename) {
|
||||
mp_reader_vfs_t *rf = m_new_obj(mp_reader_vfs_t);
|
||||
mp_obj_t arg = mp_obj_new_str(filename, strlen(filename));
|
||||
rf->file = mp_vfs_open(1, &arg, (mp_map_t *)&mp_const_empty_map);
|
||||
mp_obj_t args[2] = {
|
||||
mp_obj_new_str(filename, strlen(filename)),
|
||||
MP_OBJ_NEW_QSTR(MP_QSTR_rb),
|
||||
};
|
||||
rf->file = mp_vfs_open(MP_ARRAY_SIZE(args), &args[0], (mp_map_t *)&mp_const_empty_map);
|
||||
int errcode;
|
||||
rf->len = mp_stream_rw(rf->file, rf->buf, sizeof(rf->buf), &errcode, MP_STREAM_RW_READ | MP_STREAM_RW_ONCE);
|
||||
if (errcode != 0) {
|
||||
|
|
|
@ -1,43 +1,108 @@
|
|||
/**************************************************************************//**
|
||||
* @file cmsis_armcc.h
|
||||
* @brief CMSIS Cortex-M Core Function/Instruction Header File
|
||||
* @version V4.30
|
||||
* @date 20. October 2015
|
||||
* @brief CMSIS compiler ARMCC (Arm Compiler 5) header file
|
||||
* @version V5.0.5
|
||||
* @date 14. December 2018
|
||||
******************************************************************************/
|
||||
/* Copyright (c) 2009 - 2015 ARM LIMITED
|
||||
|
||||
All rights reserved.
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
- Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
- Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
- Neither the name of ARM nor the names of its contributors may be used
|
||||
to endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
/*
|
||||
* Copyright (c) 2009-2018 Arm Limited. All rights reserved.
|
||||
*
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
---------------------------------------------------------------------------*/
|
||||
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the License); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an AS IS BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef __CMSIS_ARMCC_H
|
||||
#define __CMSIS_ARMCC_H
|
||||
|
||||
|
||||
#if defined(__ARMCC_VERSION) && (__ARMCC_VERSION < 400677)
|
||||
#error "Please use ARM Compiler Toolchain V4.0.677 or later!"
|
||||
#error "Please use Arm Compiler Toolchain V4.0.677 or later!"
|
||||
#endif
|
||||
|
||||
/* CMSIS compiler control architecture macros */
|
||||
#if ((defined (__TARGET_ARCH_6_M ) && (__TARGET_ARCH_6_M == 1)) || \
|
||||
(defined (__TARGET_ARCH_6S_M ) && (__TARGET_ARCH_6S_M == 1)) )
|
||||
#define __ARM_ARCH_6M__ 1
|
||||
#endif
|
||||
|
||||
#if (defined (__TARGET_ARCH_7_M ) && (__TARGET_ARCH_7_M == 1))
|
||||
#define __ARM_ARCH_7M__ 1
|
||||
#endif
|
||||
|
||||
#if (defined (__TARGET_ARCH_7E_M) && (__TARGET_ARCH_7E_M == 1))
|
||||
#define __ARM_ARCH_7EM__ 1
|
||||
#endif
|
||||
|
||||
/* __ARM_ARCH_8M_BASE__ not applicable */
|
||||
/* __ARM_ARCH_8M_MAIN__ not applicable */
|
||||
|
||||
/* CMSIS compiler control DSP macros */
|
||||
#if ((defined (__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1)) )
|
||||
#define __ARM_FEATURE_DSP 1
|
||||
#endif
|
||||
|
||||
/* CMSIS compiler specific defines */
|
||||
#ifndef __ASM
|
||||
#define __ASM __asm
|
||||
#endif
|
||||
#ifndef __INLINE
|
||||
#define __INLINE __inline
|
||||
#endif
|
||||
#ifndef __STATIC_INLINE
|
||||
#define __STATIC_INLINE static __inline
|
||||
#endif
|
||||
#ifndef __STATIC_FORCEINLINE
|
||||
#define __STATIC_FORCEINLINE static __forceinline
|
||||
#endif
|
||||
#ifndef __NO_RETURN
|
||||
#define __NO_RETURN __declspec(noreturn)
|
||||
#endif
|
||||
#ifndef __USED
|
||||
#define __USED __attribute__((used))
|
||||
#endif
|
||||
#ifndef __WEAK
|
||||
#define __WEAK __attribute__((weak))
|
||||
#endif
|
||||
#ifndef __PACKED
|
||||
#define __PACKED __attribute__((packed))
|
||||
#endif
|
||||
#ifndef __PACKED_STRUCT
|
||||
#define __PACKED_STRUCT __packed struct
|
||||
#endif
|
||||
#ifndef __PACKED_UNION
|
||||
#define __PACKED_UNION __packed union
|
||||
#endif
|
||||
#ifndef __UNALIGNED_UINT32 /* deprecated */
|
||||
#define __UNALIGNED_UINT32(x) (*((__packed uint32_t *)(x)))
|
||||
#endif
|
||||
#ifndef __UNALIGNED_UINT16_WRITE
|
||||
#define __UNALIGNED_UINT16_WRITE(addr, val) ((*((__packed uint16_t *)(addr))) = (val))
|
||||
#endif
|
||||
#ifndef __UNALIGNED_UINT16_READ
|
||||
#define __UNALIGNED_UINT16_READ(addr) (*((const __packed uint16_t *)(addr)))
|
||||
#endif
|
||||
#ifndef __UNALIGNED_UINT32_WRITE
|
||||
#define __UNALIGNED_UINT32_WRITE(addr, val) ((*((__packed uint32_t *)(addr))) = (val))
|
||||
#endif
|
||||
#ifndef __UNALIGNED_UINT32_READ
|
||||
#define __UNALIGNED_UINT32_READ(addr) (*((const __packed uint32_t *)(addr)))
|
||||
#endif
|
||||
#ifndef __ALIGNED
|
||||
#define __ALIGNED(x) __attribute__((aligned(x)))
|
||||
#endif
|
||||
#ifndef __RESTRICT
|
||||
#define __RESTRICT __restrict
|
||||
#endif
|
||||
|
||||
/* ########################### Core Function Access ########################### */
|
||||
|
@ -46,7 +111,19 @@
|
|||
@{
|
||||
*/
|
||||
|
||||
/**
|
||||
\brief Enable IRQ Interrupts
|
||||
\details Enables IRQ interrupts by clearing the I-bit in the CPSR.
|
||||
Can only be executed in Privileged modes.
|
||||
*/
|
||||
/* intrinsic void __enable_irq(); */
|
||||
|
||||
|
||||
/**
|
||||
\brief Disable IRQ Interrupts
|
||||
\details Disables IRQ interrupts by setting the I-bit in the CPSR.
|
||||
Can only be executed in Privileged modes.
|
||||
*/
|
||||
/* intrinsic void __disable_irq(); */
|
||||
|
||||
/**
|
||||
|
@ -181,7 +258,8 @@ __STATIC_INLINE void __set_PRIMASK(uint32_t priMask)
|
|||
}
|
||||
|
||||
|
||||
#if (__CORTEX_M >= 0x03U) || (__CORTEX_SC >= 300U)
|
||||
#if ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \
|
||||
(defined (__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1)) )
|
||||
|
||||
/**
|
||||
\brief Enable FIQ
|
||||
|
@ -256,14 +334,13 @@ __STATIC_INLINE uint32_t __get_FAULTMASK(void)
|
|||
__STATIC_INLINE void __set_FAULTMASK(uint32_t faultMask)
|
||||
{
|
||||
register uint32_t __regFaultMask __ASM("faultmask");
|
||||
__regFaultMask = (faultMask & (uint32_t)1);
|
||||
__regFaultMask = (faultMask & (uint32_t)1U);
|
||||
}
|
||||
|
||||
#endif /* (__CORTEX_M >= 0x03U) || (__CORTEX_SC >= 300U) */
|
||||
#endif /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \
|
||||
(defined (__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1)) ) */
|
||||
|
||||
|
||||
#if (__CORTEX_M == 0x04U) || (__CORTEX_M == 0x07U)
|
||||
|
||||
/**
|
||||
\brief Get FPSCR
|
||||
\details Returns the current value of the Floating Point Status/Control register.
|
||||
|
@ -271,7 +348,8 @@ __STATIC_INLINE void __set_FAULTMASK(uint32_t faultMask)
|
|||
*/
|
||||
__STATIC_INLINE uint32_t __get_FPSCR(void)
|
||||
{
|
||||
#if (__FPU_PRESENT == 1U) && (__FPU_USED == 1U)
|
||||
#if ((defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)) && \
|
||||
(defined (__FPU_USED ) && (__FPU_USED == 1U)) )
|
||||
register uint32_t __regfpscr __ASM("fpscr");
|
||||
return(__regfpscr);
|
||||
#else
|
||||
|
@ -287,15 +365,15 @@ __STATIC_INLINE uint32_t __get_FPSCR(void)
|
|||
*/
|
||||
__STATIC_INLINE void __set_FPSCR(uint32_t fpscr)
|
||||
{
|
||||
#if (__FPU_PRESENT == 1U) && (__FPU_USED == 1U)
|
||||
#if ((defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)) && \
|
||||
(defined (__FPU_USED ) && (__FPU_USED == 1U)) )
|
||||
register uint32_t __regfpscr __ASM("fpscr");
|
||||
__regfpscr = (fpscr);
|
||||
#else
|
||||
(void)fpscr;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* (__CORTEX_M == 0x04U) || (__CORTEX_M == 0x07U) */
|
||||
|
||||
|
||||
|
||||
/*@} end of CMSIS_Core_RegAccFunctions */
|
||||
|
||||
|
@ -369,9 +447,10 @@ __STATIC_INLINE void __set_FPSCR(uint32_t fpscr)
|
|||
__schedule_barrier();\
|
||||
} while (0U)
|
||||
|
||||
|
||||
/**
|
||||
\brief Reverse byte order (32 bit)
|
||||
\details Reverses the byte order in integer value.
|
||||
\details Reverses the byte order in unsigned integer value. For example, 0x12345678 becomes 0x78563412.
|
||||
\param [in] value Value to reverse
|
||||
\return Reversed value
|
||||
*/
|
||||
|
@ -380,7 +459,7 @@ __STATIC_INLINE void __set_FPSCR(uint32_t fpscr)
|
|||
|
||||
/**
|
||||
\brief Reverse byte order (16 bit)
|
||||
\details Reverses the byte order in two unsigned short values.
|
||||
\details Reverses the byte order within each halfword of a word. For example, 0x12345678 becomes 0x34127856.
|
||||
\param [in] value Value to reverse
|
||||
\return Reversed value
|
||||
*/
|
||||
|
@ -392,14 +471,15 @@ __attribute__((section(".rev16_text"))) __STATIC_INLINE __ASM uint32_t __REV16(u
|
|||
}
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
\brief Reverse byte order in signed short value
|
||||
\details Reverses the byte order in a signed short value with sign extension to integer.
|
||||
\brief Reverse byte order (16 bit)
|
||||
\details Reverses the byte order in a 16-bit value and returns the signed 16-bit result. For example, 0x0080 becomes 0x8000.
|
||||
\param [in] value Value to reverse
|
||||
\return Reversed value
|
||||
*/
|
||||
#ifndef __NO_EMBEDDED_ASM
|
||||
__attribute__((section(".revsh_text"))) __STATIC_INLINE __ASM int32_t __REVSH(int32_t value)
|
||||
__attribute__((section(".revsh_text"))) __STATIC_INLINE __ASM int16_t __REVSH(int16_t value)
|
||||
{
|
||||
revsh r0, r0
|
||||
bx lr
|
||||
|
@ -410,8 +490,8 @@ __attribute__((section(".revsh_text"))) __STATIC_INLINE __ASM int32_t __REVSH(in
|
|||
/**
|
||||
\brief Rotate Right in unsigned value (32 bit)
|
||||
\details Rotate Right (immediate) provides the value of the contents of a register rotated by a variable number of bits.
|
||||
\param [in] value Value to rotate
|
||||
\param [in] value Number of Bits to rotate
|
||||
\param [in] op1 Value to rotate
|
||||
\param [in] op2 Number of Bits to rotate
|
||||
\return Rotated value
|
||||
*/
|
||||
#define __ROR __ror
|
||||
|
@ -433,23 +513,24 @@ __attribute__((section(".revsh_text"))) __STATIC_INLINE __ASM int32_t __REVSH(in
|
|||
\param [in] value Value to reverse
|
||||
\return Reversed value
|
||||
*/
|
||||
#if (__CORTEX_M >= 0x03U) || (__CORTEX_SC >= 300U)
|
||||
#if ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \
|
||||
(defined (__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1)) )
|
||||
#define __RBIT __rbit
|
||||
#else
|
||||
__attribute__((always_inline)) __STATIC_INLINE uint32_t __RBIT(uint32_t value)
|
||||
{
|
||||
uint32_t result;
|
||||
int32_t s = 4 /*sizeof(v)*/ * 8 - 1; /* extra shift needed at end */
|
||||
uint32_t s = (4U /*sizeof(v)*/ * 8U) - 1U; /* extra shift needed at end */
|
||||
|
||||
result = value; /* r will be reversed bits of v; first get LSB of v */
|
||||
for (value >>= 1U; value; value >>= 1U)
|
||||
for (value >>= 1U; value != 0U; value >>= 1U)
|
||||
{
|
||||
result <<= 1U;
|
||||
result |= value & 1U;
|
||||
s--;
|
||||
}
|
||||
result <<= s; /* shift when v's highest bits are zero */
|
||||
return(result);
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -463,7 +544,8 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __RBIT(uint32_t value)
|
|||
#define __CLZ __clz
|
||||
|
||||
|
||||
#if (__CORTEX_M >= 0x03U) || (__CORTEX_SC >= 300U)
|
||||
#if ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \
|
||||
(defined (__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1)) )
|
||||
|
||||
/**
|
||||
\brief LDR Exclusive (8 bit)
|
||||
|
@ -645,7 +727,60 @@ __attribute__((section(".rrx_text"))) __STATIC_INLINE __ASM uint32_t __RRX(uint3
|
|||
*/
|
||||
#define __STRT(value, ptr) __strt(value, ptr)
|
||||
|
||||
#endif /* (__CORTEX_M >= 0x03U) || (__CORTEX_SC >= 300U) */
|
||||
#else /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \
|
||||
(defined (__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1)) ) */
|
||||
|
||||
/**
|
||||
\brief Signed Saturate
|
||||
\details Saturates a signed value.
|
||||
\param [in] value Value to be saturated
|
||||
\param [in] sat Bit position to saturate to (1..32)
|
||||
\return Saturated value
|
||||
*/
|
||||
__attribute__((always_inline)) __STATIC_INLINE int32_t __SSAT(int32_t val, uint32_t sat)
|
||||
{
|
||||
if ((sat >= 1U) && (sat <= 32U))
|
||||
{
|
||||
const int32_t max = (int32_t)((1U << (sat - 1U)) - 1U);
|
||||
const int32_t min = -1 - max ;
|
||||
if (val > max)
|
||||
{
|
||||
return max;
|
||||
}
|
||||
else if (val < min)
|
||||
{
|
||||
return min;
|
||||
}
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Unsigned Saturate
|
||||
\details Saturates an unsigned value.
|
||||
\param [in] value Value to be saturated
|
||||
\param [in] sat Bit position to saturate to (0..31)
|
||||
\return Saturated value
|
||||
*/
|
||||
__attribute__((always_inline)) __STATIC_INLINE uint32_t __USAT(int32_t val, uint32_t sat)
|
||||
{
|
||||
if (sat <= 31U)
|
||||
{
|
||||
const uint32_t max = ((1U << sat) - 1U);
|
||||
if (val > (int32_t)max)
|
||||
{
|
||||
return max;
|
||||
}
|
||||
else if (val < 0)
|
||||
{
|
||||
return 0U;
|
||||
}
|
||||
}
|
||||
return (uint32_t)val;
|
||||
}
|
||||
|
||||
#endif /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \
|
||||
(defined (__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1)) ) */
|
||||
|
||||
/*@}*/ /* end of group CMSIS_Core_InstructionInterface */
|
||||
|
||||
|
@ -656,7 +791,7 @@ __attribute__((section(".rrx_text"))) __STATIC_INLINE __ASM uint32_t __RRX(uint3
|
|||
@{
|
||||
*/
|
||||
|
||||
#if (__CORTEX_M >= 0x04U) /* only for Cortex-M4 and above */
|
||||
#if ((defined (__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1)) )
|
||||
|
||||
#define __SADD8 __sadd8
|
||||
#define __QADD8 __qadd8
|
||||
|
@ -727,7 +862,7 @@ __attribute__((section(".rrx_text"))) __STATIC_INLINE __ASM uint32_t __RRX(uint3
|
|||
#define __SMMLA(ARG1,ARG2,ARG3) ( (int32_t)((((int64_t)(ARG1) * (ARG2)) + \
|
||||
((int64_t)(ARG3) << 32U) ) >> 32U))
|
||||
|
||||
#endif /* (__CORTEX_M >= 0x04) */
|
||||
#endif /* ((defined (__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1)) ) */
|
||||
/*@} end of group CMSIS_SIMD_intrinsics */
|
||||
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue