Rev 3711 | Rev 4347 | Go to most recent revision | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed
Rev 3711 | Rev 4052 | ||
---|---|---|---|
1 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
1 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
2 | ;; ;; |
2 | ;; ;; |
3 | ;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;; |
3 | ;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;; |
4 | ;; Distributed under terms of the GNU General Public License ;; |
4 | ;; Distributed under terms of the GNU General Public License ;; |
5 | ;; ;; |
5 | ;; ;; |
6 | ;; Part of the TCP/IP network stack for KolibriOS ;; |
6 | ;; Part of the TCP/IP network stack for KolibriOS ;; |
7 | ;; ;; |
7 | ;; ;; |
8 | ;; Written by hidnplayr@kolibrios.org ;; |
8 | ;; Written by hidnplayr@kolibrios.org ;; |
9 | ;; ;; |
9 | ;; ;; |
10 | ;; Based on the code of 4.4BSD ;; |
10 | ;; Based on the code of 4.4BSD ;; |
11 | ;; ;; |
11 | ;; ;; |
12 | ;; GNU GENERAL PUBLIC LICENSE ;; |
12 | ;; GNU GENERAL PUBLIC LICENSE ;; |
13 | ;; Version 2, June 1991 ;; |
13 | ;; Version 2, June 1991 ;; |
14 | ;; ;; |
14 | ;; ;; |
15 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
15 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
16 | 16 | ||
17 | $Revision: 3289 $ |
17 | $Revision: 3289 $ |
18 | 18 | ||
19 | ;----------------------------------------------------------------- |
19 | ;----------------------------------------------------------------- |
20 | ; |
20 | ; |
21 | ; TCP_output |
21 | ; TCP_output |
22 | ; |
22 | ; |
23 | ; IN: eax = socket pointer |
23 | ; IN: eax = socket pointer |
24 | ; |
24 | ; |
25 | ; OUT: / |
25 | ; OUT: / |
26 | ; |
26 | ; |
27 | ;----------------------------------------------------------------- |
27 | ;----------------------------------------------------------------- |
28 | align 4 |
28 | align 4 |
29 | TCP_output: |
29 | TCP_output: |
30 | 30 | ||
31 | DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_output: socket=%x\n", eax |
31 | DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_output: socket=%x\n", eax |
32 | 32 | ||
33 | push eax |
33 | push eax |
34 | lea ecx, [eax + SOCKET.mutex] |
34 | lea ecx, [eax + SOCKET.mutex] |
35 | call mutex_lock |
35 | call mutex_lock |
36 | pop eax |
36 | pop eax |
37 | 37 | ||
38 | DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_output: socket locked\n" |
38 | DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_output: socket locked\n" |
39 | 39 | ||
40 | ; We'll detect the length of the data to be transmitted, and flags to be used |
40 | ; We'll detect the length of the data to be transmitted, and flags to be used |
41 | ; If there is some data, or any critical controls to send (SYN / RST), then transmit |
41 | ; If there is some data, or any critical controls to send (SYN / RST), then transmit |
42 | ; Otherwise, investigate further |
42 | ; Otherwise, investigate further |
43 | 43 | ||
44 | mov ebx, [eax + TCP_SOCKET.SND_MAX] |
44 | mov ebx, [eax + TCP_SOCKET.SND_MAX] |
45 | cmp ebx, [eax + TCP_SOCKET.SND_UNA] |
45 | cmp ebx, [eax + TCP_SOCKET.SND_UNA] |
46 | jbe .not_idle |
46 | jbe .not_idle |
47 | 47 | ||
48 | mov ebx, [eax + TCP_SOCKET.t_idle] |
48 | mov ebx, [eax + TCP_SOCKET.t_idle] |
49 | cmp ebx, [eax + TCP_SOCKET.t_rxtcur] |
49 | cmp ebx, [eax + TCP_SOCKET.t_rxtcur] |
50 | jbe .not_idle |
50 | jbe .not_idle |
51 | 51 | ||
52 | ; We have been idle for a while and no ACKS are expected to clock out any data we send.. |
52 | ; We have been idle for a while and no ACKS are expected to clock out any data we send.. |
53 | ; Slow start to get ack "clock" running again. |
53 | ; Slow start to get ack "clock" running again. |
54 | 54 | ||
55 | mov ebx, [eax + TCP_SOCKET.t_maxseg] |
55 | mov ebx, [eax + TCP_SOCKET.t_maxseg] |
56 | mov [eax + TCP_SOCKET.SND_CWND], ebx |
56 | mov [eax + TCP_SOCKET.SND_CWND], ebx |
57 | 57 | ||
58 | .not_idle: |
58 | .not_idle: |
59 | .again: |
59 | .again: |
60 | mov [eax + TCP_SOCKET.temp_bits], 0 |
60 | mov [eax + TCP_SOCKET.temp_bits], 0 |
61 | 61 | ||
62 | mov ebx, [eax + TCP_SOCKET.SND_NXT] ; calculate offset (71) |
62 | mov ebx, [eax + TCP_SOCKET.SND_NXT] ; calculate offset (71) |
63 | sub ebx, [eax + TCP_SOCKET.SND_UNA] ; |
63 | sub ebx, [eax + TCP_SOCKET.SND_UNA] ; |
64 | 64 | ||
65 | mov ecx, [eax + TCP_SOCKET.SND_WND] ; determine window |
65 | mov ecx, [eax + TCP_SOCKET.SND_WND] ; determine window |
66 | cmp ecx, [eax + TCP_SOCKET.SND_CWND] ; |
66 | cmp ecx, [eax + TCP_SOCKET.SND_CWND] ; |
67 | jb @f ; |
67 | jb @f ; |
68 | mov ecx, [eax + TCP_SOCKET.SND_CWND] ; |
68 | mov ecx, [eax + TCP_SOCKET.SND_CWND] ; |
69 | @@: ; |
69 | @@: ; |
70 | 70 | ||
71 | call TCP_outflags ; flags in dl |
71 | call TCP_outflags ; flags in dl |
72 | 72 | ||
73 | ;------------------------ |
73 | ;------------------------ |
74 | ; data being forced out ? |
74 | ; data being forced out ? |
75 | 75 | ||
76 | ; If in persist timeout with window of 0, send 1 byte. |
76 | ; If in persist timeout with window of 0, send 1 byte. |
77 | ; Otherwise, if window is small but nonzero, and timer expired, |
77 | ; Otherwise, if window is small but nonzero, and timer expired, |
78 | ; we will send what we can and go to transmit state |
78 | ; we will send what we can and go to transmit state |
79 | 79 | ||
80 | cmp [eax + TCP_SOCKET.t_force], 0 |
80 | cmp [eax + TCP_SOCKET.t_force], 0 |
81 | je .no_force |
81 | je .no_force |
82 | 82 | ||
83 | DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_output: forcing data out\n" |
83 | DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_output: forcing data out\n" |
84 | 84 | ||
85 | test ecx, ecx |
85 | test ecx, ecx |
86 | jnz .no_zero_window |
86 | jnz .no_zero_window |
87 | 87 | ||
88 | cmp ebx, [eax + STREAM_SOCKET.snd.size] |
88 | cmp ebx, [eax + STREAM_SOCKET.snd.size] |
89 | jae @f |
89 | jae @f |
90 | 90 | ||
91 | and dl, not (TH_FIN) |
91 | and dl, not (TH_FIN) |
92 | 92 | ||
93 | @@: |
93 | @@: |
94 | inc ecx |
94 | inc ecx |
95 | jmp .no_force |
95 | jmp .no_force |
96 | 96 | ||
97 | .no_zero_window: |
97 | .no_zero_window: |
98 | and [eax + TCP_SOCKET.timer_flags], not timer_flag_persist |
98 | and [eax + TCP_SOCKET.timer_flags], not timer_flag_persist |
99 | mov [eax + TCP_SOCKET.t_rxtshift], 0 |
99 | mov [eax + TCP_SOCKET.t_rxtshift], 0 |
100 | 100 | ||
101 | .no_force: |
101 | .no_force: |
102 | 102 | ||
103 | ;-------------------------------- |
103 | ;-------------------------------- |
104 | ; Calculate how much data to send (106) |
104 | ; Calculate how much data to send (106) |
105 | 105 | ||
106 | mov esi, [eax + STREAM_SOCKET.snd.size] |
106 | mov esi, [eax + STREAM_SOCKET.snd.size] |
107 | cmp esi, ecx |
107 | cmp esi, ecx |
108 | jb @f |
108 | jb @f |
109 | mov esi, ecx |
109 | mov esi, ecx |
110 | @@: |
110 | @@: |
111 | sub esi, ebx |
111 | sub esi, ebx |
112 | 112 | ||
113 | 113 | ||
114 | ;------------------------ |
114 | ;------------------------ |
115 | ; check for window shrink (107) |
115 | ; check for window shrink (107) |
116 | 116 | ||
117 | ; If FIN has been set, but not ACKed, but we havent been called to retransmit, esi will be -1 |
117 | ; If FIN has been set, but not ACKed, but we havent been called to retransmit, esi will be -1 |
118 | ; Otherwise, window shrank after we sent into it. |
118 | ; Otherwise, window shrank after we sent into it. |
119 | 119 | ||
120 | jae .not_persist |
120 | jae .not_persist |
121 | 121 | ||
122 | ; enter persist state |
122 | ; enter persist state |
123 | xor esi, esi |
123 | xor esi, esi |
124 | 124 | ||
125 | ; If window shrank to 0 |
125 | ; If window shrank to 0 |
126 | test ecx, ecx |
126 | test ecx, ecx |
127 | jnz @f |
127 | jnz @f |
128 | 128 | ||
129 | ; cancel pending retransmit |
129 | ; cancel pending retransmit |
130 | and [eax + TCP_SOCKET.timer_flags], not timer_flag_retransmission |
130 | and [eax + TCP_SOCKET.timer_flags], not timer_flag_retransmission |
131 | 131 | ||
132 | ; pull SND_NXT back to (closed) window, We will enter persist state below. |
132 | ; pull SND_NXT back to (closed) window, We will enter persist state below. |
133 | push [eax + TCP_SOCKET.SND_UNA] |
133 | push [eax + TCP_SOCKET.SND_UNA] |
134 | pop [eax + TCP_SOCKET.SND_NXT] |
134 | pop [eax + TCP_SOCKET.SND_NXT] |
135 | @@: |
135 | @@: |
136 | 136 | ||
137 | ; If window didn't close completely, just wait for an ACK |
137 | ; If window didn't close completely, just wait for an ACK |
138 | 138 | ||
139 | .not_persist: |
139 | .not_persist: |
140 | 140 | ||
141 | ;--------------------------- |
141 | ;--------------------------- |
142 | ; Send one segment at a time (124) |
142 | ; Send one segment at a time (124) |
143 | 143 | ||
144 | cmp esi, [eax + TCP_SOCKET.t_maxseg] |
144 | cmp esi, [eax + TCP_SOCKET.t_maxseg] |
145 | jbe @f |
145 | jbe @f |
146 | 146 | ||
147 | mov esi, [eax + TCP_SOCKET.t_maxseg] |
147 | mov esi, [eax + TCP_SOCKET.t_maxseg] |
148 | or [eax + TCP_SOCKET.temp_bits], TCP_BIT_SENDALOT |
148 | or [eax + TCP_SOCKET.temp_bits], TCP_BIT_SENDALOT |
149 | @@: |
149 | @@: |
150 | 150 | ||
151 | ;-------------------------------------------- |
151 | ;-------------------------------------------- |
152 | ; Turn of FIN flag if send buffer not emptied (128) |
152 | ; Turn of FIN flag if send buffer not emptied (128) |
153 | 153 | ||
154 | mov edi, [eax + TCP_SOCKET.SND_NXT] |
154 | mov edi, [eax + TCP_SOCKET.SND_NXT] |
155 | add edi, esi |
155 | add edi, esi |
156 | sub edi, [eax + TCP_SOCKET.SND_UNA] |
156 | sub edi, [eax + TCP_SOCKET.SND_UNA] |
157 | cmp edi, [eax + STREAM_SOCKET.snd.size] |
157 | cmp edi, [eax + STREAM_SOCKET.snd.size] |
158 | jae @f |
158 | jae @f |
159 | and dl, not (TH_FIN) |
159 | and dl, not (TH_FIN) |
160 | 160 | ||
161 | @@: |
161 | @@: |
162 | 162 | ||
163 | ;------------------------------- |
163 | ;------------------------------- |
164 | ; calculate window advertisement (130) |
164 | ; calculate window advertisement (130) |
165 | 165 | ||
166 | mov ecx, SOCKET_MAXDATA |
166 | mov ecx, SOCKET_MAXDATA |
167 | sub ecx, [eax + STREAM_SOCKET.rcv.size] |
167 | sub ecx, [eax + STREAM_SOCKET.rcv.size] |
168 | 168 | ||
169 | ;------------------------------ |
169 | ;------------------------------ |
170 | ; Sender silly window avoidance (131) |
170 | ; Sender silly window avoidance (131) |
171 | 171 | ||
172 | test esi, esi |
172 | test esi, esi |
173 | jz .len_zero |
173 | jz .len_zero |
174 | 174 | ||
175 | cmp esi, [eax + TCP_SOCKET.t_maxseg] |
175 | cmp esi, [eax + TCP_SOCKET.t_maxseg] |
176 | je TCP_send |
176 | je TCP_send |
177 | 177 | ||
178 | add ebx, esi ; offset + length |
178 | add ebx, esi ; offset + length |
179 | cmp ebx, [eax + STREAM_SOCKET.snd.size] |
179 | cmp ebx, [eax + STREAM_SOCKET.snd.size] |
180 | jb @f |
180 | jb @f |
181 | 181 | ||
182 | test [eax + TCP_SOCKET.t_flags], TF_NODELAY |
182 | test [eax + TCP_SOCKET.t_flags], TF_NODELAY |
183 | jnz TCP_send |
183 | jnz TCP_send |
184 | 184 | ||
185 | mov ebx, [eax + TCP_SOCKET.SND_MAX] |
185 | mov ebx, [eax + TCP_SOCKET.SND_MAX] |
186 | cmp ebx, [eax + TCP_SOCKET.SND_UNA] |
186 | cmp ebx, [eax + TCP_SOCKET.SND_UNA] |
187 | je TCP_send |
187 | je TCP_send |
188 | @@: |
188 | @@: |
189 | 189 | ||
190 | test [eax + TCP_SOCKET.t_force], -1 ;;; |
190 | test [eax + TCP_SOCKET.t_force], -1 ;;; |
191 | jnz TCP_send |
191 | jnz TCP_send |
192 | 192 | ||
193 | mov ebx, [eax + TCP_SOCKET.max_sndwnd] |
193 | mov ebx, [eax + TCP_SOCKET.max_sndwnd] |
194 | shr ebx, 1 |
194 | shr ebx, 1 |
195 | cmp esi, ebx |
195 | cmp esi, ebx |
196 | jae TCP_send |
196 | jae TCP_send |
197 | 197 | ||
198 | mov ebx, [eax + TCP_SOCKET.SND_NXT] |
198 | mov ebx, [eax + TCP_SOCKET.SND_NXT] |
199 | cmp ebx, [eax + TCP_SOCKET.SND_MAX] |
199 | cmp ebx, [eax + TCP_SOCKET.SND_MAX] |
200 | jb TCP_send |
200 | jb TCP_send |
201 | 201 | ||
202 | .len_zero: |
202 | .len_zero: |
203 | 203 | ||
204 | ;---------------------------------------- |
204 | ;---------------------------------------- |
205 | ; Check if a window update should be sent (154) |
205 | ; Check if a window update should be sent (154) |
206 | 206 | ||
207 | DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_output: window=%d\n", ecx |
207 | DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_output: window=%d\n", ecx |
208 | 208 | ||
209 | ; Compare available window to amount of window known to peer (as advertised window less next expected input) |
209 | ; Compare available window to amount of window known to peer (as advertised window less next expected input) |
210 | ; If the difference is at least two max size segments, or at least 50% of the maximum possible window, |
210 | ; If the difference is at least two max size segments, or at least 50% of the maximum possible window, |
211 | ; Then we want to send a window update to the peer. |
211 | ; Then we want to send a window update to the peer. |
212 | 212 | ||
213 | test ecx, ecx |
213 | test ecx, ecx |
214 | jz .no_window |
214 | jz .no_window |
215 | 215 | ||
216 | push ecx |
216 | push ecx |
217 | mov cl, [eax + TCP_SOCKET.RCV_SCALE] |
217 | mov cl, [eax + TCP_SOCKET.RCV_SCALE] |
218 | mov ebx, TCP_max_win |
218 | mov ebx, TCP_max_win |
219 | shl ebx, cl |
219 | shl ebx, cl |
220 | pop ecx |
220 | pop ecx |
221 | 221 | ||
222 | cmp ebx, ecx |
222 | cmp ebx, ecx |
223 | jb @f |
223 | jb @f |
224 | mov ebx, ecx |
224 | mov ebx, ecx |
225 | @@: |
225 | @@: |
226 | sub ebx, [eax + TCP_SOCKET.RCV_ADV] |
226 | sub ebx, [eax + TCP_SOCKET.RCV_ADV] |
227 | add ebx, [eax + TCP_SOCKET.RCV_NXT] |
227 | add ebx, [eax + TCP_SOCKET.RCV_NXT] |
228 | 228 | ||
229 | mov edi, [eax + TCP_SOCKET.t_maxseg] |
229 | mov edi, [eax + TCP_SOCKET.t_maxseg] |
230 | shl edi, 1 |
230 | shl edi, 1 |
231 | 231 | ||
232 | ; cmp ebx, edi |
232 | ; cmp ebx, edi |
233 | ; jae TCP_send |
233 | ; jae TCP_send |
234 | 234 | ||
235 | ; cmp ebx, [eax + TCP_SOCKET.] ;;; TODO: check with receive buffer high water mark |
235 | ; cmp ebx, [eax + TCP_SOCKET.] ;;; TODO: check with receive buffer high water mark |
236 | ; jae TCP_send |
236 | ; jae TCP_send |
237 | 237 | ||
238 | .no_window: |
238 | .no_window: |
239 | 239 | ||
240 | ;-------------------------- |
240 | ;-------------------------- |
241 | ; Should a segment be sent? (174) |
241 | ; Should a segment be sent? (174) |
242 | 242 | ||
243 | DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_output: 174\n" |
243 | DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_output: 174\n" |
244 | 244 | ||
245 | test [eax + TCP_SOCKET.t_flags], TF_ACKNOW ; we need to ACK |
245 | test [eax + TCP_SOCKET.t_flags], TF_ACKNOW ; we need to ACK |
246 | jnz TCP_send |
246 | jnz TCP_send |
247 | 247 | ||
248 | test dl, TH_SYN + TH_RST ; we need to send a SYN or RST |
248 | test dl, TH_SYN + TH_RST ; we need to send a SYN or RST |
249 | jnz TCP_send |
249 | jnz TCP_send |
250 | 250 | ||
251 | mov ebx, [eax + TCP_SOCKET.SND_UP] ; when urgent pointer is beyond start of send bufer |
251 | mov ebx, [eax + TCP_SOCKET.SND_UP] ; when urgent pointer is beyond start of send bufer |
252 | cmp ebx, [eax + TCP_SOCKET.SND_UNA] |
252 | cmp ebx, [eax + TCP_SOCKET.SND_UNA] |
253 | ja TCP_send |
253 | ja TCP_send |
254 | 254 | ||
255 | test dl, TH_FIN |
255 | test dl, TH_FIN |
256 | jz .enter_persist ; no reason to send, enter persist state |
256 | jz .enter_persist ; no reason to send, enter persist state |
257 | 257 | ||
258 | ; FIN was set, only send if not already sent, or on retransmit |
258 | ; FIN was set, only send if not already sent, or on retransmit |
259 | 259 | ||
260 | test [eax + TCP_SOCKET.t_flags], TF_SENTFIN |
260 | test [eax + TCP_SOCKET.t_flags], TF_SENTFIN |
261 | jz TCP_send |
261 | jz TCP_send |
262 | 262 | ||
263 | mov ebx, [eax + TCP_SOCKET.SND_NXT] |
263 | mov ebx, [eax + TCP_SOCKET.SND_NXT] |
264 | cmp ebx, [eax + TCP_SOCKET.SND_UNA] |
264 | cmp ebx, [eax + TCP_SOCKET.SND_UNA] |
265 | je TCP_send |
265 | je TCP_send |
266 | 266 | ||
267 | ;-------------------- |
267 | ;-------------------- |
268 | ; Enter persist state (191) |
268 | ; Enter persist state (191) |
269 | 269 | ||
270 | .enter_persist: |
270 | .enter_persist: |
271 | 271 | ||
272 | cmp [eax + STREAM_SOCKET.snd.size], 0 ; Data ready to send? |
272 | cmp [eax + STREAM_SOCKET.snd.size], 0 ; Data ready to send? |
273 | jne @f |
273 | jne @f |
274 | and [eax + TCP_SOCKET.timer_flags], not timer_flag_retransmission |
274 | and [eax + TCP_SOCKET.timer_flags], not timer_flag_retransmission |
275 | jne @f |
275 | jne @f |
276 | 276 | ||
277 | test [eax + TCP_SOCKET.timer_flags], timer_flag_persist ; Persist timer already expired? |
277 | test [eax + TCP_SOCKET.timer_flags], timer_flag_persist ; Persist timer already expired? |
278 | jnz @f |
278 | jnz @f |
279 | 279 | ||
280 | DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_output: Entering persist state\n" |
280 | DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_output: Entering persist state\n" |
281 | 281 | ||
282 | mov [eax + TCP_SOCKET.t_rxtshift], 0 |
282 | mov [eax + TCP_SOCKET.t_rxtshift], 0 |
283 | call TCP_set_persist |
283 | call TCP_set_persist |
284 | @@: |
284 | @@: |
285 | 285 | ||
286 | ;---------------------------- |
286 | ;---------------------------- |
287 | ; No reason to send a segment (219) |
287 | ; No reason to send a segment (219) |
288 | 288 | ||
289 | DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_output: No reason to send a segment\n" |
289 | DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_output: No reason to send a segment\n" |
290 | 290 | ||
291 | pusha |
291 | pusha |
292 | lea ecx, [eax + SOCKET.mutex] |
292 | lea ecx, [eax + SOCKET.mutex] |
293 | call mutex_unlock |
293 | call mutex_unlock |
294 | popa |
294 | popa |
295 | 295 | ||
296 | ; Fixme: returnvalue? |
296 | ; Fixme: returnvalue? |
297 | 297 | ||
298 | ret |
298 | ret |
299 | 299 | ||
300 | 300 | ||
301 | 301 | ||
302 | 302 | ||
303 | 303 | ||
304 | 304 | ||
305 | 305 | ||
306 | 306 | ||
307 | 307 | ||
308 | ;----------------------------------------------- |
308 | ;----------------------------------------------- |
309 | ; |
309 | ; |
310 | ; Send a segment (222) |
310 | ; Send a segment (222) |
311 | ; |
311 | ; |
312 | ; eax = socket pointer |
312 | ; eax = socket pointer |
313 | ; esi = data len |
313 | ; esi = data len |
314 | ; dl = flags |
314 | ; dl = flags |
315 | ; |
315 | ; |
316 | ;----------------------------------------------- |
316 | ;----------------------------------------------- |
317 | align 4 |
317 | align 4 |
318 | TCP_send: |
318 | TCP_send: |
319 | 319 | ||
320 | DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_send: socket=%x length=%u flags=%x\n", eax, esi, dl |
320 | DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_send: socket=%x length=%u flags=%x\n", eax, esi, dl |
321 | 321 | ||
322 | push eax ; save socket ptr |
322 | push eax ; save socket ptr |
323 | push esi ; and data length too |
323 | push esi ; and data length too |
324 | mov edi, sizeof.TCP_header ; edi will contain headersize |
324 | mov edi, sizeof.TCP_header ; edi will contain headersize |
325 | 325 | ||
326 | ;------------------------------------ |
326 | ;------------------------------------ |
327 | ; Send options with first SYN segment |
327 | ; Send options with first SYN segment |
328 | 328 | ||
329 | test dl, TH_SYN |
329 | test dl, TH_SYN |
330 | jz .options_done |
330 | jz .options_done |
331 | 331 | ||
332 | push [eax + TCP_SOCKET.ISS] |
332 | push [eax + TCP_SOCKET.ISS] |
333 | pop [eax + TCP_SOCKET.SND_NXT] |
333 | pop [eax + TCP_SOCKET.SND_NXT] |
334 | 334 | ||
335 | test [eax + TCP_SOCKET.t_flags], TF_NOOPT |
335 | test [eax + TCP_SOCKET.t_flags], TF_NOOPT |
336 | jnz .options_done |
336 | jnz .options_done |
337 | 337 | ||
338 | mov ecx, 1460 ;;;; FIXME: use routing blablabla to determine MSS |
338 | mov ecx, 1460 ;;;; FIXME: use routing blablabla to determine MSS |
339 | or ecx, TCP_OPT_MAXSEG shl 24 + 4 shl 16 |
339 | or ecx, TCP_OPT_MAXSEG shl 24 + 4 shl 16 |
340 | bswap ecx |
340 | bswap ecx |
341 | push ecx |
341 | push ecx |
342 | add di, 4 |
342 | add di, 4 |
343 | 343 | ||
344 | DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_send: added maxseg option\n" |
344 | DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_send: added maxseg option\n" |
345 | 345 | ||
346 | test [eax + TCP_SOCKET.t_flags], TF_REQ_SCALE |
346 | test [eax + TCP_SOCKET.t_flags], TF_REQ_SCALE |
347 | jz .no_scale |
347 | jz .no_scale |
348 | 348 | ||
349 | test dl, TH_ACK |
349 | test dl, TH_ACK |
350 | jz .scale_opt |
350 | jz .scale_opt |
351 | 351 | ||
352 | test [eax + TCP_SOCKET.t_flags], TF_RCVD_SCALE |
352 | test [eax + TCP_SOCKET.t_flags], TF_RCVD_SCALE |
353 | jz .no_scale |
353 | jz .no_scale |
354 | 354 | ||
355 | .scale_opt: |
355 | .scale_opt: |
356 | mov cl, [eax + TCP_SOCKET.request_r_scale] |
356 | mov cl, [eax + TCP_SOCKET.request_r_scale] |
357 | mov ch, TCP_OPT_NOP |
357 | mov ch, TCP_OPT_NOP |
358 | pushw cx |
358 | pushw cx |
359 | pushw TCP_OPT_WINDOW + 3 shl 8 |
359 | pushw TCP_OPT_WINDOW + 3 shl 8 |
360 | add di, 4 |
360 | add di, 4 |
361 | 361 | ||
362 | DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_send: added scale option\n" |
362 | DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_send: added scale option\n" |
363 | 363 | ||
364 | .no_scale: |
364 | .no_scale: |
365 | .no_syn: |
365 | .no_syn: |
366 | 366 | ||
367 | ;------------------------------------ |
367 | ;------------------------------------ |
368 | ; Make the timestamp option if needed |
368 | ; Make the timestamp option if needed |
369 | 369 | ||
370 | test [eax + TCP_SOCKET.t_flags], TF_REQ_TSTMP |
370 | test [eax + TCP_SOCKET.t_flags], TF_REQ_TSTMP |
371 | jz .no_timestamp |
371 | jz .no_timestamp |
372 | 372 | ||
373 | test dl, TH_RST |
373 | test dl, TH_RST |
374 | jnz .no_timestamp |
374 | jnz .no_timestamp |
375 | 375 | ||
376 | test dl, TH_ACK |
376 | test dl, TH_ACK |
377 | jz .timestamp |
377 | jz .timestamp |
378 | 378 | ||
379 | test [eax + TCP_SOCKET.t_flags], TF_RCVD_TSTMP |
379 | test [eax + TCP_SOCKET.t_flags], TF_RCVD_TSTMP |
380 | jz .no_timestamp |
380 | jz .no_timestamp |
381 | 381 | ||
382 | .timestamp: |
382 | .timestamp: |
383 | pushd 0 |
383 | pushd 0 |
384 | pushd [timer_ticks] |
384 | pushd [timer_ticks] |
385 | pushd TCP_OPT_NOP + TCP_OPT_NOP shl 8 + TCP_OPT_TIMESTAMP shl 16 + 10 shl 24 |
385 | pushd TCP_OPT_NOP + TCP_OPT_NOP shl 8 + TCP_OPT_TIMESTAMP shl 16 + 10 shl 24 |
386 | add di, 12 |
386 | add di, 12 |
387 | 387 | ||
388 | DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_send: added timestamp\n" |
388 | DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_send: added timestamp\n" |
389 | 389 | ||
390 | .no_timestamp: |
390 | .no_timestamp: |
391 | 391 | ||
392 | ; |
392 | ; |
393 | 393 | ||
394 | .options_done: |
394 | .options_done: |
395 | 395 | ||
396 | ; eax = socket ptr |
396 | ; eax = socket ptr |
397 | ; edx = flags |
397 | ; edx = flags |
398 | ; edi = header size |
398 | ; edi = header size |
399 | ; esi = data len |
399 | ; esi = data len |
400 | 400 | ||
401 | ;--------------------------------------------- |
401 | ;--------------------------------------------- |
402 | ; check if we dont exceed the max segment size (270) |
402 | ; check if we dont exceed the max segment size (270) |
403 | 403 | ||
404 | add esi, edi ; total TCP segment size |
404 | add esi, edi ; total TCP segment size |
405 | cmp esi, [eax + TCP_SOCKET.t_maxseg] |
405 | cmp esi, [eax + TCP_SOCKET.t_maxseg] |
406 | jbe .no_overflow |
406 | jbe .no_overflow |
407 | 407 | ||
408 | mov esi, [eax + TCP_SOCKET.t_maxseg] |
408 | mov esi, [eax + TCP_SOCKET.t_maxseg] |
409 | or [eax + TCP_SOCKET.temp_bits], TCP_BIT_SENDALOT |
409 | or [eax + TCP_SOCKET.temp_bits], TCP_BIT_SENDALOT |
410 | .no_overflow: |
410 | .no_overflow: |
411 | 411 | ||
412 | ;----------------------------------------------------------------- |
412 | ;----------------------------------------------------------------- |
413 | ; Start by pushing all TCP header values in reverse order on stack |
413 | ; Start by pushing all TCP header values in reverse order on stack |
414 | ; (essentially, creating the tcp header on the stack!) |
414 | ; (essentially, creating the tcp header on the stack!) |
415 | 415 | ||
416 | pushw 0 ; .UrgentPointer dw ? |
416 | pushw 0 ; .UrgentPointer dw ? |
417 | pushw 0 ; .Checksum dw ? |
417 | pushw 0 ; .Checksum dw ? |
418 | pushw 0x00a0 ; .Window dw ? ;;;;;;; FIXME (370) |
418 | pushw 0x00a0 ; .Window dw ? ;;;;;;; FIXME (370) |
419 | shl edi, 2 ; .DataOffset db ? only 4 left-most bits |
419 | shl edi, 2 ; .DataOffset db ? only 4 left-most bits |
420 | shl dx, 8 |
420 | shl dx, 8 |
421 | or dx, di ; .Flags db ? |
421 | or dx, di ; .Flags db ? |
422 | pushw dx |
422 | pushw dx |
423 | shr edi, 2 ; .DataOffset db ? |
423 | shr edi, 2 ; .DataOffset db ? |
424 | 424 | ||
425 | push [eax + TCP_SOCKET.RCV_NXT] ; .AckNumber dd ? |
425 | push [eax + TCP_SOCKET.RCV_NXT] ; .AckNumber dd ? |
426 | ntohd [esp] |
426 | ntohd [esp] |
427 | 427 | ||
428 | push [eax + TCP_SOCKET.SND_NXT] ; .SequenceNumber dd ? |
428 | push [eax + TCP_SOCKET.SND_NXT] ; .SequenceNumber dd ? |
429 | ntohd [esp] |
429 | ntohd [esp] |
430 | 430 | ||
431 | push [eax + TCP_SOCKET.RemotePort] ; .DestinationPort dw ? |
431 | push [eax + TCP_SOCKET.RemotePort] ; .DestinationPort dw ? |
432 | push [eax + TCP_SOCKET.LocalPort] ; .SourcePort dw ? |
432 | push [eax + TCP_SOCKET.LocalPort] ; .SourcePort dw ? |
433 | 433 | ||
434 | push edi ; header size |
434 | push edi ; header size |
435 | 435 | ||
436 | ;--------------------- |
436 | ;--------------------- |
437 | ; Create the IP packet |
437 | ; Create the IP packet |
438 | 438 | ||
439 | mov ecx, esi |
439 | mov ecx, esi |
440 | - | ||
441 | mov ebx, [eax + SOCKET.device] |
- | |
442 | mov edx, [eax + IP_SOCKET.LocalIP] ; source ip |
440 | mov edx, [eax + IP_SOCKET.LocalIP] ; source ip |
443 | mov eax, [eax + IP_SOCKET.RemoteIP] ; dest ip |
441 | mov eax, [eax + IP_SOCKET.RemoteIP] ; dest ip |
444 | mov di, IP_PROTO_TCP shl 8 + 128 |
442 | mov di, IP_PROTO_TCP shl 8 + 128 |
445 | call IPv4_output |
443 | call IPv4_output |
446 | jz .ip_error |
444 | jz .ip_error |
447 | 445 | ||
448 | ;----------------------------------------- |
446 | ;----------------------------------------- |
449 | ; Move TCP header from stack to TCP packet |
447 | ; Move TCP header from stack to TCP packet |
450 | 448 | ||
451 | push ecx |
449 | push ecx |
452 | mov ecx, [esp + 4] |
450 | mov ecx, [esp + 4] |
453 | lea esi, [esp + 8] |
451 | lea esi, [esp + 8] |
454 | shr ecx, 2 ; count is in bytes, we will work with dwords |
452 | shr ecx, 2 ; count is in bytes, we will work with dwords |
455 | rep movsd |
453 | rep movsd |
456 | pop ecx ; full TCP packet size |
454 | pop ecx ; full TCP packet size |
457 | 455 | ||
458 | pop esi ; headersize |
456 | pop esi ; headersize |
459 | add esp, esi ; remove it from stack |
457 | add esp, esi ; remove it from stack |
460 | 458 | ||
461 | push edx ; packet size for send proc |
459 | push edx ; packet size for send proc |
462 | push eax ; packet ptr for send proc |
460 | push eax ; packet ptr for send proc |
463 | 461 | ||
464 | mov edx, edi ; begin of data |
462 | mov edx, edi ; begin of data |
465 | sub edx, esi ; begin of packet (edi = begin of data) |
463 | sub edx, esi ; begin of packet (edi = begin of data) |
466 | push ecx |
464 | push ecx |
467 | sub ecx, esi ; data size |
465 | sub ecx, esi ; data size |
468 | 466 | ||
469 | ;-------------- |
467 | ;-------------- |
470 | ; Copy the data |
468 | ; Copy the data |
471 | 469 | ||
472 | ; eax = ptr to ring struct |
470 | ; eax = ptr to ring struct |
473 | ; ecx = buffer size |
471 | ; ecx = buffer size |
474 | ; edi = ptr to buffer |
472 | ; edi = ptr to buffer |
475 | 473 | ||
476 | mov eax, [esp + 16] ; get socket ptr |
474 | mov eax, [esp + 16] ; get socket ptr |
477 | 475 | ||
478 | push edx |
476 | push edx |
479 | push [eax + TCP_SOCKET.SND_NXT] ; we'll need this for timing the transmission |
477 | push [eax + TCP_SOCKET.SND_NXT] ; we'll need this for timing the transmission |
480 | test ecx, ecx |
478 | test ecx, ecx |
481 | jz .nodata |
479 | jz .nodata |
482 | mov edx, [eax + TCP_SOCKET.SND_NXT] |
480 | mov edx, [eax + TCP_SOCKET.SND_NXT] |
483 | add [eax + TCP_SOCKET.SND_NXT], ecx ; update sequence number <<< CHECKME |
481 | add [eax + TCP_SOCKET.SND_NXT], ecx ; update sequence number <<< CHECKME |
484 | sub edx, [eax + TCP_SOCKET.SND_UNA] ; offset |
482 | sub edx, [eax + TCP_SOCKET.SND_UNA] ; offset |
485 | add eax, STREAM_SOCKET.snd |
483 | add eax, STREAM_SOCKET.snd |
486 | call SOCKET_ring_read |
484 | call SOCKET_ring_read |
487 | .nodata: |
485 | .nodata: |
488 | pop edi |
486 | pop edi |
489 | pop esi ; begin of data |
487 | pop esi ; begin of data |
490 | pop ecx ; full packet size |
488 | pop ecx ; full packet size |
491 | mov eax, [esp + 12] ; socket ptr |
489 | mov eax, [esp + 12] ; socket ptr |
492 | 490 | ||
493 | ;---------------------------------- |
491 | ;---------------------------------- |
494 | ; initialize retransmit timer (400) |
492 | ; initialize retransmit timer (400) |
495 | 493 | ||
496 | ;TODO: check t_force and persist |
494 | ;TODO: check t_force and persist |
497 | 495 | ||
498 | test [esi + TCP_header.Flags], TH_SYN + TH_FIN ; syn and fin take a sequence number |
496 | test [esi + TCP_header.Flags], TH_SYN + TH_FIN ; syn and fin take a sequence number |
499 | jz @f |
497 | jz @f |
500 | inc [eax + TCP_SOCKET.SND_NXT] |
498 | inc [eax + TCP_SOCKET.SND_NXT] |
501 | test [esi + TCP_header.Flags], TH_FIN |
499 | test [esi + TCP_header.Flags], TH_FIN |
502 | jz @f |
500 | jz @f |
503 | or [eax + TCP_SOCKET.t_flags], TF_SENTFIN ; if we sent a fin, set the sentfin flag |
501 | or [eax + TCP_SOCKET.t_flags], TF_SENTFIN ; if we sent a fin, set the sentfin flag |
504 | @@: |
502 | @@: |
505 | 503 | ||
506 | mov edx, [eax + TCP_SOCKET.SND_NXT] |
504 | mov edx, [eax + TCP_SOCKET.SND_NXT] |
507 | cmp edx, [eax + TCP_SOCKET.SND_MAX] ; is this a retransmission? |
505 | cmp edx, [eax + TCP_SOCKET.SND_MAX] ; is this a retransmission? |
508 | jbe @f |
506 | jbe @f |
509 | mov [eax + TCP_SOCKET.SND_MAX], edx ; [eax + TCP_SOCKET.SND_NXT] from before we updated it |
507 | mov [eax + TCP_SOCKET.SND_MAX], edx ; [eax + TCP_SOCKET.SND_NXT] from before we updated it |
510 | 508 | ||
511 | cmp [eax + TCP_SOCKET.t_rtt], 0 ; are we currently timing anything? |
509 | cmp [eax + TCP_SOCKET.t_rtt], 0 ; are we currently timing anything? |
512 | je @f |
510 | je @f |
513 | mov [eax + TCP_SOCKET.t_rtt], 1 ; nope, start transmission timer |
511 | mov [eax + TCP_SOCKET.t_rtt], 1 ; nope, start transmission timer |
514 | mov [eax + TCP_SOCKET.t_rtseq], edi |
512 | mov [eax + TCP_SOCKET.t_rtseq], edi |
515 | ;TODO: update stats |
513 | ;TODO: update stats |
516 | @@: |
514 | @@: |
517 | 515 | ||
518 | ; set retransmission timer if not already set, and not doing an ACK or keepalive probe |
516 | ; set retransmission timer if not already set, and not doing an ACK or keepalive probe |
519 | test [eax + TCP_SOCKET.timer_flags], timer_flag_retransmission |
517 | test [eax + TCP_SOCKET.timer_flags], timer_flag_retransmission |
520 | jnz .retransmit_set |
518 | jnz .retransmit_set |
521 | 519 | ||
522 | cmp edx, [eax + TCP_SOCKET.SND_UNA] ; edx is still [eax + TCP_SOCKET.SND_NXT] |
520 | cmp edx, [eax + TCP_SOCKET.SND_UNA] ; edx is still [eax + TCP_SOCKET.SND_NXT] |
523 | je .retransmit_set |
521 | je .retransmit_set |
524 | 522 | ||
525 | mov edx, [eax + TCP_SOCKET.t_rxtcur] |
523 | mov edx, [eax + TCP_SOCKET.t_rxtcur] |
526 | mov [eax + TCP_SOCKET.timer_retransmission], edx |
524 | mov [eax + TCP_SOCKET.timer_retransmission], edx |
527 | or [eax + TCP_SOCKET.timer_flags], timer_flag_retransmission |
525 | or [eax + TCP_SOCKET.timer_flags], timer_flag_retransmission |
528 | 526 | ||
529 | test [eax + TCP_SOCKET.timer_flags], timer_flag_persist |
527 | test [eax + TCP_SOCKET.timer_flags], timer_flag_persist |
530 | jz .retransmit_set |
528 | jz .retransmit_set |
531 | and [eax + TCP_SOCKET.timer_flags], not timer_flag_persist |
529 | and [eax + TCP_SOCKET.timer_flags], not timer_flag_persist |
532 | mov [eax + TCP_SOCKET.t_rxtshift], 0 |
530 | mov [eax + TCP_SOCKET.t_rxtshift], 0 |
533 | 531 | ||
534 | .retransmit_set: |
532 | .retransmit_set: |
535 | 533 | ||
536 | ;-------------------- |
534 | ;-------------------- |
537 | ; Create the checksum |
535 | ; Create the checksum |
538 | 536 | ||
539 | TCP_checksum (eax + IP_SOCKET.LocalIP), (eax + IP_SOCKET.RemoteIP) |
537 | TCP_checksum (eax + IP_SOCKET.LocalIP), (eax + IP_SOCKET.RemoteIP) |
540 | mov [esi + TCP_header.Checksum], dx |
538 | mov [esi + TCP_header.Checksum], dx |
541 | 539 | ||
542 | ;---------------- |
540 | ;---------------- |
543 | ; Send the packet |
541 | ; Send the packet |
544 | 542 | ||
545 | DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_send: Sending with device %x\n", ebx |
543 | DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_send: Sending with device %x\n", ebx |
546 | call [ebx + NET_DEVICE.transmit] |
544 | call [ebx + NET_DEVICE.transmit] |
547 | jnz .send_error |
545 | jnz .send_error |
548 | 546 | ||
549 | ;--------------- |
547 | ;--------------- |
550 | ; Ok, data sent! |
548 | ; Ok, data sent! |
551 | 549 | ||
552 | pop ecx |
550 | pop ecx |
553 | pop eax |
551 | pop eax |
554 | 552 | ||
555 | call NET_ptr_to_num4 |
553 | call NET_ptr_to_num4 |
556 | inc [TCP_segments_tx + edi] |
554 | inc [TCP_segments_tx + edi] |
557 | 555 | ||
558 | ; update advertised receive window |
556 | ; update advertised receive window |
559 | test ecx, ecx |
557 | test ecx, ecx |
560 | jz @f |
558 | jz @f |
561 | add ecx, [eax + TCP_SOCKET.RCV_NXT] |
559 | add ecx, [eax + TCP_SOCKET.RCV_NXT] |
562 | cmp ecx, [eax + TCP_SOCKET.RCV_ADV] |
560 | cmp ecx, [eax + TCP_SOCKET.RCV_ADV] |
563 | jbe @f |
561 | jbe @f |
564 | mov [eax + TCP_SOCKET.RCV_ADV], ecx |
562 | mov [eax + TCP_SOCKET.RCV_ADV], ecx |
565 | @@: |
563 | @@: |
566 | 564 | ||
567 | ; update last ack sent |
565 | ; update last ack sent |
568 | push [eax + TCP_SOCKET.RCV_NXT] |
566 | push [eax + TCP_SOCKET.RCV_NXT] |
569 | pop [eax + TCP_SOCKET.last_ack_sent] |
567 | pop [eax + TCP_SOCKET.last_ack_sent] |
570 | 568 | ||
571 | ; and flags |
569 | ; and flags |
572 | and [eax + TCP_SOCKET.t_flags], not (TF_ACKNOW + TF_DELACK) |
570 | and [eax + TCP_SOCKET.t_flags], not (TF_ACKNOW + TF_DELACK) |
573 | 571 | ||
574 | ;-------------- |
572 | ;-------------- |
575 | ; unlock socket |
573 | ; unlock socket |
576 | 574 | ||
577 | DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_send: unlocking socket 0x%x\n", eax |
575 | DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_send: unlocking socket 0x%x\n", eax |
578 | 576 | ||
579 | push eax |
577 | push eax |
580 | lea ecx, [eax + SOCKET.mutex] |
578 | lea ecx, [eax + SOCKET.mutex] |
581 | call mutex_unlock |
579 | call mutex_unlock |
582 | pop eax |
580 | pop eax |
583 | 581 | ||
584 | ;----------------------------- |
582 | ;----------------------------- |
585 | ; Check if we need more output |
583 | ; Check if we need more output |
586 | 584 | ||
587 | test [eax + TCP_SOCKET.temp_bits], TCP_BIT_SENDALOT |
585 | test [eax + TCP_SOCKET.temp_bits], TCP_BIT_SENDALOT |
588 | jnz TCP_output.again |
586 | jnz TCP_output.again |
589 | 587 | ||
590 | DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_send: success!\n" |
588 | DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_send: success!\n" |
591 | 589 | ||
592 | xor eax, eax |
590 | xor eax, eax |
593 | ret |
591 | ret |
594 | 592 | ||
595 | 593 | ||
596 | .ip_error: |
594 | .ip_error: |
597 | pop ecx |
595 | pop ecx |
598 | add esp, ecx |
596 | add esp, ecx |
599 | add esp, 4 |
597 | add esp, 4 |
600 | pop eax |
598 | pop eax |
601 | 599 | ||
602 | mov [eax + TCP_SOCKET.timer_retransmission], TCP_time_re_min |
600 | mov [eax + TCP_SOCKET.timer_retransmission], TCP_time_re_min |
603 | or [eax + TCP_SOCKET.timer_flags], timer_flag_retransmission |
601 | or [eax + TCP_SOCKET.timer_flags], timer_flag_retransmission |
604 | 602 | ||
605 | lea ecx, [eax + SOCKET.mutex] |
603 | lea ecx, [eax + SOCKET.mutex] |
606 | call mutex_unlock |
604 | call mutex_unlock |
607 | 605 | ||
608 | DEBUGF DEBUG_NETWORK_ERROR, "TCP_send: IP error\n" |
606 | DEBUGF DEBUG_NETWORK_ERROR, "TCP_send: IP error\n" |
609 | 607 | ||
610 | or eax, -1 |
608 | or eax, -1 |
611 | ret |
609 | ret |
612 | 610 | ||
613 | 611 | ||
614 | .send_error: |
612 | .send_error: |
615 | add esp, 4 |
613 | add esp, 4 |
616 | pop eax |
614 | pop eax |
617 | 615 | ||
618 | lea ecx, [eax + SOCKET.mutex] |
616 | lea ecx, [eax + SOCKET.mutex] |
619 | call mutex_unlock |
617 | call mutex_unlock |
620 | 618 | ||
621 | DEBUGF DEBUG_NETWORK_ERROR, "TCP_send: sending failed\n" |
619 | DEBUGF DEBUG_NETWORK_ERROR, "TCP_send: sending failed\n" |
622 | 620 | ||
623 | or eax, -2 |
621 | or eax, -2 |
624 | ret><><<> |
622 | ret><><<> |