|
45 | 45 |
|
46 | 46 | from random import randint
|
47 | 47 | import time
|
| 48 | +import gc |
48 | 49 | from micropython import const
|
49 | 50 |
|
50 | 51 | from adafruit_bus_device.spi_device import SPIDevice
|
51 |
| -from adafruit_wiznet5k.adafruit_wiznet5k_debug import debug_msg |
52 | 52 | import adafruit_wiznet5k.adafruit_wiznet5k_dhcp as dhcp
|
53 | 53 | import adafruit_wiznet5k.adafruit_wiznet5k_dns as dns
|
| 54 | +from adafruit_wiznet5k.adafruit_wiznet5k_debug import debug_msg |
54 | 55 |
|
55 | 56 | # Wiznet5k Registers
|
56 | 57 | _REG_MR = const(0x0000) # Mode
|
|
110 | 111 |
|
111 | 112 | # Socket n Interrupt Register
|
112 | 113 | _SNIR_SEND_OK = const(0x10)
|
113 |
| -_SNIR_TIMEOUT = const(0x08) |
| 114 | +SNIR_TIMEOUT = const(0x08) |
114 | 115 | _SNIR_RECV = const(0x04)
|
115 |
| -_SNIR_DISCON = const(0x02) |
| 116 | +SNIR_DISCON = const(0x02) |
116 | 117 | _SNIR_CON = const(0x01)
|
117 | 118 |
|
118 | 119 | _CH_SIZE = const(0x100)
|
@@ -149,6 +150,8 @@ class WIZNET5K: # pylint: disable=too-many-public-methods, too-many-instance-at
|
149 | 150 | _UDP_MODE = const(0x02)
|
150 | 151 | _TLS_MODE = const(0x03) # This is NOT currently implemented
|
151 | 152 |
|
| 153 | + _sockets_reserved = [] |
| 154 | + |
152 | 155 | # pylint: disable=too-many-arguments
|
153 | 156 | def __init__(
|
154 | 157 | self,
|
@@ -192,6 +195,13 @@ def __init__(
|
192 | 195 | self._ch_base_msb = 0
|
193 | 196 | if self._w5xxx_init() != 1:
|
194 | 197 | 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 | + |
195 | 205 | # Set MAC address
|
196 | 206 | self.mac_address = mac
|
197 | 207 | self.src_port = 0
|
@@ -719,23 +729,61 @@ def _send_socket_cmd(self, socket: int, cmd: int) -> None:
|
719 | 729 | while self.read_sncr(socket) != b"\x00":
|
720 | 730 | debug_msg("waiting for SNCR to clear...", self._debug)
|
721 | 731 |
|
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. |
724 | 744 |
|
725 |
| - Cycle through the sockets to find the first available one, if any. |
| 745 | + :param bool reserve_socket: Whether to reserve the socket. |
726 | 746 |
|
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. |
728 | 750 | """
|
729 |
| - debug_msg("get_socket", self._debug) |
| 751 | + debug_msg("*** Get socket.", self._debug) |
| 752 | + # Prefer socket zero for none reserved calls as it cannot be reserved. |
| 753 | + if not reserve_socket and self.socket_status(0)[0] == SNSR_SOCK_CLOSED: |
| 754 | + debug_msg("Allocated socket # 0", self._debug) |
| 755 | + return 0 |
| 756 | + # Then check the other sockets. |
730 | 757 |
|
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 |
| 758 | + # Call garbage collection to encourage socket.__del__() be called to on any |
| 759 | + # destroyed instances. Not at all guaranteed to work! |
| 760 | + gc.collect() |
| 761 | + debug_msg( |
| 762 | + "Reserved sockets: {}".format(WIZNET5K._sockets_reserved), self._debug |
| 763 | + ) |
| 764 | + |
| 765 | + for socket_number, reserved in enumerate(WIZNET5K._sockets_reserved, start=1): |
| 766 | + if ( |
| 767 | + not reserved |
| 768 | + and self.socket_status(socket_number)[0] == SNSR_SOCK_CLOSED |
| 769 | + ): |
| 770 | + if reserve_socket: |
| 771 | + WIZNET5K._sockets_reserved[socket_number - 1] = True |
| 772 | + debug_msg( |
| 773 | + "Allocated socket # {}.".format(socket_number), |
| 774 | + self._debug, |
| 775 | + ) |
| 776 | + return socket_number |
| 777 | + raise RuntimeError("Out of sockets.") |
| 778 | + |
| 779 | + @staticmethod |
| 780 | + def release_socket(socket_number): |
| 781 | + """ |
| 782 | + Update the socket reservation list when a socket is no longer reserved. |
| 783 | +
|
| 784 | + :param int socket_number: The socket to release. |
| 785 | + """ |
| 786 | + WIZNET5K._sockets_reserved[socket_number - 1] = False |
739 | 787 |
|
740 | 788 | def socket_listen(
|
741 | 789 | self, socket_num: int, port: int, conn_mode: int = _SNMR_TCP
|
@@ -831,7 +879,7 @@ def socket_open(self, socket_num: int, conn_mode: int = _SNMR_TCP) -> int:
|
831 | 879 | time.sleep(0.00025)
|
832 | 880 |
|
833 | 881 | self.write_snmr(socket_num, conn_mode)
|
834 |
| - self._write_snir(socket_num, 0xFF) |
| 882 | + self.write_snir(socket_num, 0xFF) |
835 | 883 |
|
836 | 884 | if self.src_port > 0:
|
837 | 885 | # write to socket source port
|
@@ -893,7 +941,6 @@ def socket_read(self, socket_num: int, length: int) -> Tuple[int, bytes]:
|
893 | 941 | raise ValueError("Provided socket exceeds max_sockets.")
|
894 | 942 |
|
895 | 943 | # Check if there is data available on the socket
|
896 |
| - resp = b"" |
897 | 944 | ret = self._get_rx_rcv_size(socket_num)
|
898 | 945 | debug_msg("Bytes avail. on sock: {}".format(ret), self._debug)
|
899 | 946 | if ret == 0:
|
@@ -980,10 +1027,8 @@ def socket_write(
|
980 | 1027 | # pylint: disable=too-many-branches
|
981 | 1028 | if not self.link_status:
|
982 | 1029 | raise ConnectionError("Ethernet cable disconnected!")
|
983 |
| - assert socket_num <= self.max_sockets, "Provided socket exceeds max_sockets." |
984 |
| - status = 0 |
985 |
| - ret = 0 |
986 |
| - free_size = 0 |
| 1030 | + if socket_num > self.max_sockets: |
| 1031 | + raise ValueError("Provided socket exceeds max_sockets.") |
987 | 1032 | if len(buffer) > _SOCK_SIZE:
|
988 | 1033 | ret = _SOCK_SIZE
|
989 | 1034 | else:
|
@@ -1045,12 +1090,12 @@ def socket_write(
|
1045 | 1090 | raise RuntimeError("Socket closed before data was sent.")
|
1046 | 1091 | if timeout and time.monotonic() - stamp > timeout:
|
1047 | 1092 | raise RuntimeError("Operation timed out. No data sent.")
|
1048 |
| - if self.read_snir(socket_num)[0] & _SNIR_TIMEOUT: |
| 1093 | + if self.read_snir(socket_num)[0] & SNIR_TIMEOUT: |
1049 | 1094 | raise TimeoutError(
|
1050 | 1095 | "Hardware timeout while sending on socket {}.".format(socket_num)
|
1051 | 1096 | )
|
1052 | 1097 | time.sleep(0.001)
|
1053 |
| - self._write_snir(socket_num, _SNIR_SEND_OK) |
| 1098 | + self.write_snir(socket_num, _SNIR_SEND_OK) |
1054 | 1099 | return ret
|
1055 | 1100 |
|
1056 | 1101 | # Socket-Register Methods
|
@@ -1137,7 +1182,7 @@ def write_snmr(self, sock: int, protocol: int) -> None:
|
1137 | 1182 | """Write to Socket n Mode Register."""
|
1138 | 1183 | self._write_socket(sock, _REG_SNMR, protocol)
|
1139 | 1184 |
|
1140 |
| - def _write_snir(self, sock: int, data: int) -> None: |
| 1185 | + def write_snir(self, sock: int, data: int) -> None: |
1141 | 1186 | """Write to Socket n Interrupt Register."""
|
1142 | 1187 | self._write_socket(sock, _REG_SNIR, data)
|
1143 | 1188 |
|
|
0 commit comments