version equ 1 include defs.asm ;SEE ENCLOSED COPYRIGHT MESSAGE ; PC/FTP Packet Driver Source, conforming to version 1.08 of spec ; Krishnan Gopalan and Gregg Stefancik, Clemson Univesity Engineering ; Computer Operations. ; Date: September 1, 1989 ; Portions of the code have been adapted from the 3c505 driver for NCSA ; Telnet by Bruce Orchard and later modified by Warren Van Houten and krus ; @diku.dk. ; Permission is granted to any individual or institution to use,copy, ; modify or redistribute this software provided this notice is retained. ; The authors make no guarantee to the suitability of the software for ; any purpose. Any damage caused by using this program is the responsibility ; of the user and not the authors. code segment word public assume cs:code, ds:code ; 3c505 card definitions ;control register bit definitions EN_ATTN equ 0200q EN_FLSH_DATA equ 0100q EN_DMA_ENABLE equ 0040q EN_TO_HOST equ 0020q EN_TERMINAL_CNT_ENBLE equ 0010q EN_COMMAND_ENABLE equ 0004q EN_HSF2 equ 0002q EN_HSF1 equ 0001q ;status register bit definitions EN_DATA_READY equ 0200q EN_HOST_COMMAND_EMPTY equ 0100q EN_ADAPTER_COMMAND_FULL equ 0040q EN_TO_HOST equ 0020q EN_DMA_DONE equ 0010q EN_ASF3 equ 0004q EN_ASF2 equ 0002q EN_ASF1 equ 0001q ; auxiliary dma register bit definition EN_BURST equ 0001q ; timeout values SECOND EQU 36 RESDEL EQU 6 RESTO EQU 15*SECOND CMDBTO EQU 6 CMDCTO EQU 6 RETRYDELAY EQU 6 RCMDTO EQU 6 RESPTO EQU 6 ;port addresses ECOMMAND equ 0 ESTATUS equ 2 EDATA equ 4 ECONTROL equ 6 recvbuf db 4096 dup(?) ; size of receive buffer rbufct dw ? ; recv buffer count pcblen dw ? ; length of pcb pcbaddr dw ? ; address of pcb lastcon db ? ; last control to board cmdlen dw ? ; length of command cbsh equ 50 cbs equ cbsh*2 icmdb db cbs dup(?) icmd db cbsh dup(?) fconc db 0 ; flag configure 82586 fgeth db 0 ; flag:get ethernet address fseth db 0 ; flag:set ethernet address fxmit db 0 ; flag:transmit packet cconc db 02h ; command configure 82586 db 2 ; 2 more bytes dw 1 ; receive broadcasts rconc db 2 dup(?);Response; configure rconc_st dw ? ; status cgeth db 03h ; command get ethernet address db 00 ; no more bytes txmit db 09h ; command ; transmit packet db 06 ; 6 more bytes tx_offset dw ? ; offset of host transmit buffer tx_segment dw ? ; segment of host transmit buffer tx_length dw ? ; packet length rxmit db 2 dup(?); Response tx packet rx_offset dw ? ; buffer offset rx_segment dw ? ; buffer segment rx_status dw ? ; completion status rx_cstatus dw ? ; 82586 status rgeth db 2 dup(?); Response get Ethernet address rgeth_ad db 6 dup(?); -- address cseth db 10h ; command :set station address db 6 ; 6 more bytes cseth_ad db 6 dup(?); ethernet address rseth db 2 dup(?); response set ethernet address rseth_status dw ? ; status crecv db 08h ; command receive db 08 ; 8 more bytes crecv_offset dw ? ; buffer offset crecv_segment dw ? ; buffer segment crecv_length dw ? ; buffer length crecv_timeout dw ? ; timeout rr db 2 dup(?); Response; receive rr_offset dw ? ; buffer offset rr_segment dw ? ; buffer segment rr_dmalen dw ? ; buffer dmalen rr_length dw ? ; packet length rr_status dw ? ; completion status rr_rstatus dw ? ; 82586 receive status rr_time dd ? ; time tag public int_no,io_addr,mem_base int_no db 2,0,0,0 ;must be four bytes long for get_number. io_addr dw 0300h,0 ; io addr for card(jumpers) mem_base dw 0d000h,0 ; shared memory address(software) public driver_class, driver_type, driver_name, driver_function, parameter_list driver_class db BLUEBOOK, IEEE8023, 0 ;from the packet spec driver_type db 2 ;from the packet spec driver_name db '3C505',0 ;name of the driver. driver_function db 2 parameter_list label byte db 1 ;major rev of packet driver db 9 ;minor rev of packet driver db 14 ;length of parameter list db EADDR_LEN ;length of MAC-layer address dw GIANT ;MTU, including MAC headers dw MAX_MULTICAST * EADDR_LEN ;buffer size of multicast addrs dw 0 ;(# of back-to-back MTU rcvs) - 1 dw 0 ;(# of successive xmits) - 1 int_num dw 0 ;Interrupt # to hook for post-EOI ;processing, 0 == none, public rcv_modes rcv_modes dw 4 ;number of receive modes in our table. dw 0,0,0,rcv_mode_3 public as_send_pkt ; The Asynchronous Transmit Packet routine. ; Enter with es:di -> i/o control block, ds:si -> packet, cx = packet length, ; interrupts possibly enabled. ; Exit with nc if ok, or else cy if error, dh set to error number. ; es:di and interrupt enable flag preserved on exit. as_send_pkt: ret public drop_pkt ; Drop a packet from the queue. ; Enter with es:di -> iocb. drop_pkt: assume ds:nothing ret public xmit ; Process a transmit interrupt with the least possible latency to achieve ; back-to-back packet transmissions. ; May only use ax and dx. xmit: assume ds:nothing ret public send_pkt send_pkt: ;enter with es:di->upcall routine, (0:0) if no upcall is desired. ; (only if the high-performance bit is set in driver_function) ;enter with ds:si -> packet, cx = packet length. ;exit with nc if ok, or else cy if error, dh set to error number. assume ds:nothing push si ; save si for lodsw push ds ; save ds push es ; save es for timer push cs ; pop ds ; cs = ds align mov tx_segment,ds ;tx->data mov tx_offset,si ;tx_offset ->segment offset mov ax,cx ; save cx cmp ax,60 ; compare with minimum limit jnb pkt_ok ; go if ok mov ax,60 ; make length minimum allowed pkt_ok: inc ax ; round up sar ax,1 ; divide by 2 shl ax,1 ; multiply by 2 mov tx_length,ax ; put in request mov fxmit,0 ; clear response received flag mov si, offset txmit ; si->request(pcb) mov ax,8 ;length of pcb call outpcb ; send command to board mov cx,tx_length sar cx,1 pop es pop ds pop si tx_1: lodsw loadport setport EDATA out dx,ax ; output word setport ESTATUS tx_2: in al,dx ; get status test al, EN_DATA_READY ; ready for next word ? jz tx_2 ;no dec cx ; count word jnz tx_1 ; loop thru buffer tx_3: test fxmit,0ffh ; is transmit over jz tx_3 ; no ret public get_address get_address: ;get the address of the interface. ;enter with es:di -> place to get the address, cx = size of address buffer. ;exit with nc, cx = actual size of address, or cy if buffer not big enough. assume ds:code push es push di cmp cx, EADDR_LEN ; does caller want reasonable length? jb get_addr_fail ; no , fails mov si,offset cgeth ; si->pcb address mov ax,2 ; length of pcb mov fgeth,0 ; clear response received flag call outpcb mov ax,RESTO ; get wait time call set_timeout get_addr_1: test fgeth,0ffh ; answered? jnz get_addr_2 ; yes call do_timeout jnz get_addr_1 ; test again jmp get_addr_fail ; go return get_addr_2: pop di pop es cld ; make sure of string operation mov si,offset rgeth_ad ; load source of address mov cx,EADDR_LEN ; return length of address rep movsb ; copy address mov cx,EADDR_LEN ; return length of address clc ret get_addr_fail: add sp,4 stc ret public set_address set_address: ;enter with ds:si -> Ethernet address, CX = length of address. ;exit with nc if okay, or cy, dh=error if any errors. assume ds:nothing cmp cx, EADDR_LEN ; check if address ok je set_addr_1 mov dh,BAD_ADDRESS ; don't like length stc ret set_addr_1: mov di,offset cseth_ad ;di->destination offset rep movsb ; return address mov si,offset cseth ; si->request ethernet address mov ax,8 ; request length -> ax mov fseth,0 ; clear response received flag call outpcb ; send the pcb mov ax,RESTO ; get response time call set_timeout set_addr_2: test fseth,0ffh ; pcb answered? jnz set_addr_3 call do_timeout ; has time expired jne set_addr_2 ; no, go back stc ; error ret set_addr_3: mov cx,EADDR_LEN ;return their address length. clc ret rcv_mode_3: ;receive mode 3 is the only one we support, so we don't have to do anything. ret public set_multicast_list set_multicast_list: ;enter with ds:si ->list of multicast addresses, cx = number of addresses. ;return nc if we set all of them, or cy,dh=error if we didn't. mov dh,NO_MULTICAST stc ret public terminate terminate: ret public reset_interface reset_interface: ;reset the interface. assume ds:code mov al, EN_ATTN OR EN_FLSH_DATA ; hard reset command loadport setport ECONTROL out dx,al ; do reset mov ax,RESDEL ; get reset delay call set_timeout rst1: call do_timeout jne rst1 ; wait for reset mov al,EN_COMMAND_ENABLE ; command interrupt enable mov lastcon,al ; save last command out dx,al ; release reset mov ax,RESDEL call set_timeout rst2: call do_timeout jne rst2 ; wait to start reset mov ax,RESTO ; add time out call set_timeout rst3: call getstat ; getstatus and ax,EN_ASF1 OR EN_ASF2 ; cmp ax,EN_ASF1 OR EN_ASF2 ; both on ? jne resdone call do_timeout ; long enough? jne rst3 ; no ret resdone: ret ;called when we want to determine what to do with a received packet. ;enter with cx = packet length, es:di -> packet type, dl = packet class. extrn recv_find: near ;called after we have copied the packet into the buffer. ;enter with ds:si ->the packet, cx = length of the packet. extrn recv_copy: near extrn count_in_err: near extrn count_out_err: near public recv recv: ;called from the recv isr. All registers have been saved, and ds=cs. ; all interrupts come here ;Upon exit, the interrupt will be acknowledged. assume ds:code ;sti cld recv1: loadport setport ESTATUS in al,dx ; read flags and al,EN_ADAPTER_COMMAND_FULL ; jnz recv2 ; yes,full jmp interrupt_done ;there might be a response, clear flags to check for response recv2: mov ax,RCMDTO ; incoming command time out call set_timeout mov al,lastcon ; last control->ax and al,NOT (EN_HSF1 OR EN_HSF2) ; clear flags mov lastcon,al loadport setport ECONTROL out dx,al ; clear flags in cntl reg mov di,offset icmdb ; di->incoming command buffer recv3: loadport setport ESTATUS in al,dx ; status->al mov cx,ax ; status->cx test al,EN_ADAPTER_COMMAND_FULL ; is adapter register full jnz recv4 ; yes go ahead to read from call do_timeout ; is time up ? jne recv3 ; give some more time jmp int1 ; give up recv4: loadport setport ECOMMAND in al,dx ; get command byte and cl,EN_ASF1 OR EN_ASF2 cmp cl,EN_ASF1 OR EN_ASF2 ; both on? je recv5 ; end of command mov [di],al ;save byte inc di ;inc di mov ax,di ; current pointer->ax sub ax,offset icmdb ; - start of buffer cmp ax,cbs ; full? jl recv3 mov si,(offset icmdb)+cbsh ; si->middle of buffer mov di,offset icmdb ; di->start of buffer mov cx,cbsh ; size of half buffer->cx push ds pop es rep movsb jmp recv3 ; loop for more bytes recv5: push ds pop es mov ah,0 mov cmdlen,ax ; save cmdlen mov si,di sub si,cmdlen mov di,offset icmd mov cx,cmdlen rep movsb ; copy mov al,icmd ; check cmp al,32h ; Configure? je respconfig cmp al,33h ; get ethernet address? je respgetaddr ; yes cmp al,39h ; transmit complete? je transmit ; yes cmp al,40h ; set ethernet address je respsetaddr ; yes cmp al,38h ; receive complete? je resprecv jmp int1 respconfig: push ds pop es mov si,offset icmd mov di,offset rconc mov cx,2 rep movsw mov fconc,1 jmp int1 respgetaddr: push ds pop es mov si,offset icmd mov di,offset rgeth mov cx,4 rep movsw mov fgeth,1 jmp int1 respsetaddr: mov si,offset icmd ; si->command received mov di,offset cseth ; di->set ethernet address resp push ds pop es mov cx,2 rep movsw mov fseth,1 jmp int1 transmit: mov si,offset icmd ; si->command received mov di,offset rxmit ; response,transmit packet push ds pop es mov cx,5 ; response length->cx rep movsw mov fxmit,1 ; response received jmp int1 ; return from interrupt resprecv: mov si,offset icmd ; si->command received mov di,offset rr ; di->receive response push ds pop es mov cx,9 ; response length->cx rep movsw ; move response mov di,offset recvbuf ; di->receive buffer mov ax,rr_length ; message size->ax inc ax ; round up shr ax,1 ; convert to words shl ax,1 ; convert to characters mov rr_length,ax ; ax->message length mov cx,ax ; message length->cx shr cx,1 ; convert to words mov al,lastcon ; lastcontrol->al or al,EN_TO_HOST OR EN_HSF1 ; set direction & ack mov lastcon,al ; response loadport setport ECONTROL out dx,al ; pass direction setport ESTATUS resprecv_1: in al,dx ; get status test al,EN_DATA_READY ; data ready ? jz resprecv_1 setport EDATA ; data word->ax in ax,dx ; get word stosw ; store word in buffer setport ESTATUS dec cx ; count word jnz resprecv_1 ; loop if more words mov al,lastcon and al,NOT (EN_TO_HOST OR EN_HSF1) mov lastcon,al loadport ; dx->control register setport ECONTROL out dx,al mov cx,rr_length ; cx->packet length push cs ; align cs and ds pop ds mov di,offset recvbuf ; reset di to beginning add di,EADDR_LEN+EADDR_LEN ; point to type field ; of buffer assume ds:code mov dl, BLUEBOOK ;assume bluebook Ethernet. mov ax, es:[di] xchg ah, al cmp ax, 1500 ja BlueBookPacket inc di ;set di to 802.2 header inc di mov dl, IEEE8023 BlueBookPacket: call recv_find ; first call assume ds:nothing mov ax,es ; ax->es or ax,di ; is es=di=0? je rcv_no_copy mov cx,rr_length ; cx->packet length push cx push es push di mov si,offset recvbuf ; prepare to copy the packet rep movsb ; copy pop si pop ds pop cx call recv_copy ; second call rcv_no_copy: push cs pop ds call anotherrecv ; start another recv int1: jmp recv1 interrupt_done: ret ;*****************************OUTPCB*********************************** ;outpcb send pcb to board, retry until accepted ; entry ax = number of bytes in pcb ; si = address of pcb outpcb proc near mov pcblen,ax ; save pcb length mov pcbaddr,si ; save pcb address pcb_0: mov cx,pcblen ;length->cx mov si,pcbaddr ; address->si cli ; stop interrupts mov al,lastcon ; save last command and al, NOT (EN_HSF1 OR EN_HSF2); clear flags mov lastcon,al sti ; enable interrupts loadport setport ECONTROL out dx,al ; send control setport ECOMMAND pcb_1: mov al,[si] ; out dx,al ; send command byte mov ax,CMDBTO ; get time out call set_timeout chk_hcre: call getstat and al, EN_HOST_COMMAND_EMPTY ; command accepted jne pcb_2 ; go on call do_timeout jne chk_hcre ; time is still left jmp cmdretry ; retry command pcb_2: inc si ; increment source pointer dec cx ; count byte jg pcb_1 ; loop loadport setport ECONTROL cli ; disable interrupts mov al,lastcon ; last control -> al or al,(EN_HSF1 OR EN_HSF2) ; set end of command mov lastcon,al ; save last control out dx,al ; send flag bits setport ECOMMAND mov ax,pcblen out dx,al ; send pcb length sti ; enable interrupts mov ax,CMDCTO ; time out for acceptance call set_timeout pcb_3: call getstat and al,(EN_ASF1 OR EN_ASF2) ; just keep status flags cmp al,1 ; accepted je cmdaccept cmp al,2 je cmdretry call do_timeout jne pcb_3 cmdretry: mov ax,RETRYDELAY ;add retry delay call set_timeout pcb_4: call do_timeout jne pcb_4 jmp pcb_0 cmdaccept: cli mov al,lastcon and al, NOT (EN_HSF1 OR EN_HSF2) ; turn off end of command flag mov lastcon,al ; save last control sti ; enable interrupts loadport setport ECONTROL out dx,al ; pass control byte ret outpcb endp ;*************get status of board*********************** getstat proc near push bx push dx loadport setport ESTATUS stat_1: in al,dx ; get status into al mov bl,al ; save al in al,dx ; get status again cmp al,bl ; same status ? jne stat_1 pop dx pop bx ret getstat endp ; UPDATE BUFFER POINTERS AND ISSUE ANOTHER RECV ; update recv anotherrecv proc near mov ax,rbufct mov crecv_offset,ax inc ax mov rbufct,ax mov ax,10 mov si,offset crecv call outpcb ret anotherrecv endp public recv_exiting recv_exiting: ;called from the recv isr after interrupts have been acknowledged. ;Only ds and ax have been saved. assume ds:nothing ret include timeout.asm ;any code after this will not be kept after initialization. end_resident label byte public usage_msg usage_msg db "usage: 3C505 [-n] [-d] [-w] <packet_int_no> <int_level> <io_addr> <mem_base>",CR,LF,'$' public copyright_msg copyright_msg db "Packet Driver for 3c505, version ",'0'+(majver / 10),'0'+(majver mod 10), ".",CR,LF db "Portions copyright 1989, Krishnan Gopalan & Gregg Stefancik.",CR,LF db "Clemson University Engineering Comp Ops.",CR,LF,'$' no_board_msg: db "3c505 apparently not present at this address.",CR,LF,'$' int_no_name db "Interrupt number ",'$' io_addr_name db "I/O port ",'$' mem_base_name db "Memory address ",'$' extrn set_recv_isr: near ;enter with si -> argument string, di -> word to store. ;if there is no number, don't change the number. extrn get_number: near ;enter with dx -> name of word, di -> dword to print. extrn print_number: near public parse_args parse_args: ;exit with nc if all went well, cy otherwise. mov di,offset int_no call get_number mov di,offset io_addr call get_number mov di,offset mem_base call get_number clc ret public etopen etopen: ;if all is okay, ; reset the 3c505 board takes about 15-20 seconds loadport setport ECONTROL mov al,EN_ATTN OR EN_FLSH_DATA ; reset command out dx,al ; do reset if 0 mov ax,1 call set_timeout reset1: call do_timeout jne reset1 ; wait for reset to propagate else endif mov al,EN_COMMAND_ENABLE ; command interrupt enable mov lastcon,al ; save last command out dx,al ; release reset mov ax,RESDEL ; time to wait call set_timeout reset2: call do_timeout jne reset2 mov ax,RESTO ;this is the long wait. call set_timeout reset3: call getstat and ax, EN_HSF1 OR EN_HSF2 cmp ax,EN_HSF1 OR EN_HSF2 jne resetdone call do_timeout jne reset3 jmp openfail ; open failed resetdone: call set_recv_isr ;install the interrupt handler ;Tell the 3c505 board to receive packets mov si,offset cconc mov ax,4 mov fconc,0 call outpcb mov ax,RESTO call set_timeout open_1: test fconc,0ffh jnz setuprecv call do_timeout jne open_1 ; set up the recv buffers setuprecv: mov rbufct,0 ; clear buffer counter mov crecv_length,1600 ; buffer length,1600 mov crecv_timeout,0 startrecv: mov ax,rbufct ; buffer counter->ax mov crecv_offset,ax ; buf no used as offset inc ax ; count buffer mov rbufct,ax mov ax,10 ; length of pcb mov si,offset crecv ;si->command receive call outpcb ; pass the pcb mov ax,rbufct cmp ax,10 jl startrecv mov al, int_no ; Get board's interrupt vector add al, 8 cmp al, 8+8 ; Is it a slave 8259 interrupt? jb set_int_num ; No. add al, 70h - 8 - 8 ; Map it to the real interrupt. set_int_num: xor ah, ah ; Clear high byte mov int_num, ax ; Set parameter_list int num. mov dx,offset end_resident clc ret public print_parameters print_parameters: mov di,offset int_no mov dx,offset int_no_name call print_number mov di,offset io_addr mov dx,offset io_addr_name call print_number mov di,offset mem_base mov dx,offset mem_base_name call print_number ret ;if we got an error, openfail: stc ret code ends end