Skip to content

Commit 63bf8f7

Browse files
committed
Merge branch 'main' into new_dhcp_state_machine
# Conflicts: # adafruit_wiznet5k/adafruit_wiznet5k.py
2 parents aa30f2e + d2637d7 commit 63bf8f7

File tree

2 files changed

+126
-31
lines changed

2 files changed

+126
-31
lines changed

adafruit_wiznet5k/adafruit_wiznet5k.py

Lines changed: 69 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,13 @@
4545

4646
from random import randint
4747
import time
48+
import gc
4849
from micropython import const
4950

5051
from adafruit_bus_device.spi_device import SPIDevice
51-
from adafruit_wiznet5k.adafruit_wiznet5k_debug import debug_msg
5252
import adafruit_wiznet5k.adafruit_wiznet5k_dhcp as dhcp
5353
import adafruit_wiznet5k.adafruit_wiznet5k_dns as dns
54+
from adafruit_wiznet5k.adafruit_wiznet5k_debug import debug_msg
5455

5556
# Wiznet5k Registers
5657
_REG_MR = const(0x0000) # Mode
@@ -110,9 +111,9 @@
110111

111112
# Socket n Interrupt Register
112113
_SNIR_SEND_OK = const(0x10)
113-
_SNIR_TIMEOUT = const(0x08)
114+
SNIR_TIMEOUT = const(0x08)
114115
_SNIR_RECV = const(0x04)
115-
_SNIR_DISCON = const(0x02)
116+
SNIR_DISCON = const(0x02)
116117
_SNIR_CON = const(0x01)
117118

118119
_CH_SIZE = const(0x100)
@@ -149,6 +150,8 @@ class WIZNET5K: # pylint: disable=too-many-public-methods, too-many-instance-at
149150
_UDP_MODE = const(0x02)
150151
_TLS_MODE = const(0x03) # This is NOT currently implemented
151152

