Welcome to mirror list, hosted at ThFree Co, Russian Federation.

Socket.cpp « DuetEthernet « DuetNG « src - github.com/Duet3D/RepRapFirmware.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 9624c56eb9192a44046ae353132a5407b9cb0e54 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
/*
 * Socket.cpp
 *
 *  Created on: 25 Dec 2016
 *      Author: David
 */

#include "Socket.h"

#include "NetworkTransaction.h"
#include "NetworkBuffer.h"
#include "socketlib.h"

//***************************************************************************************************
// Socket class

Socket::Socket() : currentTransaction(nullptr), receivedData(nullptr)
{
}

// Initialise a TCP socket
void Socket::Init(SocketNumber skt, Port serverPort)
{
	socketNum = skt;
	localPort = serverPort;
	ReInit();
}

void Socket::ReInit()
{
	// Discard any received data
	while (receivedData != nullptr)
	{
		receivedData = receivedData->Release();
	}
	ReleaseTransaction();

	persistConnection = true;
	isTerminated = false;
	isSending = false;
	state = SocketState::inactive;

	// Re-initialise the socket on the W5500
//debugPrintf("About to initialise socket %u\n", socketNum);
	socket(socketNum, Sn_MR_TCP, localPort, 0x00);
//debugPrintf("Initialised socket %u\n", socketNum);
}

// Close a connection when the last packet has been sent
void Socket::Close()
{
	disconnectNoWait(socketNum);
}

// Release the current transaction
void Socket::ReleaseTransaction()
{
	if (currentTransaction != nullptr)
	{
		currentTransaction->Release();
		currentTransaction = nullptr;
	}
}

// Terminate a connection immediately
void Socket::Terminate()
{
	disconnectNoWait(socketNum);
	isTerminated = true;
	state = SocketState::inactive;
	while (receivedData != nullptr)
	{
		receivedData = receivedData->Release();
	}
	ReleaseTransaction();
}

// Test whether we have a connection on this socket
bool Socket::IsConnected() const
{
	const uint8_t stat = getSn_SR(socketNum);
	return stat == SOCK_ESTABLISHED || stat == SOCK_CLOSE_WAIT;
}

// Return true if there is or may soon be more data to read
bool Socket::HasMoreDataToRead() const
{
	return (receivedData != nullptr && receivedData->TotalRemaining() != 0)		// already have more data
		|| getSn_SR(socketNum) == SOCK_ESTABLISHED;								// still fully connected, so we may receive more
}

bool Socket::CanWrite() const
{
	const uint8_t stat = getSn_SR(socketNum);
	return stat == SOCK_ESTABLISHED || stat == SOCK_CLOSE_WAIT;
}

// Return true if we are in the sending phase
bool Socket::IsSending() const
{
	return currentTransaction != nullptr && currentTransaction->IsSending();
}

// Read 1 character from the receive buffers, returning true if successful
bool Socket::ReadChar(char& c)
{
	while (receivedData != nullptr && receivedData->IsEmpty())
	{
		receivedData = receivedData->Release();		// discard empty buffer at head of chain
	}

	if (receivedData == nullptr)
	{
		c = 0;
		return false;
	}

	bool ret = receivedData->ReadChar(c);
	if (receivedData->IsEmpty())
	{
		receivedData = receivedData->Release();
	}
	return ret;
}

// Return a pointer to data in a buffer and a length available, and mark the data as taken
bool Socket::ReadBuffer(const char *&buffer, size_t &len)
{
	while (receivedData != nullptr && receivedData->IsEmpty())
	{
		receivedData = receivedData->Release();		// discard empty buffer at head of chain
	}

	if (receivedData == nullptr)
	{
		return false;
	}

	len = 2048;										// initial value passed to TakeData is the maximum amount we will take
	buffer = reinterpret_cast<const char*>(receivedData->TakeData(len));
//	debugPrintf("Taking %d bytes\n", len);
	return true;
}

