Merge remote-tracking branch 'upstream/main' into stm-alarm
This commit is contained in:
commit
0d3c5222d8
1
.gitattributes
vendored
1
.gitattributes
vendored
@ -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
|
||||
|
36
.github/workflows/build.yml
vendored
36
.github/workflows/build.yml
vendored
@ -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)
|
||||
|
6
.gitignore
vendored
6
.gitignore
vendored
@ -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.
|
||||
|
@ -245,8 +245,8 @@ mp_int_t common_hal_bleio_packet_buffer_get_outgoing_packet_length(bleio_packet_
|
||||
bleio_connection_internal_t *connection = bleio_conn_handle_to_connection(self->conn_handle);
|
||||
if (connection) {
|
||||
return MIN(MIN(common_hal_bleio_connection_get_max_packet_length(connection),
|
||||
self->max_packet_size),
|
||||
self->characteristic->max_length);
|
||||
self->max_packet_size),
|
||||
self->characteristic->max_length);
|
||||
}
|
||||
}
|
||||
// There's no current connection, so we don't know the MTU, and
|
||||
|
@ -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.
|
||||
|
303
docs/library/uasyncio.rst
Normal file
303
docs/library/uasyncio.rst
Normal file
@ -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
|
||||
|
175
docs/reference/glossary.rst
Normal file
175
docs/reference/glossary.rst
Normal file
@ -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)
|
||||
|
||||
@ -224,7 +225,7 @@ def support_matrix_by_board(use_branded_name=True):
|
||||
# flatmap with comprehensions
|
||||
boards = dict(sorted([board for matrix in mapped_exec for board in matrix]))
|
||||
|
||||
#print(json.dumps(boards, indent=2))
|
||||
# print(json.dumps(boards, indent=2))
|
||||
return boards
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
6
docs/templates/replace.inc
vendored
6
docs/templates/replace.inc
vendored
@ -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:*
|
||||
|
1
examples/natmod/.gitignore
vendored
Normal file
1
examples/natmod/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
*.mpy
|
37
examples/natmod/btree/Makefile
Normal file
37
examples/natmod/btree/Makefile
Normal file
@ -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
|
147
examples/natmod/btree/btree_c.c
Normal file
147
examples/natmod/btree/btree_c.c
Normal file
@ -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
|
||||
}
|
3
examples/natmod/btree/btree_py.py
Normal file
3
examples/natmod/btree/btree_py.py
Normal file
@ -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)
|
14
examples/natmod/features0/Makefile
Normal file
14
examples/natmod/features0/Makefile
Normal file
@ -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
|
40
examples/natmod/features0/features0.c
Normal file
40
examples/natmod/features0/features0.c
Normal file
@ -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
|
||||
}
|
14
examples/natmod/features1/Makefile
Normal file
14
examples/natmod/features1/Makefile
Normal file
@ -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
|
106
examples/natmod/features1/features1.c
Normal file
106
examples/natmod/features1/features1.c
Normal file
@ -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
|
||||
}
|
14
examples/natmod/features2/Makefile
Normal file
14
examples/natmod/features2/Makefile
Normal file
@ -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
|
83
examples/natmod/features2/main.c
Normal file
83
examples/natmod/features2/main.c
Normal file
@ -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
|
||||
}
|
9
examples/natmod/features2/prod.c
Normal file
9
examples/natmod/features2/prod.c
Normal file
@ -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;
|
||||
}
|
1
examples/natmod/features2/prod.h
Normal file
1
examples/natmod/features2/prod.h
Normal file
@ -0,0 +1 @@
|
||||
float prod_array(int n, float *ar);
|
29
examples/natmod/features2/test.py
Normal file
29
examples/natmod/features2/test.py
Normal file
@ -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()
|
13
examples/natmod/framebuf/Makefile
Normal file
13
examples/natmod/framebuf/Makefile
Normal file
@ -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
|
49
examples/natmod/framebuf/framebuf.c
Normal file
49
examples/natmod/framebuf/framebuf.c
Normal file
@ -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
|
||||
}
|
13
examples/natmod/uheapq/Makefile
Normal file
13
examples/natmod/uheapq/Makefile
Normal file
@ -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
|
16
examples/natmod/uheapq/uheapq.c
Normal file
16
examples/natmod/uheapq/uheapq.c
Normal file
@ -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
|
||||
}
|
13
examples/natmod/urandom/Makefile
Normal file
13
examples/natmod/urandom/Makefile
Normal file
@ -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
|
33
examples/natmod/urandom/urandom.c
Normal file
33
examples/natmod/urandom/urandom.c
Normal file
@ -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
|
||||
}
|
13
examples/natmod/ure/Makefile
Normal file
13
examples/natmod/ure/Makefile
Normal file
@ -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
|
78
examples/natmod/ure/ure.c
Normal file
78
examples/natmod/ure/ure.c
Normal file
@ -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
|
||||
}
|
13
examples/natmod/uzlib/Makefile
Normal file
13
examples/natmod/uzlib/Makefile
Normal file
@ -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
|
35
examples/natmod/uzlib/uzlib.c
Normal file
35
examples/natmod/uzlib/uzlib.c
Normal file
@ -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
|
||||
}
|
57
extmod/btstack/btstack.mk
Normal file
57
extmod/btstack/btstack.mk
Normal file
@ -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
|
47
extmod/btstack/btstack_config.h
Normal file
47
extmod/btstack/btstack_config.h
Normal file
@ -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
|
1047
extmod/btstack/modbluetooth_btstack.c
Normal file
1047
extmod/btstack/modbluetooth_btstack.c
Normal file
File diff suppressed because it is too large
Load Diff
70
extmod/btstack/modbluetooth_btstack.h
Normal file
70
extmod/btstack/modbluetooth_btstack.h
Normal file
@ -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.
|
||||
*********************************************************************/
|
||||
|
232
extmod/extmod.mk
Normal file
232
extmod/extmod.mk
Normal file
@ -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
|
||||
|
294
extmod/moduasyncio.c
Normal file
294
extmod/moduasyncio.c
Normal file
@ -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]);
|
||||
re1_5_dumpcode(code);
|
||||
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>
|
||||
|
29
extmod/uasyncio/__init__.py
Normal file
29
extmod/uasyncio/__init__.py
Normal file
@ -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
|
277
extmod/uasyncio/core.py
Normal file
277
extmod/uasyncio/core.py
Normal file
@ -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()
|
31
extmod/uasyncio/event.py
Normal file
31
extmod/uasyncio/event.py
Normal file
@ -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
|
54
extmod/uasyncio/funcs.py
Normal file
54
extmod/uasyncio/funcs.py
Normal file
@ -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
|
53
extmod/uasyncio/lock.py
Normal file
53
extmod/uasyncio/lock.py
Normal file
@ -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()
|
13
extmod/uasyncio/manifest.py
Normal file
13
extmod/uasyncio/manifest.py
Normal file
@ -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,
|
||||
)
|
158
extmod/uasyncio/stream.py
Normal file
158
extmod/uasyncio/stream.py
Normal file
@ -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?
|
168
extmod/uasyncio/task.py
Normal file
168
extmod/uasyncio/task.py
Normal file
@ -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);
|
||||
|
143
extmod/vfs_blockdev.c
Normal file
143
extmod/vfs_blockdev.c
Normal file
@ -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]);
|
||||
}
|
||||
@ -349,9 +341,9 @@ STATIC mp_obj_t fat_vfs_stat(mp_obj_t vfs_in, mp_obj_t path_in) {
|
||||
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(fno.fsize); // st_size
|
||||
t->items[7] = seconds; // st_atime
|
||||
t->items[8] = seconds; // st_mtime
|
||||
t->items[9] = seconds; // st_ctime
|
||||
t->items[7] = seconds; // st_atime
|
||||
t->items[8] = seconds; // st_mtime
|
||||
t->items[9] = seconds; // st_ctime
|
||||
|
||||
return MP_OBJ_FROM_PTR(t);
|
||||
}
|
||||
@ -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,
|
||||
};
|
||||
uint8_t bp_op = op_map[cmd & 7];
|
||||
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;
|
||||
}
|
||||
static const uint8_t op_map[8] = {
|
||||
[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) {
|
||||
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
|
||||
|
142
extmod/vfs_lfs.c
Normal file
142
extmod/vfs_lfs.c
Normal file
@ -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)
|
39
extmod/vfs_lfs.h
Normal file
39
extmod/vfs_lfs.h
Normal file
@ -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
|
491
extmod/vfs_lfsx.c
Normal file
491
extmod/vfs_lfsx.c
Normal file
@ -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),
|
||||
};
|
254
extmod/vfs_lfsx_file.c
Normal file
254
extmod/vfs_lfsx_file.c
Normal file
@ -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;
|
||||
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.
|
||||
*
|
||||
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.
|
||||
---------------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2009-2018 Arm Limited. All rights reserved.
|
||||
*
|
||||
* 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 */
|
||||
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user