!************************************************************************** !* !* Network driver interface for netboot bootrom !* !* Module: packet.S !* Purpose: Packet driver to UNDI interface !* Entries: drv_entry !* !************************************************************************** !* !* Copyright (C) 1998-2003 Gero Kuhlmann !* !* This program is free software; you can redistribute it and/or modify !* it under the terms of the GNU General Public License as published by !* the Free Software Foundation; either version 2 of the License, or !* any later version. !* !* This program is distributed in the hope that it will be useful, !* but WITHOUT ANY WARRANTY; without even the implied warranty of !* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the !* GNU General Public License for more details. !* !* You should have received a copy of the GNU General Public License !* along with this program; if not, write to the Free Software !* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. !* !* $Id: packet.S,v 1.7 2003/03/09 00:43:07 gkminix Exp $ !* ! !************************************************************************** ! ! Include assembler macros: ! #include #include #include #include #include #include "pktpriv.inc" ! !************************************************************************** ! ! If all features are required, we dont have to specify everything on ! the CPP command line. ! #ifdef NEEDALL # ifndef NEEDMCAST # define NEEDMCAST # endif # ifndef NEEDSETADDR # define NEEDSETADDR # endif # ifndef NEEDSTAT # define NEEDSTAT # endif #endif ! !************************************************************************** ! ! Definition of service flags. See NDIS-2.0 specification for further ! explanation. ! #ifdef NEEDMCAST FLAGS_MCAST equ %0000000000000010 #else FLAGS_MCAST equ %0000000000000000 #endif #ifdef NEEDSETADDR FLAGS_SETADDR equ %0000000000010000 #else FLAGS_SETADDR equ %0000000000000000 #endif FLAGS_STANDARD equ %0000110000001001 ! !************************************************************************** ! ! Values for initialization flag: ! FLAG_NOTSTART equ 0 ! UNDI driver not started FLAG_NOTINIT equ UNDI_STATE_STARTED ! device not initialized FLAG_INIT equ UNDI_STATE_INITIALIZED ! device is initialized FLAG_OPEN equ UNDI_STATE_OPENED ! device has been opened ! !************************************************************************** ! ! Transmit retry values: ! XMT_RETRY equ 10 ! retry 10 times XMT_WAIT equ 5 ! wait 50 ms between retries ! !************************************************************************** ! ! Definitions for multicast addresses: ! IP_MCAST_NET equ $E0 ! multicast IP addrs are D class HW_MCAST_1 equ $01 ! first byte of hardware mcast addr HW_MCAST_2 equ $00 ! second byte of hardware mcast addr HW_MCAST_3 equ $5E ! third byte of hardware mcast addr MCAST_BITS equ 64 ! size of multicast buffer in bits MCAST_BYTES equ (MCAST_BITS/8) ! size of multicast buffer in bytes ! !************************************************************************** ! ! BSS segment: ! .bss ! start BSS segment extrn irqnum ! hardware IRQ number extrn pktvect ! packet driver interrupt vector .lcomm hwaddr,ETH_ALEN ! buffer to hold real hardware address #ifdef NEEDSETADDR .lcomm curaddr,ETH_ALEN ! buffer to hold current hardware addr #endif #ifdef NEEDSTAT .lcomm xmtgood,4 ! number of successful transmissions .lcomm rcvgood,4 ! number of good frames received .lcomm rcvresource,4 ! number of frames discarded #endif .lcomm hwinfo,2 ! offset to hardware info structure .lcomm pkthandle,2 ! packet driver handle .lcomm pktinfo,4 ! far ptr to packet driver info struct .lcomm readsize,2 ! actual size of data in buffer .lcomm readbuf,ETH_BUF_SIZE ! read buffer .lcomm writebuf,ETH_BUF_SIZE ! write buffer .lcomm serviceflags,2 ! service flags .lcomm pktfilter,2 ! packet filter .lcomm pkttype,2 ! type of received packet .lcomm initflag,1 ! flag to indicate if initialized #ifdef NEEDSETADDR .lcomm addrflag,1 ! flag set when hardware addr changed #endif .lcomm isrflag,1 ! flag set when inside ISR handler .lcomm intcount,1 ! number of interrupts received .lcomm pktfunc,1 ! packet driver functionality #ifdef NEEDMCAST .lcomm mcastbuf,MCAST_BYTES ! multicast address buffer #endif ! !************************************************************************** ! ! Data segment: ! .data ! Default packet driver info structure for drivers which dont support the ! get_parameters call. def_info: .byte 1 ! driver conforms to version 1.09 .byte 9 .byte 14 ! size of this structure .byte ETH_ALEN ! length of network hardware address .word ETH_FRAME_MAX ! MTU including MAC header .word 0 ! buffer size for multicast addresses .word 0 ! no of back-to-back MTU receives - 1 .word 0 ! no of successive transmits - 1 .word 0 ! post-EOI interrupt hook number iface_type: .ascii "DIX+802.3" ! default interface type iface_type_end: ! !************************************************************************** ! ! Start code segment. ! .text public drv_entry ! define entry points ! External variables in code space extrn datseg ! External routines for interrupt handling extrn intstart extrn intstop extrn intset extrn intreset extrn dohwint extrn endhwint ! External routines in the general network driver interface library extrn gethwinfo extrn setmaster extrn wait10ms ! !************************************************************************** ! ! PXE API entry point. Since we get called by the general network driver ! interface API entry which already checked if the function number is for ! us, we dont have to do that again here. We can safely assume that the ! function number is always correct. Also, the general network driver ! interface API already saved all registers for us. ! Input: ES:DI - pointer to parameter structure ! BX - function number ! Output: AX - error code ! Registers changed: AX, BX, CX, DX, SI, DI, BP, DS, ES ! drv_entry: #if defined(IS386) || defined(IS286) # ifdef IS386 mov eax,cr0 # else smsw ax # endif test ax,#$0001 ! check protected mode flag jz entry1 # ifdef IS386 pushfd pop eax ! check virtual-8086 mode flag test eax,#$00020000 jnz entry1 ! we can continue in virtual-8086 mode # endif ! packet driver interface not mov ax,#PXENV_STATUS_PMODE ! available in protected mode ret #endif entry1: seg cs mov ds,[datseg] shl bx,#1 ! compute offset into jump table seg cs jmp near ptr jmptab[bx] ! call function handler ! !************************************************************************** ! ! Function table ! jmptab: .word pxe_start_undi ! start UNDI interface .word pxe_startup ! startup driver .word pxe_cleanup ! cleanup driver .word pxe_initialize ! initialize driver .word pxe_reset_nic ! reset network interface .word pxe_shutdown ! shutdown driver .word pxe_open ! open network driver .word pxe_close ! close network driver .word pxe_transmit ! transmit a packet #ifdef NEEDMCAST .word pxe_set_multicast ! set multicast address #else .word pxe_unsupported ! set multicast address #endif #ifdef NEEDSETADDR .word pxe_set_addr ! set station address #else .word pxe_unsupported ! set station address #endif .word pxe_set_filter ! set packet filter .word pxe_get_info ! get network driver information #ifdef NEEDSTAT .word pxe_get_stat ! get network statistics .word pxe_clear_stat ! clear statistics #else .word pxe_unsupported ! get network statistics .word pxe_unsupported ! clear statistics #endif .word pxe_unsupported ! initiate diagnostics .word pxe_unsupported ! force receive interrupt #ifdef NEEDMCAST .word pxe_get_mcast_addr ! get multicast address #else .word pxe_unsupported ! get multicast address #endif .word pxe_get_nic_type ! get network interface driver type .word pxe_get_iface_info ! get interface info .word pxe_isr ! handle hardware interrupt .word pxe_stop_undi ! stop UNDI interface .word pxe_get_state ! get UNDI state ! !************************************************************************** ! ! Stub for unsupported functions ! Input: ES:DI - pointer to parameter structure ! Output: AX - error code ! Registers changed: AX ! pxe_unsupported: mov ax,#PXENV_STATUS_UNSUPPORTED ret ! !************************************************************************** ! ! START_UNDI. This just saves the BIOS parameters passed to the bootrom ! and sets all interrupts required by the driver. The main UNDI handler ! routine already checked that we are only called once. ! Input: ES:DI - pointer to parameter structure ! Output: AX - error code ! Registers changed: AX, BX, CX, DX, SI, DI, ES ! pxe_start_undi: seg es mov ax,[di + o_su_reg_ax] seg es mov bx,[di + o_su_reg_bx] call gethwinfo ! get hardware info structure mov [hwinfo],si call setmaster ! set bus master if necessary call intstart ! set interrupt vectors xor ax,ax ret ! !************************************************************************** ! ! STOP_UNDI. This restores the interrupt vectors. ! Input: ES:DI - pointer to parameter structure ! Output: AX - error code ! Registers changed: AX, BX, CX, DX, SI, DI, ES ! pxe_stop_undi: mov ax,#PXENV_STATUS_FAILURE cmp byte ptr [initflag],#FLAG_NOTINIT ja pstop9 call closepkt ! close packet driver interface call intstop ! restore interrupt vectors mov ax,#PXENV_STATUS_KEEP_UNDI jc pstop9 mov byte ptr [initflag],#FLAG_NOTSTART xor ax,ax pstop9: ret ! !************************************************************************** ! ! UNDI_STARTUP. Startup the packet driver interface. This will open a ! packet driver handle and check that we are using a correct driver. ! Input: ES:DI - pointer to parameter structure ! Output: AX - error code ! Registers changed: AX, BX, CX, DX, SI, DI, ES ! pxe_startup: mov ax,#PXENV_STATUS_FAILURE cmp byte ptr [initflag],#FLAG_NOTSTART jne startA mov ah,#GET_PARAMETERS ! get parameters from packet call callpkt ! driver. if driver is too old, jnc start1 ! assume default values mov ax,ds mov es,ax mov di,#def_info start1: seg es cmp word ptr [di + 4],#ETH_FRAME_MAX ! check MTU jb start8 seg es cmp byte ptr [di + 3],#ETH_ALEN ! check MAC address size je start2 start8: mov ax,#PXENV_STATUS_UNDI_NIC_INIT ! invalid packet driver startA: jmp start9 start2: mov word ptr [pktinfo + 0],di ! save pointer to packet driver mov word ptr [pktinfo + 2],es ! information structure call openpkt ! open packet driver handle jc start8 push ds mov ax,#(DRIVER_INFO << 8) + $FF call callpkt ! get packet driver info pop ds jc start8 ! should never happen cmp ch,#CL_DEFAULT ! packet driver is not for ethernet jne start8 mov [pktfunc],al ! save packet driver functionality #if !defined(NEEDMCAST) && !defined(NEEDSETADDR) mov word ptr [serviceflags],#FLAGS_STANDARD #else mov bx,#FLAGS_STANDARD cmp al,#FUNC_ALL ! check if function supported by je start3 ! packet driver cmp al,#FUNC_EXTENDED jne start4 start3: or bx,#FLAGS_MCAST + FLAGS_SETADDR start4: mov [serviceflags],bx #endif mov ax,ds mov es,ax mov di,#hwaddr mov bx,[pkthandle] mov cx,#ETH_ALEN mov ah,#GET_ADDRESS ! determine hardware address call callpkt jc start8 cmp cx,#ETH_ALEN ! check that ethernet address has jne start8 ! correct length #ifdef NEEDSETADDR cld mov si,#hwaddr ! copy preset hardware address into mov di,#curaddr ! buffer for current address mov cx,#ETH_ALEN / 2 rep movsw call setaddr ! check if setting is supported mov byte ptr [addrflag],#0 #endif mov byte ptr [initflag],#FLAG_NOTINIT xor ax,ax ! return without error start9: push ax call closepkt ! close packet driver handle again pop ax ret ! !************************************************************************** ! ! UNDI_CLEANUP. This will remove the packet driver from memory and prepare ! the UNDI driver for removal from memory. ! Input: ES:DI - pointer to parameter structure ! Output: AX - error code ! Registers changed: AX, BX, CX, DX, SI, DI, ES ! pxe_cleanup: mov ax,#PXENV_STATUS_FAILURE cmp byte ptr [initflag],#FLAG_NOTINIT ja pclen9 call intset ! restore interrupts for packet driver jc pclen7 call openpkt ! get us a packet driver handle jc pclen7 mov ah,#TERMINATE ! terminate packet driver call callpkt jc pclen7 xor ax,ax ! return without error jmp pclen8 pclen7: mov ax,#PXENV_STATUS_KEEP_UNDI pclen8: push ax call intreset ! restore interrupts pop ax #ifdef IS386 mov dword ptr [pktvect],#0 #else mov word ptr [pktvect + 0],#0 mov word ptr [pktvect + 2],#0 #endif mov byte ptr [initflag],#FLAG_NOTSTART pclen9: ret ! !************************************************************************** ! ! Initialize the packet driver interface by setting all required access ! types and handles. ! Input: ES:DI - pointer to parameter structure ! Output: AX - error code ! Registers changed: AX, BX, CX, DX ! pxe_initialize: mov ax,#PXENV_STATUS_UNDI_ALREADY_INIT cmp byte ptr [initflag],#FLAG_NOTINIT ja pinit9 mov ax,#PXENV_STATUS_FAILURE jb pinit9 ! Reset the packet driver interface. This will also clear the receive ! mode and multicast address list. xor ax,ax mov word ptr [pktfilter],ax ! clear packet filter call resetpkt mov ax,#PXENV_STATUS_UNDI_PHY_INIT jc pinit9 call closepkt ! close packet driver again mov byte ptr [initflag],#FLAG_INIT xor ax,ax ! return without error pinit9: ret ! !************************************************************************** ! ! Shutdown the packet driver interface. ! Input: ES:DI - pointer to parameter structure ! Output: AX - error code ! Registers changed: AX ! pxe_shutdown: mov ax,#PXENV_STATUS_UNDI_NOT_INIT cmp byte ptr [initflag],#FLAG_NOTINIT jbe pshut9 xor dx,dx mov [pktfilter],dx ! disable receiver call setrcvmode call closepkt ! close packet driver mov byte ptr [initflag],#FLAG_NOTINIT xor ax,ax pshut9: ret ! !************************************************************************** ! ! Reset the packet driver and reinitialize the interface. ! Input: ES:DI - pointer to parameter structure ! Output: AX - error code ! Registers changed: AX, BX, CX, DX, SI ! pxe_reset_nic: mov ax,#PXENV_STATUS_UNDI_NOT_INIT cmp byte ptr [initflag],#FLAG_NOTINIT jbe prst9 mov byte ptr [initflag],#FLAG_NOTINIT mov al,#1 call resetpkt ! reset the packet driver interface mov ax,#PXENV_STATUS_UNDI_PHY_INIT jc prst9 #ifdef NEEDMCAST lea si,[di + o_ur_mcastbuf] call setmcast mov ax,#PXENV_STATUS_UNDI_MCAST jc prst9 ! reset the multicast address list #endif mov byte ptr [initflag],#FLAG_OPEN prst8: xor ax,ax ! return without error prst9: ret ! !************************************************************************** ! ! Open the network driver interface. ! Input: ES:DI - pointer to parameter structure ! Output: AX - error code ! Registers changed: AX, BX, CX, DX, SI ! pxe_open: mov ax,#PXENV_STATUS_UNDI_NOT_INIT cmp byte ptr [initflag],#FLAG_INIT jne popen9 mov ax,#PXENV_STATUS_UNDI_ALREADY_OPEN cmp byte ptr [initflag],#FLAG_OPEN je popen9 push di push es call openpkt ! open packet driver interface pop es pop di mov ax,#PXENV_STATUS_FAILURE jc popen9 seg es mov dx,word ptr [di + o_uo_pktfilter] cmp dx,[pktfilter] je popen1 mov [pktfilter],dx call setrcvmode ! set receive mode packet filter popen1: #ifdef NEEDMCAST lea si,[di + o_uo_mcastbuf] call setmcast ! set multicast address list mov ax,#PXENV_STATUS_UNDI_MCAST jc popen9 #endif #ifdef NEEDSETADDR test byte ptr [addrflag],#$FF jz popen8 ! check if address changed call setaddr mov ax,#PXENV_STATUS_UNDI_INV_HWADDR jc popen9 mov byte ptr [addrflag],#0 #endif popen8: mov byte ptr [initflag],#FLAG_OPEN xor ax,ax popen9: ret ! !************************************************************************** ! ! Close the network driver interface. ! Input: ES:DI - pointer to parameter structure ! Output: AX - error code ! Registers changed: AX, BX, CX, DX ! pxe_close: mov ax,#PXENV_STATUS_UNDI_NOT_OPEN cmp byte ptr [initflag],#FLAG_OPEN jne pclos9 call closepkt mov ax,#PXENV_STATUS_FAILURE jc pclos9 mov byte ptr [initflag],#FLAG_INIT xor ax,ax ! return without error pclos9: ret ! !************************************************************************** ! ! Transmit a packet ! Input: ES:DI - pointer to parameter structure ! Output: AX - error code ! Registers changed: AX, BX, CX, DX, SI, DI, BP, ES ! pxe_transmit: mov ax,#PXENV_STATUS_UNDI_NOT_OPEN cmp byte ptr [initflag],#FLAG_OPEN #ifdef IS386 jne near pxmitE #else je pxmit0 jmp near pxmitE #endif pxmit0: cld push ds push es mov ax,ds ! DS:BX - ptr to parameter structure mov es,ax ! ES:DI - ptr to send buffer mov bx,di mov di,#writebuf pop ds xor bp,bp ! BP - total size of send buffer ! First setup the MAC level header. If the protocol value in the parameter ! structure is 0, the upper layer has already filled in the MAC header. ! Otherwise, if the transmit flag is 0, we have to fill in the broadcast ! address for the destination address. Otherwise the hardware destination ! address can be found in the protocol structure. mov dl,byte ptr [bx + o_ut_protocol] or dl,dl jz pxmit4 pxmitA: mov cx,#ETH_ALEN / 2 cmp byte ptr [bx + o_ut_xmtflag],#UNDI_XMT_DESTADDR je pxmit1 mov ax,#$FFFF ! set destination address to broadcast rep stosw jmp pxmit2 pxmit1: push ds lds si,[bx + o_ut_dest_off] rep ! copy destination address into send movsw ! buffer pop ds pxmit2: push ds mov ax,es mov ds,ax #ifdef NEEDSETADDR mov si,#curaddr #else mov si,#hwaddr #endif mov cx,#ETH_ALEN / 2 rep ! copy source address into send buffer movsw pop ds mov ax,#ETH_P_IP cmp dl,#UNDI_PROT_IP ! determine type of packet je pxmit3 mov ax,#ETH_P_ARP cmp dl,#UNDI_PROT_ARP je pxmit3 mov ax,#ETH_P_RARP pxmit3: xchg al,ah ! write packet type into MAC header stosw add bp,#ETH_HLEN ! adjust buffer size to MAC header size ! The parameter structure contains a couple of buffers. One of them is the ! so called immediate buffer. Copy it into the send buffer. pxmit4: lds bx,[bx + o_ut_tbd_off] mov cx,word ptr [bx + o_tbd_xmtlength] jcxz pxmit5 add bp,cx cmp bp,#ETH_FRAME_MAX ja pxmitF push ds lds si,[bx + o_tbd_xmt_off] shr cx,#1 rep movsw jnc pxmitB movsb pxmitB: pop ds ! Now copy all the different transmit buffers into the send buffer. pxmit5: mov dx,word ptr [bx + o_tbd_blocks] or dx,dx jz pxmit8 lea bx,[bx + o_tbd_datablock] pxmit6: mov cx,word ptr [bx + o_tbd_tdlength] jcxz pxmit7 add bp,cx cmp bp,#ETH_FRAME_MAX ja pxmitF push ds lds si,[bx + o_tbd_tdaddr] shr cx,#1 rep movsw jnc pxmitC movsb pxmitC: pop ds pxmit7: add bx,#datablk_size ! continue with next buffer dec dx jnz pxmit6 jmp pxmit8 ! Handle transmit errors pxmitF: pop ds pxmitG: mov ax,#PXENV_STATUS_FAILURE jmp pxmitE ! We can now fillup the send buffer to the minimum size and send it using ! the packet driver pxmit8: cmp bp,#ETH_FRAME_MIN jae pxmit9 mov cx,#ETH_FRAME_MIN sub cx,bp xor al,al ! pad send buffer to minimum size rep stosb mov bp,#ETH_FRAME_MIN pxmit9: pop ds mov cx,#XMT_RETRY pxmitH: push cx mov si,#writebuf mov cx,bp mov bx,[pkthandle] ! send buffer using the packet driver mov ah,#SEND_PKT call callpkt pop cx jnc pxmitK cmp dh,#ERR_CANT_SEND ! if we cant send the packet, retry it jne pxmitG ! a couple of times, as the sender might mov bx,#XMT_WAIT ! just be busy right now call wait10ms loop pxmitH jmp pxmitG pxmitK: #ifdef NEEDSTAT # ifdef IS386 inc dword ptr [xmtgood] ! update the statistics # else add word ptr [xmtgood + 0],#1 adc word ptr [xmtgood + 2],#0 # endif #endif xor ax,ax ! return without error pxmitE: ret #ifdef NEEDMCAST ! !************************************************************************** ! ! Set multicast address list ! Input: ES:DI - pointer to parameter structure ! Output: AX - error code ! Registers changed: AX, BX, CX, DX, SI ! pxe_set_multicast: mov ax,#PXENV_STATUS_UNDI_NOT_OPEN cmp byte ptr [initflag],#FLAG_OPEN jne psetm9 lea si,[di + o_usma_mcastbuf] call setmcast mov ax,#PXENV_STATUS_UNDI_MCAST jc psetm9 xor ax,ax psetm9: ret #endif #ifdef NEEDSETADDR ! !************************************************************************** ! ! Set station address ! Input: ES:DI - pointer to parameter structure ! Output: AX - error code ! Registers changed: AX, BX, CX ! pxe_set_addr: #ifdef IS386 if ETH_ALEN = 6 pseta0 equ 0 else pseta0 equ 1 endif #else pseta0 equ 1 #endif mov bl,[initflag] mov ax,#PXENV_STATUS_UNDI_ALREADY_OPEN cmp bl,#FLAG_OPEN ! UNDI should not be open yet je pseta9 mov ax,#PXENV_STATUS_UNDI_NOT_INIT cmp bl,#FLAG_INIT ! is UNDI initialized already? jne pseta9 mov ax,#PXENV_STATUS_UNSUPPORTED test word ptr [serviceflags],#FLAGS_SETADDR jz pseta9 ! is this function supported? if pseta0 = 0 seg es mov eax,dword ptr [di + o_ussa_addr + 0] mov dword ptr [curaddr + 0],eax seg es mov ax,word ptr [di + o_ussa_addr + 4] mov word ptr [curaddr + 4],ax else xor bx,bx mov cx,#ETH_ALEN / 2 pseta1: seg es mov ax,word ptr [di + bx + o_ussa_addr] mov word ptr [bx + curaddr],ax add bx,#2 loop pseta1 endif inc byte ptr [addrflag] xor ax,ax pseta9: ret #endif ! !************************************************************************** ! ! Set packet filter ! Input: ES:DI - pointer to parameter structure ! Output: AX - error code ! Registers changed: AX, BX, CX, DX ! pxe_set_filter: mov ax,#PXENV_STATUS_UNDI_NOT_OPEN cmp byte ptr [initflag],#FLAG_OPEN jne psetf9 seg es mov dx,word ptr [di + o_uo_pktfilter] cmp dx,[pktfilter] je psetf8 mov [pktfilter],dx call setrcvmode psetf8: xor ax,ax psetf9: ret ! !************************************************************************** ! ! Get information about network driver ! Input: ES:DI - pointer to parameter structure ! Output: AX - error code ! Registers changed: AX, BX, CX, SI ! pxe_get_info: mov ax,#PXENV_STATUS_UNDI_NOT_INIT cmp byte ptr [initflag],#FLAG_INIT jne pgeti9 cld push di push ds add di,#o_ugi_intnumber mov ax,[irqnum] ! get interrupt IRQ number stosw lds bx,[pktinfo] mov ax,[bx + 4] ! get MTU (without MAC header size) sub ax,#ETH_HLEN stosw mov ax,#UNDI_TYPE_ETHER ! get ethernet hardware type stosw mov ax,#ETH_ALEN ! get hardware address length stosw pop ds #ifdef NEEDSETADDR mov si,#curaddr #else mov si,#hwaddr #endif mov cx,#ETH_ALEN / 2 ! copy current hardware address rep movsw add di,#UNDI_ADDR_LEN - ETH_ALEN mov si,#hwaddr mov cx,#ETH_ALEN / 2 ! copy default hardware address rep movsw pop di mov ax,#1 ! save size of send/receive queues seg es mov word ptr [di + o_ugi_rxbufcnt],ax seg es mov word ptr [di + o_ugi_txbufcnt],ax xor ax,ax pgeti9: ret #ifdef NEEDSTAT ! !************************************************************************** ! ! Read network driver statistics ! Input: ES:DI - pointer to parameter structure ! Output: AX - error code ! Registers changed: (E)AX, DX ! pxe_get_stat: #ifdef IS386 mov eax,[xmtgood] seg es mov [di + o_ugs_xmtgood],eax mov eax,[rcvgood] seg es mov [di + o_ugs_rcvgood],eax mov eax,[rcvresource] seg es mov [di + o_ugs_rcvreserr],eax #else /* IS386 */ mov ax,word ptr [xmtgood + 0] mov dx,word ptr [xmtgood + 2] seg es mov word ptr [di + o_ugs_xmtgood + 0],ax seg es mov word ptr [di + o_ugs_xmtgood + 2],dx mov ax,word ptr [rcvgood + 0] mov dx,word ptr [rcvgood + 2] seg es mov word ptr [di + o_ugs_rcvgood + 0],ax seg es mov word ptr [di + o_ugs_rcvgood + 2],dx mov ax,word ptr [rcvresource + 0] mov dx,word ptr [rcvresource + 2] seg es mov word ptr [di + o_ugs_rcvreserr + 0],ax seg es mov word ptr [di + o_ugs_rcvreserr + 2],dx #endif /* IS386 */ xor ax,ax ! return without error ret #endif #ifdef NEEDSTAT ! !************************************************************************** ! ! Clear network driver statistics ! Input: ES:DI - pointer to parameter structure ! Output: AX - error code ! Registers changed: (E)AX ! pxe_clear_stat: #ifdef IS386 xor eax,eax mov [xmtgood],eax mov [rcvgood],eax mov [rcvresource],eax #else xor ax,ax mov word ptr [xmtgood + 0],ax mov word ptr [xmtgood + 2],ax mov word ptr [rcvgood + 0],ax mov word ptr [rcvgood + 2],ax mov word ptr [rcvresource + 0],ax mov word ptr [rcvresource + 2],ax #endif ret #endif #ifdef NEEDMCAST ! !************************************************************************** ! ! Determine hardware multicast address for IP multicast address. ! Input: ES:DI - pointer to parameter structure ! Output: AX - error code ! Registers changed: AX, CX, SI, DI ! pxe_get_mcast_addr: lea bx,[di + o_ugma_inetaddr] seg es mov al,[bx] and al,#$F0 ! check that the IP address really is cmp al,#IP_MCAST_NET ! a multicast address mov ax,#PXENV_STATUS_UNDI_INV_HWADDR jne pgmca9 cld lea di,[di + o_ugma_hwaddr] mov ax,#HW_MCAST_2 * 256 + HW_MCAST_1 stosw mov al,#HW_MCAST_3 ! set hardware multicast for ethernet stosb ! devices seg es mov al,[bx + 1] and al,#$7F ! only 23 bits of the IP address will stosb ! be copied seg es mov ax,[bx + 2] stosw xor al,al ! return without error pgmca9: ret #endif ! !************************************************************************** ! ! Return NIC type information. ! Input: ES:DI - pointer to parameter structure ! Output: AX - error code ! Registers changed: AX, CX, SI, DI ! pxe_get_nic_type: cld mov si,[hwinfo] mov cx,#getnic_size rep ! just copy the hardware info structure movsb ! into destination parameter structure xor ax,ax ret ! !************************************************************************** ! ! Return interface info. ! Input: ES:DI - pointer to parameter structure ! Output: AX - error code ! Registers changed: AX, CX, SI, DI ! pxe_get_iface_info: cld push di xor ax,ax mov cx,#getiface_size rep stosb pop di mov ax,[serviceflags] seg es mov [di + o_ugii_serviceflags],ax lea di,[di + o_ugii_ifacetype] mov si,#iface_type mov cx,#iface_type_end - iface_type rep movsb xor ax,ax ret ! !************************************************************************** ! ! Handle hardware interrupt functions including reading the received ! data from the network driver. ! Input: ES:DI - pointer to parameter structure ! Output: AX - error code ! Registers changed: AX, BX ! pxe_isr: ! Dont check for the initflag status. This will be done in the receiver ! routine. Otherwise, we would block the interrupt system on the NIC by ! not calling the packet driver hardware interrupt handler. seg es mov bx,word ptr [di + o_uisr_funcflag] cmp bx,#UNDI_ISR_IN_START je pisr1 pushf ! we have to disable interrupts cli ! here cmp bx,#UNDI_ISR_IN_PROCESS je pisr3 mov al,[isrflag] ! IN_GET_NEXT is only valid or al,al ! if following a IN_PROCESS jz pisr2 cmp bx,#UNDI_ISR_IN_GET_NEXT ! we only have one rx buffer so je pisr5 ! terminate ISR on IN_GET_NEXT pisr2: popf mov ax,#PXENV_STATUS_FAILURE pisr6: jmp near pisr9 ! Handle function UNDI_ISR_IN_START. It will let the packet driver handle ! the interrupt. pisr1: inc byte ptr [intcount] call dohwint mov ax,#UNDI_ISR_OUT_NOT_OURS jc pisrB mov ax,#UNDI_ISR_OUT_OURS pisrB: jmp pisr8 ! Handle function UNDI_ISR_IN_PROCESS. It returns the address of the receive ! buffer to the kernel. pisr3: mov al,#1 xchg al,[isrflag] ! check that we are not inside or al,al ! our own handler mov ax,#UNDI_ISR_OUT_BUSY jnz pisr7 mov ax,[readsize] or ax,ax ! check that we have something jz pisr5 ! to receive seg es mov word ptr [di + o_uisr_buflength],ax seg es mov word ptr [di + o_uisr_framelength],ax seg es mov word ptr [di + o_uisr_headerlength],#ETH_HLEN mov ax,[pkttype] seg es mov byte ptr [di + o_uisr_pkttype],al mov si,#readbuf seg es mov word ptr [di + o_uisr_frame + 0],si seg es mov word ptr [di + o_uisr_frame + 2],ds mov ax,[si + ETH_ALEN * 2] xchg al,ah mov bx,#UNDI_PROT_IP cmp ax,#ETH_P_IP je pisr4 mov bx,#UNDI_PROT_ARP cmp ax,#ETH_P_ARP je pisr4 mov bx,#UNDI_PROT_RARP cmp ax,#ETH_P_RARP je pisr4 mov bx,#UNDI_PROT_OTHERS pisr4: seg es mov word ptr [di + o_uisr_prottype],bx mov ax,#UNDI_ISR_OUT_RECEIVE jmp pisr7 ! Terminate hardware interrupt processing. pisr5: cmp byte ptr [intcount],#0 je pisrA call endhwint pisrA: mov word ptr [readsize],#0 mov byte ptr [isrflag],#0 mov byte ptr [intcount],#0 mov ax,#UNDI_ISR_OUT_DONE ! terminate ISR handling pisr7: popf pisr8: seg es mov word ptr [di + o_uisr_funcflag],ax xor ax,ax pisr9: ret ! !************************************************************************** ! ! Return UNDI state. ! Input: ES:DI - pointer to parameter structure ! Output: AX - error code ! Registers changed: AX ! pxe_get_state: mov al,[initflag] seg es mov byte ptr [di + o_gs_state],al xor ax,ax ret ! !************************************************************************** ! ! This routine gets called by the packet driver when a new packet arrives. ! Input: AX - function number: 0 = return buffer pointer, 1 = buffer filled ! BX - handle ! CX - length of data received ! DS:SI - pointer to receive buffer (only when AX = 1) ! Output: ES:DI - pointer to receive buffer (only when AX = 0) ! Registers changed: AX, ES, DI ! receiver: push ds seg cs mov ds,[datseg] or ax,ax ! check function code jnz rec1 ! Return address of receive buffer in ES:DI. Discard the packet if the ! receive buffer is not empty or the receiver is disabled. cmp byte ptr [initflag],#FLAG_OPEN ! discard packet if driver not jne rec2 ! yet initialized cmp cx,#ETH_FRAME_MAX ! received data block too large? ja rec2 mov ax,[pktfilter] ! receiver disabled? or ax,ax jz rec2 mov ax,[readsize] ! buffer free? or ax,ax jnz rec2 seg cs mov es,[datseg] mov di,#readbuf ! return pointer to read buffer jmp rec9 rec2: xor di,di mov es,di ! discard packet #ifdef NEEDSTAT rec3: # ifdef IS386 inc dword ptr [rcvresource] # else add word ptr [rcvresource + 0],#1 adc word ptr [rcvresource + 2],#0 # endif #endif jmp rec9 ! The packet driver has copied the received packet into our buffer, so ! we can now check if the packet is for us at all. rec1: mov si,#readbuf call getaddrtype mov [pkttype],bx mov ax,[pktfilter] call checktype #ifdef NEEDSTAT jc rec3 #else jc rec9 #endif mov [readsize],cx ! save size of packet #ifdef NEEDSTAT # ifdef IS386 inc dword ptr [rcvgood] # else add word ptr [rcvgood + 0],#1 adc word ptr [rcvgood + 2],#0 # endif #endif rec9: pop ds retf ! !************************************************************************** ! ! Determine type of hardware address ! Input: DS:SI - pointer to MAC header hardware address ! Output: BX - type of hardware address (UNDI_PKT_*) ! Registers changed: AX, BX ! getaddrtype: ! Check if the first bit of the hardware address is zero. If it is, the ! address has to be directed. test byte ptr [si],#$01 jz getad8 ! Now check if we have a broadcast address. getad1: mov bx,#UNDI_PKT_BROADCAST if ETH_ALEN = 6 mov ax,[si + 0] and ax,[si + 2] and ax,[si + 4] inc ax else cld push cx mov cx,#ETH_ALEN mov ah,#$FF getad1: lodsb and ah,al loop getad1 pop cx sub si,#ETH_ALEN inc ah endif jz getad9 ! None of the conditions above applied, so check for a multicast address. ! All others are either directed or promiscuous. #ifdef NEEDMCAST mov bx,#UNDI_PKT_MULTICAST cmp word ptr [si],#HW_MCAST_2 * 256 + HW_MCAST_1 jne getad8 cmp byte ptr [si + 2],#HW_MCAST_3 je getad9 #endif getad8: mov bx,#UNDI_PKT_DIRECTED getad9: ret ! !************************************************************************** ! ! Check if a received packet is for us. ! Input: DS:SI - pointer to MAC header hardware address ! BX - type of hardware address (UNDI_PKT_*) ! AX - packet filter value ! Output: Carry set if packet is to be discarded ! Registers changed: (E)AX, BX, DX ! checktype: #ifdef IS386 if ETH_ALEN = 6 chkt0 equ 0 else chkt0 equ 1 endif #else chkt0 equ 1 #endif ! With promiscuous mode all packets are accepted. When no mode is ! selected, no packets are accepted. test ax,#UNDI_FLTR_PROMISC jnz chkt9 or ax,ax jz chkt8 ! Check if we have a broadcast address and are willing to accept broad- ! casts. cmp bx,#UNDI_PKT_BROADCAST jne chkt1 test ax,#UNDI_FLTR_BROADCAST jnz chkt9 jmp chkt8 ! Check if this packet is directed, and matches our own hardware address. chkt1: cmp bx,#UNDI_PKT_DIRECTED jne chkt2 test ax,#UNDI_FLTR_DIRECTED jz chkt8 if chkt0 = 0 mov eax,dword ptr [si + 0] #ifdef NEEDSETADDR cmp eax,dword ptr [curaddr + 0] #else cmp eax,dword ptr [hwaddr + 0] #endif jne chkt8 mov ax,word ptr [si + 4] #ifdef NEEDSETADDR cmp ax,word ptr [curaddr + 4] #else cmp ax,word ptr [hwaddr + 4] #endif je chkt9 else cld push es mov ax,ds mov es,ax ! temporarily save all registers mov ax,si mov dx,di mov bx,cx #ifdef NEEDSETADDR mov di,#curaddr #else mov di,#hwaddr #endif mov cx,#ETH_ALEN / 2 repe ! check if the address is our own cmpsw pop es mov si,ax mov di,dx ! restore registers mov cx,bx clc je chkt9 endif jmp chkt8 ! Check if this packet is multicast and we want to accept multicasts and ! the address is in our multicast list. chkt2: #ifdef NEEDMCAST cmp bx,#UNDI_PKT_MULTICAST jne chkt8 test ax,#UNDI_FLTR_DIRECTED jz chkt8 call getmcbit ! compute offset into multicast test [bx],al ! buffer jnz chkt9 #endif chkt8: stc chkt9: ret #ifdef NEEDMCAST ! !************************************************************************** ! ! Compute bit position in multicast buffer for a given hardware address ! Input: DS:SI - pointer to hardware address ! Output: BX - offset to byte in multicast buffer ! AL - bit mask ! Registers changed: AX, BX, DX ! getmcbit: cld push cx mov ch,#ETH_ALEN mov dx,#$FFFF ! DX:BX gets CRC value mov bx,#$FFFF ! Scan through all bytes in the hardware address and compute the CRC ! value. getmc1: lodsb xor ah,ah mov cl,#8 ! do 8 bits getmc2: #ifdef IS386 shld dx,bx,#1 ! shift DX:BX #else shl bx,#1 ! shift BX rcl dx,#1 ! through DX #endif rcl ah,#1 ! carry is at bottom of AH xor ah,al ! xor with lsb of data rcr ah,#1 ! and put in carry bit jnc getmc3 ! The polynome is: ! ! x^32 + x^26 + x^23 + x^22 + x^16 + x^12 + x^11 + x^10 + x^8 + ! x^7 + x^5 + x^4 + x^2 + x^1 xor dx,#%0000010011000001 xor bx,#%0001110110110110 + 1 ! plus one for end-around carry getmc3: shr al,#1 ! shift the data dec cl jnz getmc2 ! scan through all bits dec ch jnz getmc1 ! scan through all bytes ! Compute the byte offset into the multicast buffer and the bit mask. mov bl,dh ! get most significant 8 bits #ifdef IS186 rol bl,#3 #else rol bl,#1 rol bl,#1 rol bl,#1 ! put 3 bits at bottom #endif and bl,#$07 xor bh,bh ! make BX into an index to the byte add bx,#mcastbuf mov cl,dh ! get most significant 8 bits #ifdef IS186 ror cl,#2 #else ror cl,#1 ror cl,#1 ! put 3 bits at bottom #endif and cl,#$07 mov al,#$01 shl al,cl ! set the correct bit sub si,#ETH_ALEN ! restore SI pop cx ret #endif #ifdef NEEDMCAST ! !************************************************************************** ! ! Set multicast addresses from UNDI address list. ! Input: ES:SI - pointer to UNDI multicast address structure ! Output: Carry flag set if error ! Registers changed: AX, BX, CX, DX, SI ! setmcast: cld push ds push es push ds mov ax,es ! let DS:SI point to multicast list mov ds,ax pop es push di mov di,#mcastbuf ! clear multicast buffer mov cx,#MCAST_BYTES / 2 xor ax,ax rep stosw pop di mov ax,ds or ax,si ! do we have multicast addresses at all jz setmc8 lodsw ! get number of addresses in list mov cx,ax jcxz setmc8 ! nothing else to do seg es test word ptr [serviceflags],#FLAGS_MCAST stc jz setmc9 ! is multicast supported? setmc1: call getaddrtype ! check that the address really is cmp bx,#UNDI_PKT_MULTICAST ! a multicast address stc jne setmc9 call getmcbit ! get offset into multicast buffer seg es or [bx],al ! set multicast address bit add si,#UNDI_ADDR_LEN - ETH_ALEN loop setmc1 setmc8: clc setmc9: pop es pop ds ret #endif ! !************************************************************************** ! ! Set packet receive mode. This is not as easy as it looks because some ! packet drivers dont support this at all, and others just dont support ! certain packet filter values. Unfortunately, this is not predictable. ! Also, with some packet drivers (notably RTL-8139), after turning off ! the receiver there is no way to turn it on again. Sometimes the packet ! driver will also turn off the transmitter. So we have to avoid turning ! off the receiver. ! However, we do packet filtering by software in the receiver routine, ! and therefore have to set the packet driver filter value just to ! reduce the number of hardware interrupt frequency. In addition, the ! UNDI filter values dont directly match the packet driver values so we ! have to do some conversion. ! Input: DX - UNDI packet filter value ! Output: none ! Registers changed: AX, BX, CX ! setrcvmode: mov al,[pktfunc] cmp al,#FUNC_EXTENDED je setmd1 ! check that packet driver supports cmp al,#FUNC_ALL ! setting the receive mode jne setmd9 setmd1: mov bx,[pkthandle] or dx,dx ! never turn off the receive, instead jz setmd5 ! use directed mode test dx,#UNDI_FLTR_PROMISC jnz setmd8 #ifdef NEEDMCAST test word ptr [serviceflags],#FLAGS_MCAST jnz setmd7 #endif setmd2: test dx,#UNDI_FLTR_BROADCAST jnz setmd6 ! Set the lowest possible receiver mode. setmd5: mov ah,#SET_RCV_MODE mov cx,#MODE_DIRECTED call callpkt jnc setmd9 setmd6: mov ah,#SET_RCV_MODE mov cx,#MODE_BROADCAST call callpkt jnc setmd9 #ifdef NEEDMCAST setmd7: mov ah,#SET_RCV_MODE mov cx,#MODE_MCASTALL call callpkt jnc setmd9 #endif setmd8: mov ah,#SET_RCV_MODE mov cx,#MODE_ALL call callpkt setmd9: ret #ifdef NEEDSETADDR ! !************************************************************************** ! ! Set adapter hardware address ! Input: none ! Output: Carry flag set if error ! Registers changed: AX, CX ! setaddr: push es test word ptr [serviceflags],#FLAGS_SETADDR jz setad9 ! is this function supported? mov ax,ds mov es,ax mov di,#curaddr mov cx,#ETH_ALEN mov ah,#SET_ADDRESS call callpkt jnc setad9 cmp dh,#ERR_BAD_ADDRESS ! check if function supported? stc je setad9 and word ptr [serviceflags],#~FLAGS_SETADDR setad9: pop es ret #endif ! !************************************************************************** ! ! Open packet driver interface. ! Input: None ! Output: BX - packet driver handle ! Carry flag set if error ! Registers changed: AX, BX, CX, DX, SI, DI, ES ! openpkt: mov ax,[pkthandle] or ax,ax jnz openp9 mov ax,cs mov es,ax mov di,#receiver mov ax,#(ACCESS_TYPE << 8) + CL_DEFAULT mov bx,#TYPE_DEFAULT xor dl,dl ! set general packet handle xor cx,cx ! this will setup the packet call callpkt ! driver to receive packets jc openp9 ! with any packet type mov [pkthandle],ax openp9: mov bx,ax ret ! !************************************************************************** ! ! Close packet driver interface. ! Input: None ! Output: Carry flag set if error ! Registers changed: AX, BX ! closepkt: mov bx,[pkthandle] or bx,bx jz closp9 mov ah,#RELEASE_TYPE call callpkt jc closp9 mov word ptr [pkthandle],#0 closp9: ret ! !************************************************************************** ! ! Reset packet driver interface. Some packet drivers (pcipd.com for ! RTL8029 cards for example) incorrectly :-( hang the network card ! completely, so that it isnt able to receive anymore. We therefore ! disable the hardware reset feature and just reprogram the packet ! driver with some default values. ! Input: AL - non-zero if we have to physically reset the network card ! Output: Carry flag set if error ! Registers changed: AX, BX, CX, DX, SI, DI ! resetpkt: ! Reset the packet driver interface #ifdef USEBUGGY push ax call openpkt ! get us a packet driver handle pop ax jc reset9 or al,al ! check if we have to actually reset jz reset1 ! the interface mov ah,#RESET_INTERFACE call callpkt jc reset9 #else call openpkt ! get us a packet driver handle jc reset9 #endif ! Clear the multicast list reset1: #ifdef NEEDMCAST push es xor si,si mov es,si ! clear the multicast buffer call setmcast pop es #endif ! Restore the packet filter and hardware address mov dx,[pktfilter] ! reset packet filter call setrcvmode #ifdef NEEDSETADDR cld push es mov ax,ds mov es,ax mov si,#hwaddr ! copy preset hardware address into mov di,#curaddr ! buffer for current address mov cx,#ETH_ALEN / 2 rep movsw call setaddr ! set hardware address mov byte ptr [addrflag],#0 pop es #endif reset9: ret ! !************************************************************************** ! ! Call packet driver interrupt ! Input: AH - function code ! Output: depends on function called ! Registers changed: depends on function called ! callpkt: push ax mov ax,word ptr [pktvect + 0] or ax,word ptr [pktvect + 2] pop ax jnz callp1 mov dh,#ERR_BAD_COMMAND ! check if we have a packet driver stc ! at all ret callp1: push cs push ax ! set stack to simulate an INT pushf pop ax push bp mov bp,sp xchg ax,[bp + 6] ! set flags xchg ax,[bp + 2] ! set return address and restore AX pop bp jmp far ptr [pktvect] ! call packet driver ! !************************************************************************** ! end