76 |
err_t |
err_t |
77 |
tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t copy) |
tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t copy) |
78 |
{ |
{ |
79 |
|
DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_write(pcb=%p, arg=%p, len=%u, copy=%d)\n", pcb, arg, len, copy)); |
80 |
if(pcb->state == SYN_SENT || |
if(pcb->state == SYN_SENT || |
81 |
pcb->state == SYN_RCVD || |
pcb->state == SYN_RCVD || |
82 |
pcb->state == ESTABLISHED || |
pcb->state == ESTABLISHED || |
86 |
} |
} |
87 |
return ERR_OK; |
return ERR_OK; |
88 |
} else { |
} else { |
89 |
|
DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_write() called in invalid state\n")); |
90 |
return ERR_CONN; |
return ERR_CONN; |
91 |
} |
} |
92 |
} |
} |
103 |
void *ptr; |
void *ptr; |
104 |
u8_t queuelen; |
u8_t queuelen; |
105 |
|
|
106 |
|
DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_enqueue(pcb=%p, arg=%p, len=%u, flags=%x, copy=%d)\n", pcb, arg, len, flags, copy)); |
107 |
left = len; |
left = len; |
108 |
ptr = arg; |
ptr = arg; |
109 |
/* fail on too much data */ |
/* fail on too much data */ |
113 |
} |
} |
114 |
|
|
115 |
/* seqno will be the sequence number of the first segment enqueued |
/* seqno will be the sequence number of the first segment enqueued |
116 |
by the call to this function. */ |
by the call to this function. */ |
117 |
seqno = pcb->snd_lbb; |
seqno = pcb->snd_lbb; |
118 |
|
|
119 |
queue = NULL; |
queue = NULL; |
120 |
DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue: %d\n", pcb->snd_queuelen)); |
DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue: queuelen: %d\n", pcb->snd_queuelen)); |
121 |
|
|
122 |
/* Check if the queue length exceeds the configured maximum queue |
/* Check if the queue length exceeds the configured maximum queue |
123 |
length. If so, we return an error. */ |
length. If so, we return an error. */ |
124 |
queuelen = pcb->snd_queuelen; |
queuelen = pcb->snd_queuelen; |
125 |
if(queuelen >= TCP_SND_QUEUELEN) { |
if(queuelen >= TCP_SND_QUEUELEN) { |
126 |
DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_enqueue: too long queue %d (max %d)\n", queuelen, TCP_SND_QUEUELEN)); |
DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_enqueue: too long queue %d (max %d)\n", queuelen, TCP_SND_QUEUELEN)); |
127 |
goto memerr; |
goto memerr; |
128 |
} |
} |
129 |
|
|
130 |
#ifdef LWIP_DEBUG |
#ifdef LWIP_DEBUG |
131 |
if(pcb->snd_queuelen != 0) { |
if(pcb->snd_queuelen != 0) { |
132 |
ASSERT("tcp_enqueue: valid queue length", pcb->unacked != NULL || |
ASSERT("tcp_enqueue: valid queue length", pcb->unacked != NULL || |
133 |
pcb->unsent != NULL); |
pcb->unsent != NULL); |
134 |
} |
} |
135 |
#endif /* LWIP_DEBUG */ |
#endif /* LWIP_DEBUG */ |
136 |
|
|
137 |
seg = NULL; |
seg = NULL; |
138 |
seglen = 0; |
seglen = 0; |
139 |
|
|
140 |
/* First, break up the data into segments and tuck them together in |
/* First, break up the data into segments and tuck them together in |
141 |
the local "queue" variable. */ |
the local "queue" variable. */ |
142 |
while(queue == NULL || left > 0) { |
while(queue == NULL || left > 0) { |
143 |
|
|
144 |
/* The segment length should be the MSS if the data to be enqueued |
/* The segment length should be the MSS if the data to be enqueued |
145 |
is larger than the MSS. */ |
is larger than the MSS. */ |
146 |
seglen = left > pcb->mss? pcb->mss: left; |
seglen = left > pcb->mss? pcb->mss: left; |
147 |
|
|
148 |
/* Allocate memory for tcp_seg, and fill in fields. */ |
/* Allocate memory for tcp_seg, and fill in fields. */ |
149 |
seg = memp_malloc(MEMP_TCP_SEG); |
seg = memp_malloc(MEMP_TCP_SEG); |
150 |
if(seg == NULL) { |
if(seg == NULL) { |
153 |
} |
} |
154 |
seg->next = NULL; |
seg->next = NULL; |
155 |
seg->p = NULL; |
seg->p = NULL; |
156 |
|
|
157 |
if(queue == NULL) { |
if(queue == NULL) { |
158 |
queue = seg; |
queue = seg; |
159 |
} else { |
} |
160 |
|
else { |
161 |
/* Attach the segment to the end of the queued segments. */ |
/* Attach the segment to the end of the queued segments. */ |
162 |
for(useg = queue; useg->next != NULL; useg = useg->next); |
for(useg = queue; useg->next != NULL; useg = useg->next); |
163 |
useg->next = seg; |
useg->next = seg; |
164 |
} |
} |
165 |
|
|
166 |
/* If copy is set, memory should be allocated |
/* If copy is set, memory should be allocated |
167 |
and data copied into pbuf, otherwise data comes from |
and data copied into pbuf, otherwise data comes from |
168 |
ROM or other static memory, and need not be copied. If |
ROM or other static memory, and need not be copied. If |
169 |
optdata is != NULL, we have options instead of data. */ |
optdata is != NULL, we have options instead of data. */ |
170 |
if(optdata != NULL) { |
if(optdata != NULL) { |
171 |
if((seg->p = pbuf_alloc(PBUF_TRANSPORT, optlen, PBUF_RAM)) == NULL) { |
if((seg->p = pbuf_alloc(PBUF_TRANSPORT, optlen, PBUF_RAM)) == NULL) { |
172 |
goto memerr; |
goto memerr; |
173 |
} |
} |
174 |
++queuelen; |
++queuelen; |
175 |
seg->dataptr = seg->p->payload; |
seg->dataptr = seg->p->payload; |
176 |
} else if(copy) { |
} |
177 |
|
else if(copy) { |
178 |
if((seg->p = pbuf_alloc(PBUF_TRANSPORT, seglen, PBUF_RAM)) == NULL) { |
if((seg->p = pbuf_alloc(PBUF_TRANSPORT, seglen, PBUF_RAM)) == NULL) { |
179 |
DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_enqueue: could not allocate memory for pbuf copy\n")); |
DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_enqueue: could not allocate memory for pbuf copy size %u\n", seglen)); |
180 |
goto memerr; |
goto memerr; |
181 |
} |
} |
182 |
++queuelen; |
++queuelen; |
183 |
if(arg != NULL) { |
if(arg != NULL) { |
184 |
memcpy(seg->p->payload, ptr, seglen); |
memcpy(seg->p->payload, ptr, seglen); |
185 |
} |
} |
186 |
seg->dataptr = seg->p->payload; |
seg->dataptr = seg->p->payload; |
187 |
} else { |
} |
188 |
|
else { |
189 |
/* Do not copy the data. */ |
/* Do not copy the data. */ |
190 |
|
|
191 |
/* First, allocate a pbuf for holding the data. */ |
/* First, allocate a pbuf for holding the data. */ |
192 |
if((p = pbuf_alloc(PBUF_TRANSPORT, seglen, PBUF_ROM)) == NULL) { |
if((p = pbuf_alloc(PBUF_TRANSPORT, seglen, PBUF_ROM)) == NULL) { |
193 |
DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_enqueue: could not allocate memory for pbuf non-copy\n")); |
DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_enqueue: could not allocate memory for pbuf non-copy\n")); |
194 |
goto memerr; |
goto memerr; |
195 |
} |
} |
196 |
++queuelen; |
++queuelen; |
197 |
p->payload = ptr; |
p->payload = ptr; |
199 |
|
|
200 |
/* Second, allocate a pbuf for the headers. */ |
/* Second, allocate a pbuf for the headers. */ |
201 |
if((seg->p = pbuf_alloc(PBUF_TRANSPORT, 0, PBUF_RAM)) == NULL) { |
if((seg->p = pbuf_alloc(PBUF_TRANSPORT, 0, PBUF_RAM)) == NULL) { |
202 |
/* If allocation fails, we have to deallocate the data pbuf as |
/* If allocation fails, we have to deallocate the data pbuf as |
203 |
well. */ |
well. */ |
204 |
pbuf_free(p); |
pbuf_free(p); |
205 |
DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_enqueue: could not allocate memory for header pbuf\n")); |
DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_enqueue: could not allocate memory for header pbuf\n")); |
206 |
goto memerr; |
goto memerr; |
207 |
} |
} |
208 |
++queuelen; |
++queuelen; |
209 |
|
|
212 |
} |
} |
213 |
|
|
214 |
/* Now that there are more segments queued, we check again if the |
/* Now that there are more segments queued, we check again if the |
215 |
length of the queue exceeds the configured maximum. */ |
length of the queue exceeds the configured maximum. */ |
216 |
if(queuelen > TCP_SND_QUEUELEN) { |
if(queuelen > TCP_SND_QUEUELEN) { |
217 |
DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_enqueue: queue too long %d (%d)\n", queuelen, TCP_SND_QUEUELEN)); |
DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_enqueue: queue too long %d (%d)\n", queuelen, TCP_SND_QUEUELEN)); |
218 |
goto memerr; |
goto memerr; |
219 |
} |
} |
220 |
|
|
221 |
seg->len = seglen; |
seg->len = seglen; |
222 |
/* if((flags & TCP_SYN) || (flags & TCP_FIN)) { |
/* if((flags & TCP_SYN) || (flags & TCP_FIN)) { |
223 |
++seg->len; |
++seg->len; |
224 |
}*/ |
}*/ |
225 |
|
|
226 |
/* Build TCP header. */ |
/* Build TCP header. */ |
227 |
if(pbuf_header(seg->p, TCP_HLEN)) { |
if(pbuf_header(seg->p, TCP_HLEN)) { |
228 |
|
|
229 |
DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_enqueue: no room for TCP header in pbuf.\n")); |
DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_enqueue: no room for TCP header in pbuf.\n")); |
230 |
|
|
231 |
#ifdef TCP_STATS |
#ifdef TCP_STATS |
232 |
++lwip_stats.tcp.err; |
++lwip_stats.tcp.err; |
233 |
#endif /* TCP_STATS */ |
#endif /* TCP_STATS */ |
244 |
/* Copy the options into the header, if they are present. */ |
/* Copy the options into the header, if they are present. */ |
245 |
if(optdata == NULL) { |
if(optdata == NULL) { |
246 |
TCPH_OFFSET_SET(seg->tcphdr, 5 << 4); |
TCPH_OFFSET_SET(seg->tcphdr, 5 << 4); |
247 |
} else { |
} |
248 |
|
else { |
249 |
TCPH_OFFSET_SET(seg->tcphdr, (5 + optlen / 4) << 4); |
TCPH_OFFSET_SET(seg->tcphdr, (5 + optlen / 4) << 4); |
250 |
/* Copy options into data portion of segment. |
/* Copy options into data portion of segment. |
251 |
Options can thus only be sent in non data carrying |
Options can thus only be sent in non data carrying |
252 |
segments such as SYN|ACK. */ |
segments such as SYN|ACK. */ |
253 |
memcpy(seg->dataptr, optdata, optlen); |
memcpy(seg->dataptr, optdata, optlen); |
254 |
} |
} |
255 |
DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_enqueue: queueing %lu:%lu (0x%x)\n", |
DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_enqueue: queueing %lu:%lu (0x%x)\n", |
256 |
ntohl(seg->tcphdr->seqno), |
ntohl(seg->tcphdr->seqno), |
257 |
ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg), |
ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg), |
258 |
flags)); |
flags)); |
259 |
|
|
260 |
left -= seglen; |
left -= seglen; |
261 |
seqno += seglen; |
seqno += seglen; |
262 |
ptr = (void *)((char *)ptr + seglen); |
ptr = (void *)((char *)ptr + seglen); |
263 |
} |
} |
264 |
|
|
265 |
|
|
266 |
/* Now that the data to be enqueued has been broken up into TCP |
/* Now that the data to be enqueued has been broken up into TCP |
267 |
segments in the queue variable, we add them to the end of the |
segments in the queue variable, we add them to the end of the |
268 |
pcb->unsent queue. */ |
pcb->unsent queue. */ |
269 |
if(pcb->unsent == NULL) { |
if(pcb->unsent == NULL) { |
270 |
useg = NULL; |
useg = NULL; |
271 |
} else { |
} |
272 |
|
else { |
273 |
for(useg = pcb->unsent; useg->next != NULL; useg = useg->next); |
for(useg = pcb->unsent; useg->next != NULL; useg = useg->next); |
274 |
} |
} |
275 |
|
|
276 |
/* If there is room in the last pbuf on the unsent queue, |
/* If there is room in the last pbuf on the unsent queue, |
277 |
chain the first pbuf on the queue together with that. */ |
chain the first pbuf on the queue together with that. */ |
278 |
if(useg != NULL && |
if(useg != NULL && |
279 |
TCP_TCPLEN(useg) != 0 && |
TCP_TCPLEN(useg) != 0 && |
280 |
!(TCPH_FLAGS(useg->tcphdr) & (TCP_SYN | TCP_FIN)) && |
!(TCPH_FLAGS(useg->tcphdr) & (TCP_SYN | TCP_FIN)) && |
281 |
!(flags & (TCP_SYN | TCP_FIN)) && |
!(flags & (TCP_SYN | TCP_FIN)) && |
282 |
useg->len + queue->len <= pcb->mss) { |
useg->len + queue->len <= pcb->mss) { |
283 |
/* Remove TCP header from first segment. */ |
/* Remove TCP header from first segment. */ |
284 |
pbuf_header(queue->p, -TCP_HLEN); |
pbuf_header(queue->p, -TCP_HLEN); |
285 |
pbuf_chain(useg->p, queue->p); |
pbuf_chain(useg->p, queue->p); |
286 |
useg->len += queue->len; |
useg->len += queue->len; |
287 |
useg->next = queue->next; |
useg->next = queue->next; |
288 |
|
|
289 |
DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_enqueue: chaining, new len %u\n", useg->len)); |
DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_enqueue: chaining, new len %u\n", useg->len)); |
290 |
if(seg == queue) { |
if(seg == queue) { |
291 |
seg = NULL; |
seg = NULL; |
292 |
} |
} |
293 |
memp_free(MEMP_TCP_SEG, queue); |
memp_free(MEMP_TCP_SEG, queue); |
294 |
} else { |
} |
295 |
|
else { |
296 |
if(useg == NULL) { |
if(useg == NULL) { |
297 |
pcb->unsent = queue; |
pcb->unsent = queue; |
298 |
|
|
299 |
} else { |
} |
300 |
|
else { |
301 |
useg->next = queue; |
useg->next = queue; |
302 |
} |
} |
303 |
} |
} |
311 |
#ifdef LWIP_DEBUG |
#ifdef LWIP_DEBUG |
312 |
if(pcb->snd_queuelen != 0) { |
if(pcb->snd_queuelen != 0) { |
313 |
ASSERT("tcp_enqueue: valid queue length", pcb->unacked != NULL || |
ASSERT("tcp_enqueue: valid queue length", pcb->unacked != NULL || |
314 |
pcb->unsent != NULL); |
pcb->unsent != NULL); |
315 |
|
|
316 |
} |
} |
317 |
#endif /* LWIP_DEBUG */ |
#endif /* LWIP_DEBUG */ |
318 |
|
|
319 |
/* Set the PSH flag in the last segment that we enqueued, but only |
/* Set the PSH flag in the last segment that we enqueued, but only |
320 |
if the segment has data (indicated by seglen > 0). */ |
if the segment has data (indicated by seglen > 0). */ |
321 |
if(seg != NULL && seglen > 0 && seg->tcphdr != NULL) { |
if(seg != NULL && seglen > 0 && seg->tcphdr != NULL) { |
322 |
TCPH_FLAGS_SET(seg->tcphdr, TCPH_FLAGS(seg->tcphdr) | TCP_PSH); |
TCPH_FLAGS_SET(seg->tcphdr, TCPH_FLAGS(seg->tcphdr) | TCP_PSH); |
323 |
} |
} |
324 |
|
|
325 |
return ERR_OK; |
return ERR_OK; |
326 |
memerr: |
memerr: |
327 |
#ifdef TCP_STATS |
#ifdef TCP_STATS |
328 |
++lwip_stats.tcp.memerr; |
++lwip_stats.tcp.memerr; |
329 |
#endif /* TCP_STATS */ |
#endif /* TCP_STATS */ |
334 |
#ifdef LWIP_DEBUG |
#ifdef LWIP_DEBUG |
335 |
if(pcb->snd_queuelen != 0) { |
if(pcb->snd_queuelen != 0) { |
336 |
ASSERT("tcp_enqueue: valid queue length", pcb->unacked != NULL || |
ASSERT("tcp_enqueue: valid queue length", pcb->unacked != NULL || |
337 |
pcb->unsent != NULL); |
pcb->unsent != NULL); |
338 |
|
|
339 |
} |
} |
340 |
#endif /* LWIP_DEBUG */ |
#endif /* LWIP_DEBUG */ |
341 |
DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue: %d (with mem err)\n", pcb->snd_queuelen)); |
DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue: %d (with mem err)\n", pcb->snd_queuelen)); |
481 |
if(pcb->rcv_wnd < pcb->mss) { |
if(pcb->rcv_wnd < pcb->mss) { |
482 |
seg->tcphdr->wnd = 0; |
seg->tcphdr->wnd = 0; |
483 |
} else { |
} else { |
484 |
|
/* advertise our receive window size in this TCP segment */ |
485 |
seg->tcphdr->wnd = htons(pcb->rcv_wnd); |
seg->tcphdr->wnd = htons(pcb->rcv_wnd); |
486 |
} |
} |
487 |
|
|