pypicoboot 1.2__tar.gz → 1.3.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pypicoboot
3
- Version: 1.2
3
+ Version: 1.3.1
4
4
  Summary: Pico Boot for Python
5
5
  Home-page: https://github.com/polhenarejos/pypicoboot
6
6
  Author: Pol Henarejos
@@ -0,0 +1,3 @@
1
+ from ._version import __version__
2
+ from .picoboot import PicoBoot, Model
3
+ from .core.exceptions import PicoBootError, PicoBootNotFoundError, PicoBootInvalidStateError
@@ -17,4 +17,4 @@
17
17
  */
18
18
  """
19
19
 
20
- __version__ = "1.2"
20
+ __version__ = "1.3.1"
@@ -1 +1,2 @@
1
1
  from .enums import NamedIntEnum
2
+ from .exceptions import *
@@ -0,0 +1,28 @@
1
+ """
2
+ /*
3
+ * This file is part of the pypicoboot distribution (https://github.com/polhenarejos/pypicoboot).
4
+ * Copyright (c) 2025 Pol Henarejos.
5
+ *
6
+ * This program is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU Affero General Public License as published by
8
+ * the Free Software Foundation, version 3.
9
+ *
10
+ * This program is distributed in the hope that it will be useful, but
11
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
+ * Affero General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU Affero General Public License
16
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
17
+ */
18
+ """
19
+
20
+
21
+ class PicoBootError(Exception):
22
+ pass
23
+
24
+ class PicoBootNotFoundError(PicoBootError):
25
+ pass
26
+
27
+ class PicoBootInvalidStateError(PicoBootError):
28
+ pass
@@ -27,6 +27,7 @@ from .utils import uint_to_int
27
27
  from .core.enums import NamedIntEnum
28
28
  from .picobootmonitor import PicoBootMonitor, PicoBootMonitorObserver
29
29
  from .core.log import get_logger
30
+ from .core.exceptions import PicoBootError, PicoBootNotFoundError, PicoBootInvalidStateError
30
31
 
31
32
  logger = get_logger("PicoBoot")
32
33
 
@@ -126,7 +127,7 @@ class PartitionInfoType(NamedIntEnum):
126
127
  SLOT_1 = -3
127
128
  IMAGE = -4
128
129
 
129
- class Model(NamedIntEnum):
130
+ class Platform(NamedIntEnum):
130
131
  RP2040 = 0x01754d
131
132
  RP2350 = 0x02754d
132
133
  UNKNOWN = 0x000000
@@ -134,9 +135,6 @@ class Model(NamedIntEnum):
134
135
  class Addresses(NamedIntEnum):
135
136
  BOOTROM_MAGIC = 0x00000010
136
137
 
137
- class PicoBootError(Exception):
138
- pass
139
-
140
138
  class PicoBoot:
141
139
 
142
140
  def __init__(self, dev: usb.core.Device, intf, ep_out, ep_in) -> None:
@@ -151,9 +149,9 @@ class PicoBoot:
151
149
  logger.debug("Guessing flash size...")
152
150
  self._memory = self._guess_flash_size()
153
151
  logger.debug(f"Detected flash size: {self._memory // 1024} kB")
154
- logger.debug("Determining model...")
155
- self._model = self._determine_model()
156
- logger.debug(f"Detected model: {self._model.name}")
152
+ logger.debug("Determining platform...")
153
+ self._platform = self._determine_platform()
154
+ logger.debug(f"Detected platform: {self._platform.name}")
157
155
 
158
156
  class PicoBootObserver(PicoBootMonitorObserver):
159
157
 
@@ -193,7 +191,7 @@ class PicoBoot:
193
191
  devices = list(devices) if devices is not None else []
194
192
  if not devices:
195
193
  logger.error("No device found in PICOBOOT mode")
196
- raise PicoBootError("No device found in PICOBOOT mode")
194
+ raise PicoBootNotFoundError("No device found in PICOBOOT mode")
197
195
 
198
196
  dev = None
199
197
  if serial is None:
@@ -211,7 +209,7 @@ class PicoBoot:
211
209
  break
212
210
  if dev is None:
213
211
  logger.error("No device found with this serial number")
214
- raise PicoBootError("No device found with this serial number")
212
+ raise PicoBootNotFoundError("No device found with this serial number")
215
213
 
216
214
  # Ensure active configuration
217
215
  # macOS does not allow detach_kernel_driver, and often returns Access Denied
@@ -240,7 +238,7 @@ class PicoBoot:
240
238
  break
241
239
  if intf is None:
242
240
  logger.error("No interface found with PICOBOOT at the device")
243
- raise PicoBootError("No interface found with PICOBOOT at the device")
241
+ raise PicoBootNotFoundError("No interface found with PICOBOOT at the device")
244
242
 
245
243
  #usb.util.claim_interface(dev, intf.bInterfaceNumber)
246
244
 
@@ -256,7 +254,7 @@ class PicoBoot:
256
254
 
257
255
  if ep_in is None or ep_out is None:
258
256
  logger.error("No PICOBOOT BULK_IN/BULK_OUT endpoints found")
259
- raise PicoBootError("No PICOBOOT BULK_IN/BULK_OUT endpoints found")
257
+ raise PicoBootNotFoundError("No PICOBOOT BULK_IN/BULK_OUT endpoints found")
260
258
  logger.info("PICOBOOT device opened successfully.")
261
259
  return cls(dev, intf, ep_out, ep_in)
262
260
 
@@ -272,6 +270,16 @@ class PicoBoot:
272
270
  def has_device(self):
273
271
  return self.dev is not None
274
272
 
273
+ @property
274
+ def serial_number(self) -> int:
275
+ s = usb.util.get_string(self.dev, self.dev.iSerialNumber)
276
+ return int(s, 16)
277
+
278
+ @property
279
+ def serial_number_str(self) -> str:
280
+ s = usb.util.get_string(self.dev, self.dev.iSerialNumber)
281
+ return s
282
+
275
283
  def interface_reset(self) -> None:
276
284
  logger.debug("Resetting interface...")
277
285
  self.dev.ctrl_transfer(
@@ -348,7 +356,11 @@ class PicoBoot:
348
356
  logger.debug(f"Sending command {cmd_id} (0x{cmd_id:02X}) with token {token} (0x{token:08X}) and transfer_length {transfer_length}")
349
357
 
350
358
  logger.trace(f"Command header: {hexlify(header).decode()}")
351
- self.ep_out.write(header, timeout=timeout)
359
+ try:
360
+ self.ep_out.write(header, timeout=timeout)
361
+ except usb.core.USBError as e:
362
+ logger.error(f"Failed to send command header: {e}")
363
+ raise PicoBootInvalidStateError("Failed to send command header: " + str(e))
352
364
  logger.debug(f"Command header sent: {hexlify(header).decode()}")
353
365
 
354
366
  data_in = b""
@@ -359,7 +371,11 @@ class PicoBoot:
359
371
  chunks = []
360
372
  maxpkt = self.ep_in.wMaxPacketSize
361
373
  while remaining > 0:
362
- chunk = bytes(self.ep_in.read(min(maxpkt, remaining), timeout=timeout))
374
+ try:
375
+ chunk = bytes(self.ep_in.read(min(maxpkt, remaining), timeout=timeout))
376
+ except usb.core.USBError as e:
377
+ logger.error(f"Failed to read data_in: {e}")
378
+ raise PicoBootInvalidStateError("Failed to read data_in: " + str(e))
363
379
  if not chunk:
364
380
  break
365
381
  chunks.append(chunk)
@@ -374,7 +390,11 @@ class PicoBoot:
374
390
  logger.error("data_out missing or too short for OUT command")
375
391
  raise ValueError("data_out missing or too short for OUT command")
376
392
  logger.trace(f"Sending data_out: {hexlify(data_out[:transfer_length]).decode()}")
377
- self.ep_out.write(data_out[:transfer_length], timeout=timeout)
393
+ try:
394
+ self.ep_out.write(data_out[:transfer_length], timeout=timeout)
395
+ except usb.core.USBError as e:
396
+ logger.error(f"Failed to send data_out: {e}")
397
+ raise PicoBootInvalidStateError("Failed to send data_out: " + str(e))
378
398
 
379
399
  try:
380
400
  logger.debug("Waiting for ACK...")
@@ -427,9 +447,9 @@ class PicoBoot:
427
447
 
428
448
  def reboot(self, delay_ms: int = 100) -> None:
429
449
  logger.debug(f"Rebooting device with delay_ms={delay_ms}")
430
- if (self.model == Model.RP2040):
450
+ if (self.platform == Platform.RP2040):
431
451
  self.reboot1(delay_ms=delay_ms)
432
- elif (self.model == Model.RP2350):
452
+ elif (self.platform == Platform.RP2350):
433
453
  self.reboot2(delay_ms=delay_ms)
434
454
 
435
455
  def exit_xip(self) -> None:
@@ -440,17 +460,17 @@ class PicoBoot:
440
460
  logger.debug("Requesting exclusive access to flash...")
441
461
  self._send_command(CommandID.EXCLUSIVE_ACCESS, args=struct.pack("<B", 1), transfer_length=0)
442
462
 
443
- def _determine_model(self) -> str:
444
- logger.debug("Determining device model...")
445
- if (hasattr(self, "_model")) and (self._model is not None):
446
- return self._model
463
+ def _determine_platform(self) -> str:
464
+ logger.debug("Determining device platform...")
465
+ if (hasattr(self, "_platform")) and (self._platform is not None):
466
+ return self._platform
447
467
  data = self.flash_read(Addresses.BOOTROM_MAGIC, 4)
448
468
  (magic,) = struct.unpack("<I", data)
449
- return Model(magic & 0xf0ffffff)
469
+ return Platform(magic & 0xf0ffffff)
450
470
 
451
471
  @property
452
- def model(self) -> str:
453
- return self._model
472
+ def platform(self) -> str:
473
+ return self._platform
454
474
 
455
475
  def _guess_flash_size(self) -> int:
456
476
  logger.debug("Guessing flash size...")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pypicoboot
3
- Version: 1.2
3
+ Version: 1.3.1
4
4
  Summary: Pico Boot for Python
5
5
  Home-page: https://github.com/polhenarejos/pypicoboot
6
6
  Author: Pol Henarejos
@@ -9,6 +9,7 @@ picoboot/picobootmonitor.py
9
9
  picoboot/utils.py
10
10
  picoboot/core/__init__.py
11
11
  picoboot/core/enums.py
12
+ picoboot/core/exceptions.py
12
13
  picoboot/core/log.py
13
14
  picoboot/tools/picotool.py
14
15
  pypicoboot.egg-info/PKG-INFO
@@ -1,5 +1,5 @@
1
1
  [build-system]
2
- requires = ["setuptools>=61.0"]
2
+ requires = ["setuptools"]
3
3
  build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
@@ -1,2 +0,0 @@
1
- from ._version import __version__
2
- from .picoboot import PicoBoot, Model
File without changes
File without changes
File without changes
File without changes
File without changes