From 5fd12457dbda5c717149d5dbb45e154a7467733d Mon Sep 17 00:00:00 2001 From: BiffoBear <martin@martinstehens.net> Date: Wed, 21 Jun 2023 08:26:04 +0700 Subject: [PATCH 1/6] Move message generation outside loop. --- adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py b/adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py index 8d42ca7..ccc6f76 100644 --- a/adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py +++ b/adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py @@ -366,9 +366,8 @@ def _handle_dhcp_message(self) -> int: raise ValueError( "FSM can only send messages while in SELECTING or REQUESTING states." ) + message_length = self._generate_dhcp_message(message_type=msg_type_out) for attempt in range(4): # Initial attempt plus 3 retries. - message_length = self._generate_dhcp_message(message_type=msg_type_out) - if self._renew: dhcp_server_address = self.dhcp_server_ip else: From ba001b791a887b745c955dc254650d81a7e87210 Mon Sep 17 00:00:00 2001 From: BiffoBear <martin@martinstehens.net> Date: Wed, 21 Jun 2023 08:29:51 +0700 Subject: [PATCH 2/6] Move move get UDP port to _handle_dhcp_message. --- adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py b/adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py index ccc6f76..51292b3 100644 --- a/adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py +++ b/adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py @@ -197,7 +197,6 @@ def _dsm_reset(self) -> None: state machine INIT state.""" debug_msg("Resetting DHCP state machine.", self._debug) self._socket_release() - self._dhcp_connection_setup() self.dhcp_server_ip = _BROADCAST_SERVER_ADDR self._eth.ifconfig = ( _UNASSIGNED_IP_ADDR, @@ -366,6 +365,7 @@ def _handle_dhcp_message(self) -> int: raise ValueError( "FSM can only send messages while in SELECTING or REQUESTING states." ) + self._dhcp_connection_setup() message_length = self._generate_dhcp_message(message_type=msg_type_out) for attempt in range(4): # Initial attempt plus 3 retries. if self._renew: @@ -431,7 +431,6 @@ def _dhcp_state_machine(self, *, blocking: bool = False) -> None: if self._dhcp_state == _STATE_RENEWING: debug_msg("FSM state is RENEWING.", self._debug) self._renew = "renew" - self._dhcp_connection_setup() self._start_time = time.monotonic() self._dhcp_state = _STATE_REQUESTING @@ -439,7 +438,6 @@ def _dhcp_state_machine(self, *, blocking: bool = False) -> None: debug_msg("FSM state is REBINDING.", self._debug) self._renew = "rebind" self.dhcp_server_ip = _BROADCAST_SERVER_ADDR - self._dhcp_connection_setup() self._start_time = time.monotonic() self._dhcp_state = _STATE_REQUESTING From a5a6d60ece3099f9c7437c47023fd4198e24fa25 Mon Sep 17 00:00:00 2001 From: BiffoBear <martin@martinstehens.net> Date: Wed, 21 Jun 2023 08:45:37 +0700 Subject: [PATCH 3/6] Move close UDP port to _handle_dhcp_message. --- adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py b/adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py index 51292b3..dd03cd9 100644 --- a/adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py +++ b/adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py @@ -196,7 +196,6 @@ def _dsm_reset(self) -> None: """Close the socket and set attributes to default values used by the state machine INIT state.""" debug_msg("Resetting DHCP state machine.", self._debug) - self._socket_release() self.dhcp_server_ip = _BROADCAST_SERVER_ADDR self._eth.ifconfig = ( _UNASSIGNED_IP_ADDR, @@ -215,7 +214,7 @@ def _dsm_reset(self) -> None: def _socket_release(self) -> None: """Close the socket if it exists.""" debug_msg("Releasing socket.", self._debug) - if self._wiz_sock: + if self._wiz_sock is not None: self._eth.socket_close(self._wiz_sock) self._wiz_sock = None debug_msg(" Socket released.", self._debug) @@ -386,12 +385,16 @@ def _handle_dhcp_message(self) -> int: return msg_type_in except ValueError as error: debug_msg(error, self._debug) + finally: + self._socket_release() if not self._blocking or self._renew: debug_msg( "No message, FSM is nonblocking or renewing, exiting loop.", self._debug, ) + self._socket_release() return 0 # Did not receive a response in a single attempt. + self._socket_release() raise TimeoutError( "No response from DHCP server after {} retries.".format(attempt) ) @@ -409,7 +412,6 @@ def _dhcp_state_machine(self, *, blocking: bool = False) -> None: now = time.monotonic() if now < self._t1: debug_msg("No timers have expired. Exiting FSM.", self._debug) - self._socket_release() return if now > self._lease: debug_msg( From 7805e1a3958de591ffb61ceb52084ade6eae274e Mon Sep 17 00:00:00 2001 From: BiffoBear <martin@martinstehens.net> Date: Wed, 21 Jun 2023 14:20:08 +0700 Subject: [PATCH 4/6] Move all socket calls to _receive_dhcp_response and _handle_dhcp_message. --- adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py | 131 ++++++++------------ 1 file changed, 52 insertions(+), 79 deletions(-) diff --git a/adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py b/adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py index dd03cd9..b4ff581 100644 --- a/adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py +++ b/adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py @@ -147,7 +147,6 @@ def __init__( # Set socket interface self._eth = eth - self._wiz_sock = None # DHCP state machine self._dhcp_state = _STATE_INIT @@ -211,45 +210,6 @@ def _dsm_reset(self) -> None: self._increment_transaction_id() self._start_time = time.monotonic() - def _socket_release(self) -> None: - """Close the socket if it exists.""" - debug_msg("Releasing socket.", self._debug) - if self._wiz_sock is not None: - self._eth.socket_close(self._wiz_sock) - self._wiz_sock = None - debug_msg(" Socket released.", self._debug) - - def _dhcp_connection_setup(self, timeout: float = 5.0) -> None: - """Initialise a UDP socket. - - Attempt to initialise a UDP socket. If the finite state machine (FSM) is in - blocking mode, repeat failed attempts until a socket is initialised or - the operation times out, then raise an exception. If the FSM is in non-blocking - mode, ignore the error and return. - - :param int timeout: Time to keep retrying if the FSM is in blocking mode. - Defaults to 5. - - :raises TimeoutError: If the FSM is in blocking mode and a socket cannot be - initialised. - """ - stop_time = time.monotonic() + timeout - debug_msg("Setting up connection for DHCP.", self._debug) - while self._wiz_sock is None and time.monotonic() < stop_time: - self._wiz_sock = self._eth.get_socket() - if self._wiz_sock == 0xFF: - self._wiz_sock = None - while time.monotonic() < stop_time: - self._eth.write_snmr(self._wiz_sock, 0x02) # Set UDP connection - self._eth.write_sock_port(self._wiz_sock, 68) # Set DHCP client port. - self._eth.write_sncr(self._wiz_sock, 0x01) # Open the socket. - if self._eth.read_snsr(self._wiz_sock) == 0x22: - self._eth.write_sndport(2, _DHCP_SERVER_PORT) - debug_msg("+ Connection OK, port set.", self._debug) - return - self._wiz_sock = None - raise RuntimeError("Unable to initialize UDP socket.") - def _increment_transaction_id(self) -> None: """Increment the transaction ID and roll over from 0x7fffffff to 0.""" debug_msg("Incrementing transaction ID", self._debug) @@ -278,7 +238,7 @@ def _next_retry_time(self, *, attempt: int, interval: int = 4) -> float: delay = 2**attempt * interval + randint(-1, 1) + time.monotonic() return delay - def _receive_dhcp_response(self, timeout: float) -> int: + def _receive_dhcp_response(self, socket_num: int, timeout: float) -> int: """ Receive data from the socket in response to a DHCP query. @@ -288,23 +248,22 @@ def _receive_dhcp_response(self, timeout: float) -> int: If the packet is too short, it is discarded and zero is returned. The maximum packet size is limited by the size of the global buffer. - :param float timeout: time.monotonic at which attempt should timeout. + :param int socket_num: Socket to read from. + :param float timeout: time.monotonic at which attempt should time out. :returns int: The number of bytes stored in the global buffer. """ debug_msg("Receiving a DHCP response.", self._debug) while time.monotonic() < timeout: # DHCP returns the query plus additional data. The query length is 236 bytes. - if self._eth.socket_available(self._wiz_sock, _SNMR_UDP) > 236: - bytes_count, bytes_read = self._eth.read_udp( - self._wiz_sock, _BUFF_LENGTH - ) + if self._eth.socket_available(socket_num, _SNMR_UDP) > 236: + bytes_count, bytes_read = self._eth.read_udp(socket_num, _BUFF_LENGTH) _BUFF[:bytes_count] = bytes_read debug_msg("Received {} bytes".format(bytes_count), self._debug) del bytes_read gc.collect() return bytes_count - raise TimeoutError("No DHCP response received.") + return 0 # No bytes received. def _process_messaging_states(self, *, message_type: int): """ @@ -355,6 +314,7 @@ def _handle_dhcp_message(self) -> int: :raises TimeoutError: If the FSM is in blocking mode and no valid response has been received before the timeout expires. """ + # pylint: disable=too-many-branches debug_msg("Processing SELECTING or REQUESTING state.", self._debug) if self._dhcp_state == _STATE_SELECTING: msg_type_out = _DHCP_DISCOVER @@ -364,40 +324,53 @@ def _handle_dhcp_message(self) -> int: raise ValueError( "FSM can only send messages while in SELECTING or REQUESTING states." ) - self._dhcp_connection_setup() - message_length = self._generate_dhcp_message(message_type=msg_type_out) - for attempt in range(4): # Initial attempt plus 3 retries. - if self._renew: - dhcp_server_address = self.dhcp_server_ip - else: - dhcp_server_address = _BROADCAST_SERVER_ADDR - self._eth.write_sndipr(self._wiz_sock, dhcp_server_address) - self._eth.write_sndport(self._wiz_sock, _DHCP_SERVER_PORT) - self._eth.socket_write(self._wiz_sock, _BUFF[:message_length]) - next_resend = self._next_retry_time(attempt=attempt) - while time.monotonic() < next_resend: - if self._receive_dhcp_response(next_resend): - try: - msg_type_in = self._parse_dhcp_response() + debug_msg("Setting up connection for DHCP.", self._debug) + if self._renew: + dhcp_server = self.dhcp_server_ip + else: + dhcp_server = _BROADCAST_SERVER_ADDR + sock_num = None + deadline = time.monotonic() + 5.0 + try: + while sock_num is None: + sock_num = self._eth.get_socket() + if sock_num == 0xFF: + sock_num = None + if time.monotonic() > deadline: + raise RuntimeError("Unable to initialize UDP socket.") + + self._eth.src_port = 68 + self._eth.socket_connect( + sock_num, dhcp_server, _DHCP_SERVER_PORT, conn_mode=0x02 + ) + self._eth.src_port = 0 + + message_length = self._generate_dhcp_message(message_type=msg_type_out) + for attempt in range(4): # Initial attempt plus 3 retries. + self._eth.socket_write(sock_num, _BUFF[:message_length]) + next_resend = self._next_retry_time(attempt=attempt) + while time.monotonic() < next_resend: + if self._receive_dhcp_response(sock_num, next_resend): + try: + msg_type_in = self._parse_dhcp_response() + debug_msg( + "Received message type {}".format(msg_type_in), + self._debug, + ) + return msg_type_in + except ValueError as error: + debug_msg(error, self._debug) + if not self._blocking or self._renew: debug_msg( - "Received message type {}".format(msg_type_in), self._debug + "No message, FSM is nonblocking or renewing, exiting loop.", + self._debug, ) - return msg_type_in - except ValueError as error: - debug_msg(error, self._debug) - finally: - self._socket_release() - if not self._blocking or self._renew: - debug_msg( - "No message, FSM is nonblocking or renewing, exiting loop.", - self._debug, - ) - self._socket_release() - return 0 # Did not receive a response in a single attempt. - self._socket_release() - raise TimeoutError( - "No response from DHCP server after {} retries.".format(attempt) - ) + return 0 # Did not receive a response in a single attempt. + raise TimeoutError( + "No response from DHCP server after {} retries.".format(attempt) + ) + finally: + self._eth.socket_close(sock_num) # Close the socket whatever happens. def _dhcp_state_machine(self, *, blocking: bool = False) -> None: """ From 79668f482757248d7c5f52d8171de934657d59f3 Mon Sep 17 00:00:00 2001 From: BiffoBear <martin@martinstehens.net> Date: Wed, 21 Jun 2023 15:14:25 +0700 Subject: [PATCH 5/6] Refactor, rename methods to reflect their private status. --- adafruit_wiznet5k/adafruit_wiznet5k.py | 62 +++++++++++++------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/adafruit_wiznet5k/adafruit_wiznet5k.py b/adafruit_wiznet5k/adafruit_wiznet5k.py index 278b886..5452f82 100644 --- a/adafruit_wiznet5k/adafruit_wiznet5k.py +++ b/adafruit_wiznet5k/adafruit_wiznet5k.py @@ -156,7 +156,7 @@ _MR_RST = const(0x80) # Mode Register RST # Socket mode register _SNMR_CLOSE = const(0x00) -SNMR_TCP = const(0x21) +_SNMR_TCP = const(0x21) SNMR_UDP = const(0x02) _SNMR_IPRAW = const(0x03) _SNMR_MACRAW = const(0x04) @@ -492,7 +492,7 @@ def ifconfig( # *** Public Socket Methods *** - def socket_available(self, socket_num: int, sock_type: int = SNMR_TCP) -> int: + def socket_available(self, socket_num: int, sock_type: int = _SNMR_TCP) -> int: """ Number of bytes available to be read from the socket. @@ -514,7 +514,7 @@ def socket_available(self, socket_num: int, sock_type: int = SNMR_TCP) -> int: self._sock_num_in_range(socket_num) number_of_bytes = self._get_rx_rcv_size(socket_num) - if self.read_snsr(socket_num) == SNMR_UDP: + if self._read_snsr(socket_num) == SNMR_UDP: number_of_bytes -= 8 # Subtract UDP header from packet size. if number_of_bytes < 0: raise ValueError("Negative number of bytes found on socket.") @@ -533,14 +533,14 @@ def socket_status(self, socket_num: int) -> int: :return int: The connection status. """ - return self.read_snsr(socket_num) + return self._read_snsr(socket_num) def socket_connect( self, socket_num: int, dest: IpAddress4Raw, port: int, - conn_mode: int = SNMR_TCP, + conn_mode: int = _SNMR_TCP, ) -> int: """ Open and verify a connection from a socket to a destination IPv4 address @@ -567,11 +567,11 @@ def socket_connect( # initialize a socket and set the mode self.socket_open(socket_num, conn_mode=conn_mode) # set socket destination IP and port - self.write_sndipr(socket_num, dest) - self.write_sndport(socket_num, port) - self.write_sncr(socket_num, _CMD_SOCK_CONNECT) + self._write_sndipr(socket_num, dest) + self._write_sndport(socket_num, port) + self._write_sncr(socket_num, _CMD_SOCK_CONNECT) - if conn_mode == SNMR_TCP: + if conn_mode == _SNMR_TCP: # wait for tcp connection establishment while self.socket_status(socket_num) != SNSR_SOCK_ESTABLISHED: time.sleep(0.001) @@ -638,7 +638,7 @@ def release_socket(self, socket_number): WIZNET5K._sockets_reserved[socket_number - 1] = False def socket_listen( - self, socket_num: int, port: int, conn_mode: int = SNMR_TCP + self, socket_num: int, port: int, conn_mode: int = _SNMR_TCP ) -> None: """ Listen on a socket's port. @@ -665,7 +665,7 @@ def socket_listen( self.socket_open(socket_num, conn_mode=conn_mode) self.src_port = 0 # Send listen command - self.write_sncr(socket_num, _CMD_SOCK_LISTEN) + self._write_sncr(socket_num, _CMD_SOCK_LISTEN) # Wait until ready status = SNSR_SOCK_CLOSED while status not in ( @@ -673,7 +673,7 @@ def socket_listen( SNSR_SOCK_ESTABLISHED, _SNSR_SOCK_UDP, ): - status = self.read_snsr(socket_num) + status = self._read_snsr(socket_num) if status == SNSR_SOCK_CLOSED: raise RuntimeError("Listening socket closed.") @@ -703,7 +703,7 @@ def socket_accept(self, socket_num: int) -> Tuple[int, Tuple[str, int]]: ) return next_socknum, (dest_ip, dest_port) - def socket_open(self, socket_num: int, conn_mode: int = SNMR_TCP) -> None: + def socket_open(self, socket_num: int, conn_mode: int = _SNMR_TCP) -> None: """ Open an IP socket. @@ -720,7 +720,7 @@ def socket_open(self, socket_num: int, conn_mode: int = SNMR_TCP) -> None: self._sock_num_in_range(socket_num) self._check_link_status() debug_msg("*** Opening socket {}".format(socket_num), self._debug) - if self.read_snsr(socket_num) not in ( + if self._read_snsr(socket_num) not in ( SNSR_SOCK_CLOSED, SNSR_SOCK_TIME_WAIT, SNSR_SOCK_FIN_WAIT, @@ -732,22 +732,22 @@ def socket_open(self, socket_num: int, conn_mode: int = SNMR_TCP) -> None: debug_msg("* Opening W5k Socket, protocol={}".format(conn_mode), self._debug) time.sleep(0.00025) - self.write_snmr(socket_num, conn_mode) + self._write_snmr(socket_num, conn_mode) self.write_snir(socket_num, 0xFF) if self.src_port > 0: # write to socket source port - self.write_sock_port(socket_num, self.src_port) + self._write_sock_port(socket_num, self.src_port) else: s_port = randint(49152, 65535) while s_port in self._src_ports_in_use: s_port = randint(49152, 65535) - self.write_sock_port(socket_num, s_port) + self._write_sock_port(socket_num, s_port) self._src_ports_in_use[socket_num] = s_port # open socket - self.write_sncr(socket_num, _CMD_SOCK_OPEN) - if self.read_snsr(socket_num) not in [_SNSR_SOCK_INIT, _SNSR_SOCK_UDP]: + self._write_sncr(socket_num, _CMD_SOCK_OPEN) + if self._read_snsr(socket_num) not in [_SNSR_SOCK_INIT, _SNSR_SOCK_UDP]: raise RuntimeError("Could not open socket in TCP or UDP mode.") def socket_close(self, socket_num: int) -> None: @@ -760,14 +760,14 @@ def socket_close(self, socket_num: int) -> None: """ debug_msg("*** Closing socket {}".format(socket_num), self._debug) self._sock_num_in_range(socket_num) - self.write_sncr(socket_num, _CMD_SOCK_CLOSE) + self._write_sncr(socket_num, _CMD_SOCK_CLOSE) debug_msg(" Waiting for socket to close…", self._debug) timeout = time.monotonic() + 5.0 - while self.read_snsr(socket_num) != SNSR_SOCK_CLOSED: + while self._read_snsr(socket_num) != SNSR_SOCK_CLOSED: if time.monotonic() > timeout: raise RuntimeError( "Wiznet5k failed to close socket, status = {}.".format( - self.read_snsr(socket_num) + self._read_snsr(socket_num) ) ) time.sleep(0.0001) @@ -783,7 +783,7 @@ def socket_disconnect(self, socket_num: int) -> None: """ debug_msg("*** Disconnecting socket {}".format(socket_num), self._debug) self._sock_num_in_range(socket_num) - self.write_sncr(socket_num, _CMD_SOCK_DISCON) + self._write_sncr(socket_num, _CMD_SOCK_DISCON) def socket_read(self, socket_num: int, length: int) -> Tuple[int, bytes]: """ @@ -819,7 +819,7 @@ def socket_read(self, socket_num: int, length: int) -> Tuple[int, bytes]: # After reading the received data, update Sn_RX_RD register. pointer = (pointer + bytes_on_socket) & 0xFFFF self._write_snrx_rd(socket_num, pointer) - self.write_sncr(socket_num, _CMD_SOCK_RECV) + self._write_sncr(socket_num, _CMD_SOCK_RECV) else: # no data on socket if self._read_snmr(socket_num) in ( @@ -906,7 +906,7 @@ def socket_write( # update sn_tx_wr to the value + data size pointer = (pointer + bytes_to_write) & 0xFFFF self._write_sntx_wr(socket_num, pointer) - self.write_sncr(socket_num, _CMD_SOCK_SEND) + self._write_sncr(socket_num, _CMD_SOCK_SEND) # check data was transferred correctly while not self.read_snir(socket_num) & _SNIR_SEND_OK: @@ -1175,18 +1175,18 @@ def _read_sndipr(self, sock) -> bytes: ) return bytes(data) - def write_sndipr(self, sock: int, ip_addr: bytes) -> None: + def _write_sndipr(self, sock: int, ip_addr: bytes) -> None: """Write to socket destination IP Address.""" for offset, value in enumerate(ip_addr): self._write_socket_register( sock, _REG_SNDIPR[self._chip_type] + offset, value ) - def write_sndport(self, sock: int, port: int) -> None: + def _write_sndport(self, sock: int, port: int) -> None: """Write to socket destination port.""" self._write_two_byte_sock_reg(sock, _REG_SNDPORT[self._chip_type], port) - def read_snsr(self, sock: int) -> int: + def _read_snsr(self, sock: int) -> int: """Read Socket n Status Register.""" return self._read_socket_register(sock, _REG_SNSR[self._chip_type]) @@ -1202,15 +1202,15 @@ def _read_snmr(self, sock: int) -> int: """Read the socket MR register.""" return self._read_socket_register(sock, _REG_SNMR) - def write_snmr(self, sock: int, protocol: int) -> None: + def _write_snmr(self, sock: int, protocol: int) -> None: """Write to Socket n Mode Register.""" self._write_socket_register(sock, _REG_SNMR, protocol) - def write_sock_port(self, sock: int, port: int) -> None: + def _write_sock_port(self, sock: int, port: int) -> None: """Write to the socket port number.""" self._write_two_byte_sock_reg(sock, _REG_SNPORT[self._chip_type], port) - def write_sncr(self, sock: int, data: int) -> None: + def _write_sncr(self, sock: int, data: int) -> None: """Write to socket command register.""" self._write_socket_register(sock, _REG_SNCR[self._chip_type], data) # Wait for command to complete before continuing. From 91d1f31054c656308e689bb3eee0bf9c823256f6 Mon Sep 17 00:00:00 2001 From: BiffoBear <martin@martinstehens.net> Date: Wed, 21 Jun 2023 16:13:58 +0700 Subject: [PATCH 6/6] Add _read_socket_reservations and _read_sndport methods to aid trouble shooting. --- adafruit_wiznet5k/adafruit_wiznet5k.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/adafruit_wiznet5k/adafruit_wiznet5k.py b/adafruit_wiznet5k/adafruit_wiznet5k.py index 5452f82..692bc57 100644 --- a/adafruit_wiznet5k/adafruit_wiznet5k.py +++ b/adafruit_wiznet5k/adafruit_wiznet5k.py @@ -1057,6 +1057,11 @@ def _check_link_status(self): if not self.link_status: raise ConnectionError("The Ethernet connection is down.") + @staticmethod + def _read_socket_reservations() -> list[int]: + """Return the list of reserved sockets.""" + return WIZNET5K._sockets_reserved + def _read_mr(self) -> int: """Read from the Mode Register (MR).""" return int.from_bytes(self._read(_REG_MR[self._chip_type], 0x00), "big") @@ -1182,6 +1187,10 @@ def _write_sndipr(self, sock: int, ip_addr: bytes) -> None: sock, _REG_SNDIPR[self._chip_type] + offset, value ) + def _read_sndport(self, sock: int) -> int: + """Read socket destination port.""" + return self._read_two_byte_sock_reg(sock, _REG_SNDPORT[self._chip_type]) + def _write_sndport(self, sock: int, port: int) -> None: """Write to socket destination port.""" self._write_two_byte_sock_reg(sock, _REG_SNDPORT[self._chip_type], port)