Skip to content

Commit c8604d9

Browse files
committed
micropython/usb/device/mtp: Add USB MTP class driver.
Signed-off-by: Andrew Leech <andrew@alelec.net>
1 parent 8467c47 commit c8604d9

File tree

2 files changed

+61
-65
lines changed

2 files changed

+61
-65
lines changed

micropython/usb/examples/device/mtp_example.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818

1919
import os
2020
import time
21-
import usb.device
21+
import machine
2222
from usb.device.mtp import MTPInterface
2323

2424
def main():
@@ -32,16 +32,18 @@ def main():
3232
# Here we use '/' to expose the entire filesystem
3333
mtp = MTPInterface(root_dir="/")
3434

35-
# Initialize the MTP interface
36-
mtp.init()
35+
# Create a USB device using machine.USBDevice
36+
usb_dev = machine.USBDevice(0xF055, 0x9802, "MicroPython", "MTP Device", "123456789")
3737

38-
# Initialize the USB device with the MTP interface
39-
# This registers the device with USB and starts accepting connections
40-
usb.device.get().init(mtp, builtin_driver=True)
38+
# Add the MTP interface to the USB device
39+
usb_dev.add_interface(mtp)
40+
41+
# Enable the USB device
42+
usb_dev.enable()
4143

4244
# Wait for USB to be configured
4345
print("Waiting for USB connection...")
44-
while not mtp.is_open():
46+
while not usb_dev.configured():
4547
time.sleep(0.1)
4648

4749
print("USB MTP device connected!")
@@ -55,6 +57,7 @@ def main():
5557
except KeyboardInterrupt:
5658
# Clean up on exit
5759
print("Exiting MTP example")
60+
usb_dev.disable()
5861

5962
if __name__ == "__main__":
6063
main()

micropython/usb/usb-device-mtp/usb/device/mtp.py

Lines changed: 51 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -162,11 +162,9 @@ def __init__(self, root_dir="/"):
162162
self._bulk_in_ep = None
163163
self._bulk_out_ep = None
164164
self._intr_ep = None
165-
self._bulk_in_buf = None
166-
self._bulk_out_buf = None
167-
self._rx_buf = None
168-
self._tx_buf = None
169-
self._rx_packet = None
165+
self._itf_num = None
166+
167+
# Create packet and container buffers
170168
self._container_buf = bytearray(MTP_MAX_PACKET_SIZE)
171169
self._container_mview = memoryview(self._container_buf)
172170

@@ -193,21 +191,22 @@ def __init__(self, root_dir="/"):
193191
self._transfer_offset = 0
194192
self._transfer_length = 0
195193
self._transfer_file = None
194+
195+
# Initialize with default parameters
196+
self.init()
196197

197198
def init(self, packet_size=MTP_MAX_PACKET_SIZE):
198199
"""Initialize the MTP interface with the given configuration.
199200
200201
Args:
201202
packet_size: Maximum packet size for bulk transfers (default: 512)
202203
"""
203-
# Create buffers
204-
self._bulk_in_buf = Buffer(packet_size * 2) # Double buffering
205-
self._bulk_out_buf = Buffer(packet_size * 2)
204+
# Create buffers for data transfer
206205
self._rx_buf = bytearray(packet_size)
207206
self._rx_packet = memoryview(self._rx_buf)
208207
self._tx_buf = bytearray(packet_size)
209208

210-
# Set internal state
209+
# Reset internal state
211210
self._transaction_id = 0
212211
self._session_open = False
213212
self._handles = {}
@@ -297,56 +296,53 @@ def get_object_info(self, handle):
297296
except OSError:
298297
return None
299298

300-
def desc_cfg(self, desc):
299+
def desc_cfg(self, desc, itf_num, ep_num, strs):
301300
"""Build configuration descriptor.
302301
303302
Args:
304303
desc: Descriptor builder object
304+
itf_num: Interface number to use
305+
ep_num: First endpoint number to use
306+
strs: String descriptor array
305307
"""
306-
# Calculate total descriptor length
307-
len_itf = 9 # Interface descriptor
308-
len_eps = 7 * 3 # 3 endpoint descriptors
309-
len_itf_assoc = 8 # Interface Association Descriptor
310-
len_func = 5 # Functional descriptor (minimal)
311-
312-
total_len = len_itf + len_eps + len_itf_assoc + len_func
313-
314-
# Reserve space
315-
itf_idx = desc.reserve(total_len)
316-
self._itf_num = itf_idx
317-
318-
# Build descriptor
319-
# Interface Association Descriptor
320-
desc.interface_assoc(
321-
itf_idx, 1, MTP_CLASS, MTP_SUBCLASS, MTP_PROTOCOL, "MTP"
322-
)
308+
# Store interface number
309+
self._itf_num = itf_num
310+
311+
# Interface Association Descriptor for MTP
312+
desc.interface_assoc(itf_num, 1, MTP_CLASS, MTP_SUBCLASS, MTP_PROTOCOL)
323313