// Poll a socket to see if it needs to be serviced
void Socket::Poll(bool full)
{
	switch(getSn_SR(socketNum))
	{
	case SOCK_INIT:
		// Socket has been initialised but is not listening yet
		if (localPort != 0)			// localPort for the FTP data socket is 0 until we have decided what port number to use
		{
			ExecCommand(socketNum, Sn_CR_LISTEN);
		}
		break;

	case SOCK_LISTEN:				// Socket is listening but no client has connected to it yet
		break;

	case SOCK_ESTABLISHED:			// A client is connected to this socket
		if (getSn_IR(socketNum) & Sn_IR_CON)
		{
			// New connection, so retrieve the sending IP address and port, and clear the interrupt
			getSn_DIPR(socketNum, reinterpret_cast<uint8_t*>(&remoteIPAddress));
			remotePort = getSn_DPORT(socketNum);
			setSn_IR(socketNum, Sn_IR_CON);
		}

		// See if the socket has received any data
		{
			const uint16_t len = getSn_RX_RSR(socketNum);
			if (len != 0)
			{
//				debugPrintf("%u available\n", len);
				// There is data available, so allocate a buffer
				NetworkBuffer * const buf = NetworkBuffer::Allocate();
				if (buf != nullptr)
				{
					const int32_t ret = recv(socketNum, buf->Data(), min<size_t>(len, NetworkBuffer::bufferSize));
//					debugPrintf("ret %d\n", ret);
					if (ret > 0)
					{
						buf->dataLength = (size_t)ret;
						buf->readPointer = 0;
						NetworkBuffer::AppendToList(&receivedData, buf);
					}
					else
					{
						buf->Release();
//						debugPrintf("Bad receive, code = %d\n", ret);
					}
				}
				else debugPrintf("no buffer\n");
			}

			if (receivedData != nullptr && currentTransaction == nullptr)
			{
				currentTransaction = NetworkTransaction::Allocate();
				if (currentTransaction != nullptr)
				{
					currentTransaction->Set(this, TransactionStatus::receiving);
				}
			}
		}

		// See if we can send any data.
		// Currently we don't send if we are being called from hsmci because we don't want to risk releasing a buffer that we may be reading data into.
		// We could use a buffer locking mechanism instead. However, the speed of sending is not critical, so we don't do that yet.
		if (full && IsSending() && TrySendData())
		{
			ReleaseTransaction();
			ExecCommand(socketNum, Sn_CR_DISCON);
		}
		break;

	case SOCK_CLOSE_WAIT:			// A client has asked to disconnect
#ifdef _HTTPSERVER_DEBUG_
		printf("> HTTPSocket[%d] : ClOSE_WAIT\r\n", socketNum);
#endif
		if (!IsSending() || TrySendData())
		{
			ReleaseTransaction();
			ExecCommand(socketNum, Sn_CR_DISCON);
		}
		break;

	case SOCK_CLOSED:
#ifdef _HTTPSERVER_DEBUG_
		printf("> HTTPSocket[%d] : CLOSED\r\n", s);
#endif
		if (socket(socketNum, Sn_MR_TCP, localPort, 0x00) == socketNum)    // Reinitialize the socket
		{
#ifdef _HTTPSERVER_DEBUG_
			printf("> HTTPSocket[%d] : OPEN\r\n", socketNum);
#endif
		}
		break;

	default:
		break;
	} // end of switch

#ifdef _USE_WATCHDOG_
	HTTPServer_WDT_Reset();
#endif
}

// Try to send data, returning true if all data has been sent and we ought to close the socket
// We have already checked that the socket is in the ESTABLISHED or CLOSE_WAIT state.
bool Socket::TrySendData()
{
	if (isSending)									// are we already sending?
	{
		const uint8_t tmp = getSn_IR(socketNum);
		if (tmp & Sn_IR_SENDOK)						// did the previous send complete?
		{
			setSn_IR(socketNum, Sn_IR_SENDOK);		// if yes
			isSending = false;
		}
		else if(tmp & Sn_IR_TIMEOUT)				// did it time out?
		{
			isSending = false;
			disconnectNoWait(socketNum);			// if so, close the socket
			return true;							// and release buffers etc.
		}
		else
		{
			//debugPrintf("Waiting for data to be sent\n");
			return false;							// last send is still in progress
		}
	}

	// Socket is free to send
	if (currentTransaction->GetStatus() == TransactionStatus::finished)
	{
		return true;
	}

	size_t freesize = (size_t)getSn_TX_FSR(socketNum);
	uint16_t ptr = getSn_TX_WR(socketNum);
	bool sent = false;
	while (freesize != 0)
	{
		size_t length = freesize;
		const uint8_t *data = currentTransaction->GetDataToSend(length);
		if (data == nullptr)
		{
			break;									// no more data or can't allocate a file buffer
		}
		//debugPrintf("rp=%04x tp=%04x\n", getSn_TX_RD(socketNum), getSn_TX_WR(socketNum));
		wiz_send_data_at(socketNum, data, length, ptr);
		//debugPrintf("Wrote %u bytes of %u free, %02x %02x %02x\n", length, freesize, data[0], data[1], data[2]);
		freesize -= length;
		ptr += (uint16_t)length;
		sent = true;
	}
	if (sent)
	{
		//debugPrintf("Sending data, rp=%04x tp=%04x\n", getSn_TX_RD(socketNum), getSn_TX_WR(socketNum));
		ExecCommand(socketNum, Sn_CR_SEND);
		isSending = true;
	}
	else if (currentTransaction->GetStatus() == TransactionStatus::finished)
	{
		return true;								// there was no more data to send
	}

	return false;
}

// Discard any received data for this transaction
void Socket::DiscardReceivedData()
{
	while (receivedData != nullptr)
	{
		receivedData = receivedData->Release();
	}
}

// End