Skip to content

Commit 299da14

Browse files
authored
Merge branch 'micropython:master' into master
2 parents c8c668d + 68e3e07 commit 299da14

File tree

22 files changed

+296
-95
lines changed

22 files changed

+296
-95
lines changed

CONTRIBUTING.md

+9-4
Original file line numberDiff line numberDiff line change
@@ -102,15 +102,20 @@ Pages](https://docs.github.com/en/pages):
102102
"truthy" value).
103103
5. The settings for GitHub Actions and GitHub Pages features should not need to
104104
be changed from the repository defaults, unless you've explicitly disabled
105-
them.
105+
Actions or Pages in your fork.
106106

107107
The next time you push commits to a branch in your fork, GitHub Actions will run
108108
an additional step in the "Build All Packages" workflow named "Publish Packages
109-
for branch".
109+
for branch". This step runs in *your fork*, but if you open a pull request then
110+
this workflow is not shown in the Pull Request's "Checks". These run in the
111+
upstream repository. Navigate to your fork's Actions tab in order to see
112+
the additional "Publish Packages for branch" step.
110113

111114
Anyone can then install these packages as described under [Installing packages
112-
from forks](README.md#installing-packages-from-forks). The exact commands are also
113-
quoted in the GitHub Actions log for the "Publish Packages for branch" step.
115+
from forks](README.md#installing-packages-from-forks).
116+
117+
The exact command is also quoted in the GitHub Actions log in your fork's
118+
Actions for the "Publish Packages for branch" step of "Build All Packages".
114119

115120
#### Opting Back Out
116121

micropython/bluetooth/aioble-central/manifest.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
metadata(version="0.2.2")
1+
metadata(version="0.3.0")
22

33
require("aioble-core")
44

micropython/bluetooth/aioble-core/manifest.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
metadata(version="0.3.0")
1+
metadata(version="0.4.0")
22

33
package(
44
"aioble",

micropython/bluetooth/aioble/aioble/central.py

+10-2
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,9 @@ async def _cancel_pending():
104104

105105
# Start connecting to a peripheral.
106106
# Call device.connect() rather than using method directly.
107-
async def _connect(connection, timeout_ms):
107+
async def _connect(
108+
connection, timeout_ms, scan_duration_ms, min_conn_interval_us, max_conn_interval_us
109+
):
108110
device = connection.device
109111
if device in _connecting:
110112
return
@@ -122,7 +124,13 @@ async def _connect(connection, timeout_ms):
122124

123125
try:
124126
with DeviceTimeout(None, timeout_ms):
125-
ble.gap_connect(device.addr_type, device.addr)
127+
ble.gap_connect(
128+
device.addr_type,
129+
device.addr,
130+
scan_duration_ms,
131+
min_conn_interval_us,
132+
max_conn_interval_us,
133+
)
126134

127135
# Wait for the connected IRQ.
128136
await connection._event.wait()

micropython/bluetooth/aioble/aioble/device.py

+14-2
Original file line numberDiff line numberDiff line change
@@ -132,14 +132,26 @@ def __str__(self):
132132
def addr_hex(self):
133133
return binascii.hexlify(self.addr, ":").decode()
134134

135-
async def connect(self, timeout_ms=10000):
135+
async def connect(
136+
self,
137+
timeout_ms=10000,
138+
scan_duration_ms=None,
139+
min_conn_interval_us=None,
140+
max_conn_interval_us=None,
141+
):
136142
if self._connection:
137143
return self._connection
138144

139145
# Forward to implementation in central.py.
140146
from .central import _connect
141147

142-
await _connect(DeviceConnection(self), timeout_ms)
148+
await _connect(
149+
DeviceConnection(self),
150+
timeout_ms,
151+
scan_duration_ms,
152+
min_conn_interval_us,
153+
max_conn_interval_us,
154+
)
143155

144156
# Start the device task that will clean up after disconnection.
145157
self._connection._run_task()

micropython/bluetooth/aioble/manifest.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# code. This allows (for development purposes) all the files to live in the
44
# one directory.
55

6-
metadata(version="0.5.2")
6+
metadata(version="0.6.0")
77

88
# Default installation gives you everything. Install the individual
99
# components (or a combination of them) if you want a more minimal install.

micropython/lora/lora-sx126x/lora/sx126x.py

+6-6
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ def __init__(
152152
dio3_tcxo_start_time_us if dio3_tcxo_millivolts else 0
153153
)
154154

155-
self._buf = bytearray(9) # shared buffer for commands
155+
self._buf_view = memoryview(bytearray(9)) # shared buffer for commands
156156

157157
# These settings are kept in the object (as can't read them back from the modem)
158158
self._output_power = 14
@@ -704,11 +704,11 @@ def _cmd(self, fmt, *write_args, n_read=0, write_buf=None, read_buf=None):
704704
# have happened well before _cmd() is called again.
705705
self._wait_not_busy(self._busy_timeout)
706706

707-
# Pack write_args into _buf and wrap a memoryview of the correct length around it
707+
# Pack write_args into slice of _buf_view memoryview of correct length
708708
wrlen = struct.calcsize(fmt)
709-
assert n_read + wrlen <= len(self._buf) # if this fails, make _buf bigger!
710-
struct.pack_into(fmt, self._buf, 0, *write_args)
711-
buf = memoryview(self._buf)[: (wrlen + n_read)]
709+
assert n_read + wrlen <= len(self._buf_view) # if this fails, make _buf bigger!
710+
struct.pack_into(fmt, self._buf_view, 0, *write_args)
711+
buf = self._buf_view[: (wrlen + n_read)]
712712

713713
if _DEBUG:
714714
print(">>> {}".format(buf[:wrlen].hex()))
@@ -723,7 +723,7 @@ def _cmd(self, fmt, *write_args, n_read=0, write_buf=None, read_buf=None):
723723
self._cs(1)
724724

725725
if n_read > 0:
726-
res = memoryview(buf)[wrlen : (wrlen + n_read)] # noqa: E203
726+
res = self._buf_view[wrlen : (wrlen + n_read)] # noqa: E203
727727
if _DEBUG:
728728
print("<<< {}".format(res.hex()))
729729
return res
+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
metadata(version="0.1.2")
1+
metadata(version="0.1.3")
22
require("lora")
33
package("lora")

micropython/lora/lora-sx127x/lora/sx127x.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -416,13 +416,13 @@ def configure(self, lora_cfg):
416416
modem_config1 |= (self._coding_rate - 4) << _MODEM_CONFIG1_CODING_RATE_SHIFT
417417
update_mask |= _MODEM_CONFIG1_CODING_RATE_MASK << _MODEM_CONFIG1_CODING_RATE_SHIFT
418418

419-
self._reg_update(_REG_MODEM_CONFIG1, update_mask, modem_config1)
420-
421419
if "implicit_header" in lora_cfg:
422420
self._implicit_header = lora_cfg["implicit_header"]
423421
modem_config1 |= _flag(_MODEM_CONFIG1_IMPLICIT_HEADER_MODE_ON, self._implicit_header)
424422
update_mask |= _MODEM_CONFIG1_IMPLICIT_HEADER_MODE_ON
425423

424+
self._reg_update(_REG_MODEM_CONFIG1, update_mask, modem_config1)
425+
426426
# Update MODEM_CONFIG2, for any fields that changed
427427
modem_config2 = 0
428428
update_mask = 0
+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
metadata(version="0.1.1")
1+
metadata(version="0.1.2")
22
require("lora")
33
package("lora")

micropython/umqtt.robust/example_sub_robust.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,7 @@ def sub_cb(topic, msg):
1919
#
2020
# There can be a problem when a session for a given client exists,
2121
# but doesn't have subscriptions a particular application expects.
22-
# In this case, a session needs to be cleaned first. See
23-
# example_reset_session.py for an obvious way how to do that.
22+
# In this case, a session needs to be cleaned first.
2423
#
2524
# In an actual application, it's up to its developer how to
2625
# manage these issues. One extreme is to have external "provisioning"

micropython/usb/README.md

+12
Original file line numberDiff line numberDiff line change
@@ -134,3 +134,15 @@ USB MIDI devices in MicroPython.
134134

135135
The example [midi_example.py](examples/device/midi_example.py) demonstrates how
136136
to create a simple MIDI device to send MIDI data to and from the USB host.
137+
138+
### Limitations
139+
140+
#### Buffer thread safety
141+
142+
The internal Buffer class that's used by most of the USB device classes expects data
143+
to be written to it (i.e. sent to the host) by only one thread. Bytes may be
144+
lost from the USB transfers if more than one thread (or a thread and a callback)
145+
try to write to the buffer simultaneously.
146+
147+
If writing USB data from multiple sources, your code may need to add
148+
synchronisation (i.e. locks).
+1-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
metadata(version="0.1.0")
1+
metadata(version="0.2.0")
22
package("usb")

micropython/usb/usb-device/usb/device/core.py

+44-11
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,14 @@
88
import machine
99
import struct
1010

11+
try:
12+
from _thread import get_ident
13+
except ImportError:
14+
15+
def get_ident():
16+
return 0 # Placeholder, for no threading support
17+
18+
1119
_EP_IN_FLAG = const(1 << 7)
1220

1321
# USB descriptor types
@@ -76,6 +84,8 @@ def __init__(self):
7684
self._itfs = {} # Mapping from interface number to interface object, set by init()
7785
self._eps = {} # Mapping from endpoint address to interface object, set by _open_cb()
7886
self._ep_cbs = {} # Mapping from endpoint address to Optional[xfer callback]
87+
self._cb_thread = None # Thread currently running endpoint callback
88+
self._cb_ep = None # Endpoint number currently running callback
7989
self._usbd = machine.USBDevice() # low-level API
8090

8191
def init(self, *itfs, **kwargs):
@@ -100,6 +110,7 @@ def config( # noqa: PLR0913
100110
device_protocol=0,
101111
config_str=None,
102112
max_power_ma=None,
113+
remote_wakeup=False,
103114
):
104115
# Configure the USB device with a set of interfaces, and optionally reconfiguring the
105116
# device and configuration descriptor fields
@@ -189,7 +200,7 @@ def maybe_set(value, idx):
189200
bmAttributes = (
190201
(1 << 7) # Reserved
191202
| (0 if max_power_ma else (1 << 6)) # Self-Powered
192-
# Remote Wakeup not currently supported
203+
| ((1 << 5) if remote_wakeup else 0)
193204
)
194205

195206
# Configuration string is optional but supported
@@ -242,8 +253,8 @@ def active(self, *optional_value):
242253
return self._usbd.active(*optional_value)
243254

244255
def _open_itf_cb(self, desc):
245-
# Singleton callback from TinyUSB custom class driver, when USB host does
246-
# Set Configuration. Called once per interface or IAD.
256+
# Callback from TinyUSB lower layer, when USB host does Set
257+
# Configuration. Called once per interface or IAD.
247258

248259
# Note that even if the configuration descriptor contains an IAD, 'desc'
249260
# starts from the first interface descriptor in the IAD and not the IAD
@@ -281,7 +292,7 @@ def _open_itf_cb(self, desc):
281292
itf.on_open()
282293

283294
def _reset_cb(self):
284-
# Callback when the USB device is reset by the host
295+
# TinyUSB lower layer callback when the USB device is reset by the host
285296

286297
# Allow interfaces to respond to the reset
287298
for itf in self._itfs.values():
@@ -292,13 +303,13 @@ def _reset_cb(self):
292303
self._ep_cbs = {}
293304

294305
def _submit_xfer(self, ep_addr, data, done_cb=None):
295-
# Singleton function to submit a USB transfer (of any type except control).
306+
# Submit a USB transfer (of any type except control) to TinyUSB lower layer.
296307
#
297308
# Generally, drivers should call Interface.submit_xfer() instead. See
298309
# that function for documentation about the possible parameter values.
299310
if ep_addr not in self._eps:
300311
raise ValueError("ep_addr")
301-
if self._ep_cbs[ep_addr]:
312+
if self._xfer_pending(ep_addr):
302313
raise RuntimeError("xfer_pending")
303314

304315
# USBDevice callback may be called immediately, before Python execution
@@ -308,15 +319,32 @@ def _submit_xfer(self, ep_addr, data, done_cb=None):
308319
self._ep_cbs[ep_addr] = done_cb or True
309320
return self._usbd.submit_xfer(ep_addr, data)
310321

322+
def _xfer_pending(self, ep_addr):
323+
# Returns True if a transfer is pending on this endpoint.
324+
#
325+
# Generally, drivers should call Interface.xfer_pending() instead. See that
326+
# function for more documentation.
327+
return self._ep_cbs[ep_addr] or (self._cb_ep == ep_addr and self._cb_thread != get_ident())
328+
311329
def _xfer_cb(self, ep_addr, result, xferred_bytes):
312-
# Singleton callback from TinyUSB custom class driver when a transfer completes.
330+
# Callback from TinyUSB lower layer when a transfer completes.
313331
cb = self._ep_cbs.get(ep_addr, None)
332+
self._cb_thread = get_ident()
333+
self._cb_ep = ep_addr # Track while callback is running
314334
self._ep_cbs[ep_addr] = None
315-
if callable(cb):
316-
cb(ep_addr, result, xferred_bytes)
335+
336+
# In most cases, 'cb' is a callback function for the transfer. Can also be:
337+
# - True (for a transfer with no callback)
338+
# - None (TinyUSB callback arrived for invalid endpoint, or no transfer.
339+
# Generally unlikely, but may happen in transient states.)
340+
try:
341+
if callable(cb):
342+
cb(ep_addr, result, xferred_bytes)
343+
finally:
344+
self._cb_ep = None
317345

318346
def _control_xfer_cb(self, stage, request):
319-
# Singleton callback from TinyUSB custom class driver when a control
347+
# Callback from TinyUSB lower layer when a control
320348
# transfer is in progress.
321349
#
322350
# stage determines appropriate responses (possible values
@@ -528,7 +556,12 @@ def xfer_pending(self, ep_addr):
528556
# Return True if a transfer is already pending on ep_addr.
529557
#
530558
# Only one transfer can be submitted at a time.
531-
return _dev and bool(_dev._ep_cbs[ep_addr])
559+
#
560+
# The transfer is marked pending while a completion callback is running
561+
# for that endpoint, unless this function is called from the callback
562+
# itself. This makes it simple to submit a new transfer from the
563+
# completion callback.
564+
return _dev and _dev._xfer_pending(ep_addr)
532565

533566
def submit_xfer(self, ep_addr, data, done_cb=None):
534567
# Submit a USB transfer (of any type except control)

python-stdlib/base64/manifest.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
metadata(version="3.3.5")
1+
metadata(version="3.3.6")
22

33
require("binascii")
4-
require("struct")
54

65
module("base64.py")

tools/build.py

+14-6
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@
6464

6565
# index.json is:
6666
# {
67-
# "v": 1, <-- file format version
67+
# "v": 2, <-- file format version
6868
# "updated": <utc-seconds-since-1970>,
6969
# "packages": {
7070
# {
@@ -78,7 +78,9 @@
7878
# "7": ["0.2", "0.3", "0.4"],
7979
# ... <-- Other bytecode versions
8080
# "py": ["0.1", "0.2", "0.3", "0.4"]
81-
# }
81+
# },
82+
# // The following entries were added in file format version 2.
83+
# path: "micropython/bluetooth/aioble",
8284
# },
8385
# ...
8486
# }
@@ -122,7 +124,7 @@
122124
import time
123125

124126

125-
_JSON_VERSION_INDEX = 1
127+
_JSON_VERSION_INDEX = 2
126128
_JSON_VERSION_PACKAGE = 1
127129

128130

@@ -268,7 +270,7 @@ def _copy_as_py(
268270

269271
# Update to the latest metadata, and add any new versions to the package in
270272
# the index json.
271-
def _update_index_package_metadata(index_package_json, metadata, mpy_version):
273+
def _update_index_package_metadata(index_package_json, metadata, mpy_version, package_path):
272274
index_package_json["version"] = metadata.version or ""
273275
index_package_json["author"] = "" # TODO: Make manifestfile.py capture this.
274276
index_package_json["description"] = metadata.description or ""
@@ -283,6 +285,9 @@ def _update_index_package_metadata(index_package_json, metadata, mpy_version):
283285
print(" New version {}={}".format(v, metadata.version))
284286
index_package_json["versions"][v].append(metadata.version)
285287

288+
# The following entries were added in file format version 2.
289+
index_package_json["path"] = package_path
290+
286291

287292
def build(output_path, hash_prefix_len, mpy_cross_path):
288293
import manifestfile
@@ -318,7 +323,8 @@ def build(output_path, hash_prefix_len, mpy_cross_path):
318323

319324
for lib_dir in lib_dirs:
320325
for manifest_path in glob.glob(os.path.join(lib_dir, "**", "manifest.py"), recursive=True):
321-
print("{}".format(os.path.dirname(manifest_path)))
326+
package_path = os.path.dirname(manifest_path)
327+
print("{}".format(package_path))
322328
# .../foo/manifest.py -> foo
323329
package_name = os.path.basename(os.path.dirname(manifest_path))
324330

@@ -342,7 +348,9 @@ def build(output_path, hash_prefix_len, mpy_cross_path):
342348
}
343349
index_json["packages"].append(index_package_json)
344350

345-
_update_index_package_metadata(index_package_json, manifest.metadata(), mpy_version)
351+
_update_index_package_metadata(
352+
index_package_json, manifest.metadata(), mpy_version, package_path
353+
)
346354

347355
# This is the package json that mip/mpremote downloads.
348356
mpy_package_json = {

0 commit comments

Comments
 (0)