324314
# Interface descriptor
325-
desc.interface(
326-
itf_idx, 0, 3, MTP_CLASS, MTP_SUBCLASS, MTP_PROTOCOL, "MTP"
315+
desc.interface(itf_num, 3, MTP_CLASS, MTP_SUBCLASS, MTP_PROTOCOL)
316+
317+
# Class-specific functional descriptor
318+
desc.pack(
319+
"<BBBBB",
320+
5, # bFunctionLength
321+
0x24, # bDescriptorType - CS_INTERFACE
322+
0x00, # bDescriptorSubtype
323+
0x01, # bcdMTPVersion - 1.0 LSB
324+
0x00, # bcdMTPVersion - 1.0 MSB
327325
)
328326

329-
# Minimal functional descriptor
330-
desc.add(b"\x05\x24\x00\x01\x00") # Minimal class-specific descriptor
331-
332327
# Endpoint descriptors
333328
# Bulk OUT endpoint
334-
self._bulk_out_ep = usb.device.get().alloc_ep(1, usb.device.EP_TYPE_BULK, MTP_BULK_EP_SIZE)
335-
desc.endpoint(self._bulk_out_ep, usb.device.EP_TYPE_BULK, MTP_BULK_EP_SIZE)
329+
self._bulk_out_ep = ep_num
330+
desc.endpoint(self._bulk_out_ep, "bulk", MTP_BULK_EP_SIZE, 0)
336331

337332
# Bulk IN endpoint
338-
self._bulk_in_ep = usb.device.get().alloc_ep(1, usb.device.EP_TYPE_BULK, MTP_BULK_EP_SIZE)
339-
desc.endpoint(self._bulk_in_ep | 0x80, usb.device.EP_TYPE_BULK, MTP_BULK_EP_SIZE)
333+
self._bulk_in_ep = (ep_num + 1) | 0x80
334+
desc.endpoint(self._bulk_in_ep, "bulk", MTP_BULK_EP_SIZE, 0)
340335

341336
# Interrupt IN endpoint for events
342-
self._intr_ep = usb.device.get().alloc_ep(1, usb.device.EP_TYPE_INTERRUPT, MTP_INTERRUPT_EP_SIZE)
343-
desc.endpoint(self._intr_ep | 0x80, usb.device.EP_TYPE_INTERRUPT, MTP_INTERRUPT_EP_SIZE, 10) # 10ms interval
337+
self._intr_ep = (ep_num + 2) | 0x80
338+
desc.endpoint(self._intr_ep, "interrupt", MTP_INTERRUPT_EP_SIZE, 10) # 10ms interval
344339

345-
def control_req(self, req, *args, **kwargs):
346-
"""Handle class-specific control requests.
340+
def on_interface_control_xfer(self, stage, request):
341+
"""Handle class-specific interface control transfers.
347342
348343
Args:
349-
req: USB_SETUP_DESCRIPTOR setup packet
344+
stage: Stage of the control transfer
345+
request: The setup packet
350346
351347
Returns:
352348
True if request was handled, False otherwise
@@ -380,27 +376,26 @@ def on_open(self):
380376

381377
def is_open(self):
382378
"""Check if the device is configured and open."""
383-
return usb.device.get().configured() and not self._session_open
379+
return super().is_open()
384380

385381
def _submit_out_transfer(self):
386382
"""Submit an OUT transfer to receive data."""
387-
if not usb.device.get().configured():
383+
if not super().is_open():
388384
return
389-
usb.device.get().submit_xfer(
390-
self._bulk_out_ep, self._rx_packet, self._on_data_received
391-
)
385+
386+
self.submit_xfer(self._bulk_out_ep, self._rx_packet, self._on_data_received)
392387

393-
def _on_data_received(self, ep, data, status):
388+
def _on_data_received(self, ep, res, num_bytes):
394389
"""Handle received data from the USB host.
395390
396391
Args:
397392
ep: Endpoint number
398-
data: Received data
399-
status: Transfer status
393+
res: Result code (0 for success)
394+
num_bytes: Number of bytes received
400395
"""
401-
if status == usb.device.XFER_COMPLETED and len(data) > 0:
396+
if res == 0 and num_bytes > 0:
402397
# Process the received data
403-
self._process_container(data)
398+
self._process_container(self._rx_packet[:num_bytes])
404399

405400
# Submit a new transfer
406401
self._submit_out_transfer()
@@ -411,7 +406,7 @@ def _send_data(self, data):
411406
Args:
412407
data: Data to send
413408
"""
414-
if not usb.device.get().configured():
409+
if not super().is_open():
415410
return
416411

417412
# Copy data to the transmit buffer
@@ -420,17 +415,15 @@ def _send_data(self, data):
420415
tx_view[:length] = data[:length]
421416

422417
# Submit the transfer
423-
usb.device.get().submit_xfer(
424-
self._bulk_in_ep | 0x80, tx_view[:length], self._on_data_sent
425-
)
418+
self.submit_xfer(self._bulk_in_ep, tx_view[:length], self._on_data_sent)
426419

427-
def _on_data_sent(self, ep, data, status):
420+
def _on_data_sent(self, ep, res, num_bytes):
428421
"""Handle completion of data transmission.
429422
430423
Args:
431424
ep: Endpoint number
432-
data: Sent data
433-
status: Transfer status
425+
res: Result code (0 for success)
426+
num_bytes: Number of bytes sent
434427
"""
435428
pass # Could be used for flow control
436429

0 commit comments

Comments
 (0)