Subversion Repositories Kolibri OS

Rev

Rev 5442 | Rev 5584 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

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