153+
_sockets_reserved = []
154+
152155
# pylint: disable=too-many-arguments
153156
def __init__(
154157
self,
@@ -192,6 +195,13 @@ def __init__(
192195
self._ch_base_msb = 0
193196
if self._w5xxx_init() != 1:
194197
raise RuntimeError("Failed to initialize WIZnet module.")
198+
if self._chip_type == "w5100s":
199+
WIZNET5K._sockets_reserved = [False] * (_W5100_MAX_SOCK_NUM - 1)
200+
elif self._chip_type == "w5500":
201+
WIZNET5K._sockets_reserved = [False] * (_W5200_W5500_MAX_SOCK_NUM - 1)
202+
else:
203+
raise RuntimeError("Unrecognized chip type.")
204+
195205
# Set MAC address
196206
self.mac_address = mac
197207
self.src_port = 0
@@ -719,23 +729,62 @@ def _send_socket_cmd(self, socket: int, cmd: int) -> None:
719729
while self.read_sncr(socket) != b"\x00":
720730
debug_msg("waiting for SNCR to clear...", self._debug)
721731

722-
def get_socket(self) -> int:
723-
"""Request, allocate and return a socket from the W5k chip.
732+
def get_socket(self, *, reserve_socket=False) -> int:
733+
"""
734+
Request, allocate and return a socket from the W5k chip.
735+
736+
Cycle through the sockets to find the first available one. If the called with
737+
reserve_socket=True, update the list of reserved sockets (intended to be used with
738+
socket.socket()). Note that reserved sockets must be released by calling
739+
cancel_reservation() once they are no longer needed.
740+
741+
If all sockets are reserved, no sockets are available for DNS calls, etc. Therefore,
742+
one socket cannot be reserved. Since socket 0 is the only socket that is capable of
743+
operating in MacRAW mode, it is the non-reservable socket.
724744
725-
Cycle through the sockets to find the first available one, if any.
745+
:param bool reserve_socket: Whether to reserve the socket.
726746
727-
:return int: The first available socket. Returns 0xFF if no sockets are free.
747+
:returns int: The first available socket.
748+
749+
:raises RuntimeError: If no socket is available.
728750
"""
729-
debug_msg("get_socket", self._debug)
730751

731-
sock = _SOCKET_INVALID
732-
for _sock in range(self.max_sockets):
733-
status = self.socket_status(_sock)[0]
734-
if status == SNSR_SOCK_CLOSED:
735-
sock = _sock
736-
break
737-
debug_msg("Allocated socket #{}".format(sock), self._debug)
738-
return sock
752+
debug_msg("*** Get socket.", self._debug)
753+
# Prefer socket zero for none reserved calls as it cannot be reserved.
754+
if not reserve_socket and self.socket_status(0)[0] == SNSR_SOCK_CLOSED:
755+
debug_msg("Allocated socket # 0", self._debug)
756+
return 0
757+
# Then check the other sockets.
758+
759+
# Call garbage collection to encourage socket.__del__() be called to on any
760+
# destroyed instances. Not at all guaranteed to work!
761+
gc.collect()
762+
debug_msg(
763+
"Reserved sockets: {}".format(WIZNET5K._sockets_reserved), self._debug
764+
)
765+
766+
for socket_number, reserved in enumerate(WIZNET5K._sockets_reserved, start=1):
767+
if (
768+
not reserved
769+
and self.socket_status(socket_number)[0] == SNSR_SOCK_CLOSED
770+
):
771+
if reserve_socket:
772+
WIZNET5K._sockets_reserved[socket_number - 1] = True
773+
debug_msg(
774+
"Allocated socket # {}.".format(socket_number),
775+
self._debug,
776+
)
777+
return socket_number
778+
raise RuntimeError("Out of sockets.")
779+
780+
@staticmethod
781+
def release_socket(socket_number):
782+
"""
783+
Update the socket reservation list when a socket is no longer reserved.
784+
785+
:param int socket_number: The socket to release.
786+
"""
787+
WIZNET5K._sockets_reserved[socket_number - 1] = False
739788

740789
def socket_listen(
741790
self, socket_num: int, port: int, conn_mode: int = _SNMR_TCP
@@ -831,7 +880,7 @@ def socket_open(self, socket_num: int, conn_mode: int = _SNMR_TCP) -> int:
831880
time.sleep(0.00025)
832881

833882
self.write_snmr(socket_num, conn_mode)
834-
self._write_snir(socket_num, 0xFF)
883+
self.write_snir(socket_num, 0xFF)
835884

836885
if self.src_port > 0:
837886
# write to socket source port
@@ -1050,7 +1099,7 @@ def socket_write(
10501099
"Hardware timeout while sending on socket {}.".format(socket_num)
10511100
)
10521101
time.sleep(0.001)
1053-
self._write_snir(socket_num, _SNIR_SEND_OK)
1102+
self.write_snir(socket_num, _SNIR_SEND_OK)
10541103
return ret
10551104

10561105
# Socket-Register Methods
@@ -1130,14 +1179,14 @@ def read_snsr(self, sock: int) -> Optional[bytearray]:
11301179
return self._read_socket(sock, _REG_SNSR)
11311180

11321181
def read_snir(self, sock: int) -> Optional[bytearray]:
1133-
"""Read Socket n Interrupt Register."""
1182+
"""Read Socket n Status Register."""
11341183
return self._read_socket(sock, _REG_SNIR)
11351184

11361185
def write_snmr(self, sock: int, protocol: int) -> None:
11371186
"""Write to Socket n Mode Register."""
11381187
self._write_socket(sock, _REG_SNMR, protocol)
11391188

1140-
def _write_snir(self, sock: int, data: int) -> None:
1189+
def write_snir(self, sock: int, data: int) -> None:
11411190
"""Write to Socket n Interrupt Register."""
11421191
self._write_socket(sock, _REG_SNIR, data)
11431192

adafruit_wiznet5k/adafruit_wiznet5k_socket.py

Lines changed: 57 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -227,30 +227,55 @@ def __init__(
227227
"""
228228
if family != AF_INET:
229229
raise RuntimeError("Only AF_INET family supported by W5K modules.")
230+
self._socket_closed = False
230231
self._sock_type = type
231232
self._buffer = b""
232233
self._timeout = _default_socket_timeout
233234
self._listen_port = None
234235

235-
self._socknum = _the_interface.get_socket()
236+
self._socknum = _the_interface.get_socket(reserve_socket=True)
236237
if self._socknum == _SOCKET_INVALID:
237238
raise RuntimeError("Failed to allocate socket.")
238239

240+
def __del__(self):
241+
_the_interface.release_socket(self._socknum)
242+
239243
def __enter__(self):
240244
return self
241245

242246
def __exit__(self, exc_type, exc_val, exc_tb) -> None:
247+
_the_interface.release_socket(self._socknum)
243248
if self._sock_type == SOCK_STREAM:
244-
self._disconnect()
245-
stamp = time.monotonic()
246-
while self._status == wiznet5k.adafruit_wiznet5k.SNSR_SOCK_FIN_WAIT:
247-
if time.monotonic() - stamp > 1000:
248-
raise RuntimeError("Failed to disconnect socket")
249-
self.close()
250-
stamp = time.monotonic()
251-
while self._status != wiznet5k.adafruit_wiznet5k.SNSR_SOCK_CLOSED:
252-
if time.monotonic() - stamp > 1000:
253-
raise RuntimeError("Failed to close socket")
249+
_the_interface.write_snir(
250+
self._socknum, 0xFF
251+
) # Reset socket interrupt register.
252+
_the_interface.socket_disconnect(self._socknum)
253+
mask = (
254+
wiznet5k.adafruit_wiznet5k.SNIR_TIMEOUT
255+
| wiznet5k.adafruit_wiznet5k.SNIR_DISCON
256+
)
257+
while not _the_interface.read_snir(self._socknum)[0] & mask:
258+
pass
259+
_the_interface.write_snir(
260+
self._socknum, 0xFF
261+
) # Reset socket interrupt register.
262+
_the_interface.socket_close(self._socknum)
263+
while (
264+
_the_interface.socket_status(self._socknum)[0]
265+
!= wiznet5k.adafruit_wiznet5k.SNSR_SOCK_CLOSED
266+
):
267+
pass
268+
269+
# This works around problems with using a class method as a decorator.
270+
def _check_socket_closed(func): # pylint: disable=no-self-argument
271+
"""Decorator to check whether the socket object has been closed."""
272+
273+
def wrapper(self, *args, **kwargs):
274+
if self._socket_closed: # pylint: disable=protected-access
275+
raise RuntimeError("The socket has been closed.")
276+
return func(self, *args, **kwargs) # pylint: disable=not-callable
277+
278+
return wrapper
254279

255280
@property
256281
def _status(self) -> int:
@@ -289,6 +314,7 @@ def _connected(self) -> bool:
289314
self.close()
290315
return result
291316

317+
@_check_socket_closed
292318
def getpeername(self) -> Tuple[str, int]:
293319
"""
294320
Return the remote address to which the socket is connected.
@@ -299,6 +325,7 @@ def getpeername(self) -> Tuple[str, int]:
299325
self._socknum
300326
)
301327

328+
@_check_socket_closed
302329
def bind(self, address: Tuple[Optional[str], int]) -> None:
303330
"""
304331
Bind the socket to address. The socket must not already be bound.
@@ -344,6 +371,7 @@ def _bind(self, address: Tuple[Optional[str], int]) -> None:
344371
)
345372
self._buffer = b""
346373

374+
@_check_socket_closed
347375
def listen(self, backlog: int = 0) -> None:
348376
"""
349377
Enable a server to accept connections.
@@ -355,6 +383,7 @@ def listen(self, backlog: int = 0) -> None:
355383
_the_interface.socket_listen(self._socknum, self._listen_port)
356384
self._buffer = b""
357385

386+
@_check_socket_closed
358387
def accept(
359388
self,
360389
) -> Tuple[socket, Tuple[str, int]]:
@@ -389,6 +418,7 @@ def accept(
389418
raise RuntimeError("Failed to open new listening socket")
390419
return client_sock, addr
391420

421+
@_check_socket_closed
392422
def connect(self, address: Tuple[str, int]) -> None:
393423
"""
394424
Connect to a remote socket at address.
@@ -408,6 +438,7 @@ def connect(self, address: Tuple[str, int]) -> None:
408438
raise RuntimeError("Failed to connect to host ", address[0])
409439
self._buffer = b""
410440

441+
@_check_socket_closed
411442
def send(self, data: Union[bytes, bytearray]) -> int:
412443
"""
413444
Send data to the socket. The socket must be connected to a remote socket.
@@ -423,6 +454,7 @@ def send(self, data: Union[bytes, bytearray]) -> int:
423454
gc.collect()
424455
return bytes_sent
425456

457+
@_check_socket_closed
426458
def sendto(self, data: bytearray, *flags_and_or_address: any) -> int:
427459
"""
428460
Send data to the socket. The socket should not be connected to a remote socket, since the
@@ -446,6 +478,7 @@ def sendto(self, data: bytearray, *flags_and_or_address: any) -> int:
446478
self.connect(address)
447479
return self.send(data)
448480

481+
@_check_socket_closed
449482
def recv(
450483
# pylint: disable=too-many-branches
451484
self,
@@ -501,6 +534,7 @@ def _embed_recv(
501534
gc.collect()
502535
return ret
503536

537+
@_check_socket_closed
504538
def recvfrom(self, bufsize: int, flags: int = 0) -> Tuple[bytes, Tuple[str, int]]:
505539
"""
506540
Receive data from the socket. The return value is a pair (bytes, address) where bytes is
@@ -521,6 +555,7 @@ def recvfrom(self, bufsize: int, flags: int = 0) -> Tuple[bytes, Tuple[str, int]
521555
),
522556
)
523557

558+
@_check_socket_closed
524559
def recv_into(self, buffer: bytearray, nbytes: int = 0, flags: int = 0) -> int:
525560
"""
526561
Receive up to nbytes bytes from the socket, storing the data into a buffer
@@ -539,6 +574,7 @@ def recv_into(self, buffer: bytearray, nbytes: int = 0, flags: int = 0) -> int:
539574
buffer[:nbytes] = bytes_received
540575
return nbytes
541576

577+
@_check_socket_closed
542578
def recvfrom_into(
543579
self, buffer: bytearray, nbytes: int = 0, flags: int = 0
544580
) -> Tuple[int, Tuple[str, int]]:
@@ -597,12 +633,15 @@ def _disconnect(self) -> None:
597633
raise RuntimeError("Socket must be a TCP socket.")
598634
_the_interface.socket_disconnect(self._socknum)
599635

636+
@_check_socket_closed
600637
def close(self) -> None:
601638
"""
602639
Mark the socket closed. Once that happens, all future operations on the socket object
603640
will fail. The remote end will receive no more data.
604641
"""
642+
_the_interface.release_socket(self._socknum)
605643
_the_interface.socket_close(self._socknum)
644+
self._socket_closed = True
606645

607646
def _available(self) -> int:
608647
"""
@@ -612,6 +651,7 @@ def _available(self) -> int:
612651
"""
613652
return _the_interface.socket_available(self._socknum, self._sock_type)
614653

654+
@_check_socket_closed
615655
def settimeout(self, value: Optional[float]) -> None:
616656
"""
617657
Set a timeout on blocking socket operations. The value argument can be a
@@ -628,6 +668,7 @@ def settimeout(self, value: Optional[float]) -> None:
628668
else:
629669
raise ValueError("Timeout must be None, 0.0 or a positive numeric value.")
630670

671+
@_check_socket_closed
631672
def gettimeout(self) -> Optional[float]:
632673
"""
633674
Return the timeout in seconds (float) associated with socket operations, or None if no
@@ -637,6 +678,7 @@ def gettimeout(self) -> Optional[float]:
637678
"""
638679
return self._timeout
639680

681+
@_check_socket_closed
640682
def setblocking(self, flag: bool) -> None:
641683
"""
642684
Set blocking or non-blocking mode of the socket: if flag is false, the socket is set
@@ -659,6 +701,7 @@ def setblocking(self, flag: bool) -> None:
659701
else:
660702
raise TypeError("Flag must be a boolean.")
661703

704+
@_check_socket_closed
662705
def getblocking(self) -> bool:
663706
"""
664707
Return True if socket is in blocking mode, False if in non-blocking.
@@ -670,16 +713,19 @@ def getblocking(self) -> bool:
670713
return self.gettimeout() == 0
671714

672715
@property
716+
@_check_socket_closed
673717
def family(self) -> int:
674718
"""Socket family (always 0x03 in this implementation)."""
675719
return 3
676720

677721
@property
722+
@_check_socket_closed
678723
def type(self):
679724
"""Socket type."""
680725
return self._sock_type
681726

682727
@property
728+
@_check_socket_closed
683729
def proto(self):
684730
"""Socket protocol (always 0x00 in this implementation)."""
685731
return 0

0 commit comments

Comments
 (0)