;**************************************************************************** ; * ; THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY * ; KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE * ; IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR * ; PURPOSE. * ; * ; Copyright (C) 1993-95 Microsoft Corporation. All Rights Reserved. * ; * ;**************************************************************************** PAGE 58,132 ;****************************************************************************** ; ; Title: SERUTIL.ASM ; ;****************************************************************************** .386p .xlist include VMM.INC include VPICD.INC include VCOMM.INC include DEBUG.INC include OPTTEST.INC include INTERNAL.INC include INS8250.INC .list VxD_My_Pageable_Data_Seg EXTRN Serial_Name:BYTE EXTRN PortInfoHandle:DWORD VxD_My_Pageable_Data_Ends VxD_Locked_Data_Seg EXTRN SysVMHandle:DWORD EXTRN TimeOutHandle:DWORD VxD_Locked_Data_Ends VxD_My_Pageable_Code_Seg ;****************************************************************************** ; ; GetCOMPort ; ; Description: ; Calls VCOMM to acquire the port ; Entry: ; ESI -> PortInformation struct ; Exit: ; NC iff successful ; Uses: ; Flags,EAX,ECX ; ;****************************************************************************** BeginProc GetCOMPort, PUBLIC push edx mov ecx,[esi.ContentionHnd] ; get the contention handler test ecx,ecx jz GCP_Done mov eax,[esi.ContentionRes] ; get the resource test eax,eax jz GCP_Failed cCall ecx, mov [esi.VCD_Data],eax ; save handle GCP_Failed: cmp eax,1 ; CY if 0, else NC. GCP_Done: pop edx ret EndProc GetCOMPort ;****************************************************************************** ; ; ReleaseCOMPort ; ; Description: ; Calls VCOMM to release a com port ; ; Entry: ; ESI -> PortInformation struct ; Exit: ; NONE ; Uses: ; C style ; ;****************************************************************************** BeginProc ReleaseCOMPort,PUBLIC mov ecx,[esi.VCD_Data] jecxz RCP_Done RCP_OwnerShipSet: cCall [esi.ContentionHnd], RCP_Done: ret EndProc ReleaseCOMPort ;****************************************************************************** ; ; StealPort ; ; Description: ; Calls VCOMM to see if we can get the port. ; Entry: ; ESI -> DEB ; Exit: ; NZ if success, else failure ; Uses: ; C style - ECX ;****************************************************************************** BeginProc StealPort,PUBLIC push ecx TestMem [esi.pData.LossByte],1 ; Q: Have we lost the port ? jz SP_Success ; N: cCall [esi.ContentionHnd], test eax,eax ; Q: Did it succeed ? jz SP_Done ; N: ClrFlag [esi.pData.LossByte],1 ; Y: say we own the port SP_Success: test esi,esi ; esi -> Port handle SP_Done: pop ecx ret EndProc StealPort ;**** ; ; WaitForXmitEmpty ; ; Description: ; We need to wait for the transmit register to empty. However, ; with some comm cards, this seems impossible (Doesn't work ; with win3.1 either). Maybe has something to do with using ; the FIFO. Anyway, now we use a timeout of 200 ms for such ; machines so that we don't hang. ; Entry: ; EDX = Line status register. ; Exit: ; None ; Uses: ; Flags. ; BeginProc WaitForXmitEmpty push ecx push eax sti VMMCall Get_Last_Updated_System_Time mov ecx, eax add ecx, 200 ; wait for 200 ms. wfxe_loop: in al, dx ; read line status and al, ACE_THRE+ACE_TSRE cmp al, ACE_THRE+ACE_TSRE je @F VMMCall Get_Last_Updated_System_Time cmp eax, ecx jb wfxe_loop @@: pop eax pop ecx ret EndProc WaitForXmitEmpty ;****************************************************************************** ; ; Terminate ; ; Description: terminate device ; Restore the port I/O address and make sure that interrupts ; are off. ; ; Entry: ; EDX = device port address ; ESI -> PortInformation struct ; ; Exit: ; EAX = 0 => success ; EAX = -1 => error ; Uses: ; EAX,EBX,EDX,EDI,FLAGS. ;============================================================================== BeginProc Terminate, NO_PROLOG, PUBLIC test [esi.pData.LossByte],1 ; Q: Do we have the port ? jz Terminate_SerialOwnsPort ; Y: Continue call StealPort ; N: Try to steal it jz Terminate_LostPort ; Couldn't steal it Terminate_SerialOwnsPort: ; ; ; When the OUT2 bit is reset to 0 to disable interrupts, many ports ; generate an interrupt which can not be identified, because the the ; interrupt ID register will not be set. To work around this hardware ; problem we first mask the IRQ, then set the port into loopback mode ; and output a NULL to generate a receive interrupt request. Then we ; reset OUT2 and unmask the IRQ. This will cause the interrupt to occur ; and the interrupt handler will be able to correctly identify the ; interrupt as coming from the com port. ; ; inc edx ; disable chip interrupts ; .errnz ACE_IER-ACE_RBR-1 ; mov al, ACE_ERBFI ; except receive ; out dx,al ; add dl,ACE_LSR-ACE_IER ; --> line status register ; IO_Delay ; call WaitForXmitEmpty ; xor al,al ; test [esi.EFlags],fFIFOpre ; Q: leave FIFO enable? ; jz @F ; mov al,ACE_TRIG08 OR ACE_EFIFO OR ACE_CRFIFO OR ACE_CTFIFO ;@@: ; sub dl,ACE_LSR-ACE_FCR ; out dx,al ; IO_Delay ; call MaskIRQ ; add dl,ACE_MCR-ACE_FCR ; --> Modem control reg ; in al,dx ; IO_Delay ; mov ah, al ; or al, ACE_LOOP ; turn on loopback ; out dx, al ; IO_Delay ; sub dl, ACE_MCR-ACE_THR ; xor al, al ; out dx, al ; output a NULL to generate an int ; IO_Delay ; add dl, ACE_LSR-ACE_THR ; call WaitForXmitEmpty ; mov al, ah ; dec dl ; now clear OUT2 and loopback ; .errnz ACE_LSR-ACE_MCR-1 ; and al,ACE_DTR+ACE_RTS ; leave DTR,RTS high if already so ; out dx,al ; but tri-state IRQ line ; call UnmaskIRQ ; this will cause the receive int ; to occur and be processed ; sub dl, ACE_MCR-ACE_IER ; clear the receive int enable ;xor al, al ; out dx, al ; dec dl ; .errnz ACE_IER-ACE_RBR-1 ; call MaskIRQ Terminate_LostPort: push ecx ; save these push ebx Terminate47: pop ebx ; original EBX call ReleaseCOMPort pop ecx ; original ECX Terminate50: ; also called from IniCom ??? xor eax,eax ; port closed and deallocated. ret EndProc Terminate ;****************************************************************************** ; ; StrCmp ; ; Entry: ; EAX -> PortInformation struct ; ESI -> PortInfoHandle ; EDI -> Name to look for ; Exit: ; Z if found, else NZ ; Uses: ; Everything except EAX,ESI,EDI ;============================================================================== BeginProc StrCmp, PUBLIC push edi mov edx,[eax.MyName] ; EDX -> Name in the PortInfo struct xor ecx,ecx sc_lp: mov cl,[edx] mov ch,[edi] or ecx,ecx jz sc_success call MyLower xchg cl,ch call MyLower inc edx inc edi cmp cl,ch jz sc_lp sc_failed: or esi,esi ; ESI won't be zero...... sc_success: pop edi ret EndProc StrCmp ;*** ; ; MyLower ; ; Description: ; converts an upper case letter into a lower case one. ; Entry: ; CL = char ; Exit: ; CL = lower cased char ; Uses: ; CL ; BeginProc MyLower,NO_PROLOG cmp cl,'A' jb ml_done cmp cl,'Z' ja ml_done or cl,20h ml_done: ret EndProc MyLower VxD_My_Pageable_Code_Ends VxD_Locked_Code_Seg BeginProc Notify_Loss,CCALL,esp ArgVar refData,DWORD ArgVar fAction,DWORD EnterProc mov eax,RefData mov ecx,fAction or [eax.pData.LossByte],1 ; assume loss of port jecxz NL_Losing and [eax.pData.LossByte],NOT 1 NL_Losing: LeaveProc return EndProc Notify_Loss ;*** ; ; MaskIRQ ; ; Description: masks irq for the com port. ; ; Entry: ESI -> Portinformation struct ; Exit: ; AL = 0, if was unmasked, else -1 if already masked. ; Uses: BeginProc MaskIRQ, NO_PROLOG, PUBLIC push ebx push ecx mov eax,[esi.IRQHandle] or eax,eax jz MaskIRQ_Done ; handle doesn't exist mov ebx,[SysVMHandle] VxDCall VPICD_Get_Complete_Status ; get the status mov al,-1 ; assume masked test cl,0100b ; is IRQ physically masked ? jnz MaskIRQ_Done ; yes, get out mov eax,[esi.IRQHandle] VxDCall VPICD_Physically_Mask xor al,al ; was not masked. MaskIRQ_Done: pop ecx pop ebx ret EndProc MaskIRQ ;*** ; ; UnMaskIRQ ; ; Description: Unmasks the irq of the port. ; ; Entry: ESI -> PortInformation struct ; Exit: None. ; Uses: ; BeginProc UnMaskIRQ, NO_PROLOG, PUBLIC push eax mov eax,[esi.IRQHandle] or eax,eax jz UnMaskIRQ_Done VxDCall VPICD_Physically_Unmask UnMaskIRQ_Done: pop eax ret EndProc UnMaskIRQ ;*** ; ; KickTx ; ; Descirption: "Kick" the xmitter interrupt routine into operation. ; If the transmitter holding register isn't empty, then ; nothing needs to be done. If it is empty, then the xmit ; interrupt needs to be enabled in the IER. ; ; Entry: ESI-> PortInformation struct, ; interrupts are disabled. ; ; Exit: None. ; ; Uses: EAX,EDX,flags ; BeginProc KickTx,Public test [esi.pData.LossByte],1 ; Q: Do we still own the port ? jnz can_we_steal ; N: enable_int: mov edx,[esi.Port] ; get device I/O address add dl,ACE_IER ; --> interrupt enable register in al,dx ; get current IER state test al,ACE_ETBEI ; interrupts already enabled? jnz KickTx10 ; yes, don't re-enable it or al,ACE_ETBEI ; No, enable it out dx,al ; 8250, 8250-B requires IO_Delay out dx,al ; writing register twice. KickTx10: ret can_we_steal: call StealPort ; call VCOMM to see if we can steal ; the port back jnz enable_int ; jump, if we got it TRAP ; ; flush out the queue ; xor eax,eax mov [esi.pData.QOutCount],eax mov [esi.pData.QOutMod],eax mov eax,[esi.pData.QOutGet] mov [esi.pData.QOutPut],eax ret EndProc KickTx ;*** ; ; MSRWait ; ; Description: This routine checks the modem status register for CTS,DSR, ; and/or RLSD signals. If a timeout occurs while checking, ; the appropriate error code will be returned. ; ; This routine will not check for any signal with a corresponding ; time out value of 0 (ignore line). ; Entry: ; ESI -> PortInformation struct ; Exit: ; AL = error code, [esi.pData.dwCommError] updated, ; 'Z' set if no timeout ; Uses: ; EAX,ECX,EDX,Flags. ; BeginProc MSRWait, PUBLIC push edi MSRRestart: xor edi,edi ; init timer MSRWait10: mov ecx,11 ; init delay counter (used on non-ATs) MSRWait20: xor dh,dh ; init error accumulator mov eax,[esi.AddrMsrShadow] ; get address of MSR shadow mov al,[eax] ; get modem status and al,[esi.MSRMask] ; only leave bits of interest. xor al,[esi.MSRMask] ; 0 = line high jz MSRWait90 ; All lines of interest are high mov ah,al ; AH has 1 bits for down lines shl ah,1 ; line signal detect low? jnc MSRWait30 ; No, it is high .errnz ACE_RLSD-10000000b cmp edi,[esi.ComDCB.RlsTimeout]; RLSD timeout yet? jb MSRWait30 ; NO or dh,CE_RLSDTO ; show modem status timeout MSRWait30: shl ah,1 ; data set ready low shl ah,1 .errnz ACE_DSR-00100000b jnc MSRWait40 ; No, it's high cmp edi,[esi.ComDCB.DsrTimeOut]; DSR timout yet ? jb MSRWait40 ; NO or dh,CE_DSRTO ; show data set ready timeout MSRWait40: shl ah,1 ; CTS low? jnc MSRWait50 ; No, it's high .errnz ACE_CTS-00010000b cmp edi,[esi.ComDCB.CtsTimeout] ; cts timeout yet? jb MSRWait50 ; NO or dh,CE_CTSTO ; show clear to send timeout MSRWait50: or dh,dh ; any timeout yet? jnz MSRWait80 ; Yes loop MSRWait20 ; continue until timeout ; should have taken about 1ms. inc edi ; timer+1 jmp MSRWait10 ; until timeout or good status MSRWait80: xor ah,ah mov al,dh or BYTE PTR [esi.pData.dwCommError],al ; return updated status .errnz HIGH CE_CTSTO .errnz HIGH CE_DSRTO .errnz HIGH CE_RLSDTO MSRWait90: or al,al ; set 'Z' if no timeout pop edi ret EndProc MSRWait ;****************************************************************************** ; ; ExtCom_FN1 ; ; Description: ; similar to receiving an X-OFF char. Buffered xmission of ; chars is halted until and X-ON char is received, or until ; we fake that with a clear X-Off call. ; Entry: ; ESI -> PortInformation struct ; EDX = port base, interrupts off. ; Exit: ; CLC. ; Uses: ; Flags ;============================================================================== BeginProc ExtCom_FN1, PUBLIC or [esi.HSFlag],XOffReceived PUBLIC ExtComDummy ExtComDummy: clc ret EndProc ExtCom_FN1 ;****************************************************************************** ; ; ExtCom_FN2 ; ; Description: ; Similar to receiving X-Off char. Buffered xmission is ; restarted. ; Entry: ; ESI -> PortInformation struct ; EDX = port base, interrupts disabled. ; Exit: ; CLC ; Uses: ; Same as KickTx + flags. ;============================================================================== BeginProc ExtCom_FN2, PUBLIC and [esi.HSFlag],NOT XoffReceived call KickTx ; kick transmitter interrupts on clc ret EndProc ExtCom_FN2 ;****************************************************************************** ; ; ExtCom_FN3 ; ; Description: ; Set the RTS signal active ; Entry: ; ESI --> PortInformation struct ; EDX = port base, interrupts OFF ; Exit: ; CLC ; Uses: ; EAX,EDX ;============================================================================== BeginProc ExtCom_FN3, PUBLIC ; add dl,ACE_MCR ; --> modem control reg ; in al,dx ; get current settings ; or al,ACE_RTS ; set RTS ; IO_Delay ; out dx,al ; and update clc ret EndProc ExtCom_FN3 ;****************************************************************************** ; ; ExtCom_FN4 ; ; Description: ; Set the RTS signal INACTIVE. ; Entry: ; ESI --> PortInformation struct ; EDX = port base, interrupts off ; Exit: ; CLC ; Uses: ; EDX,EAX ;============================================================================== BeginProc ExtCom_FN4, PUBLIC ; add dl,ACE_MCR ; --> modem control reg ; in al,dx ; get current settings ; and al,NOT ACE_RTS ; clear RTS ; IO_Delay ; out dx,al ; update clc ret EndProc ExtCom_FN4 ;****************************************************************************** ; ; ExtCom_FN5 ; ; Description: ; Set DTR ; Entry: ; ESI -> PortInformation struct ; EDX = port base, interrupts off ; Exit: ; CLC ; Uses: ; EAX,EDX ;============================================================================== BeginProc ExtCom_FN5, PUBLIC ; add dl,ACE_MCR ; --> modem control reg ; in al,dx ; current settings ; or al,ACE_DTR ; set DTR ; IO_Delay ; out dx,al ; update clc ret EndProc ExtCom_FN5 ;****************************************************************************** ; ; ExtCom_FN6 ; ; Description: ; Clear DTR signal ; Entry: ; ESI -> PortInformation struct ; EDX = port base, interrupts off ; Exit: ; CLC ; Uses: ; EAX,EDX ;============================================================================== BeginProc ExtCom_FN6, PUBLIC ; add dl,ACE_MCR ; --> Modem control reg ; in al,dx ; current settings ; and al,NOT ACE_DTR ; turn it off ; IO_delay ; out dx,al ; update SaveReg Debug_Printf "BPQVC ExtnFcn 6" RestoreReg clc ret EndProc ExtCom_FN6 ;****************************************************************************** ; ; ExtCom_FN7 ; ; Description: ; Assert the RESET line on an LPT port, useless for us. ; Entry: ; ESI -> PortInformation struct ; EDX = port base, interrupts off ; Exit: ; CLC ; Uses: ; None. ;============================================================================== BeginProc ExtCom_FN7, PUBLIC sti clc ret EndProc ExtCom_FN7 ;****************************************************************************** ; ; ExtCom_FN10 ; ; Description: ; Get COM port base and IRQ ; Entry: ; EDX = port base ; ESI -> PortInformation struct ; Exit: ; EAX = (IRQ,base) ; Uses: ; FLAGS,eax,edx ;============================================================================== BeginProc ExtCom_FN10, PUBLIC movzx eax,[esi.IRQn] shl eax,16 mov ax,WORD PTR [esi.Port] stc ret EndProc ExtCom_FN10 ;****************************************************************************** ; ; Notify_Owner: ; ; Description: notifies owner of events ; ; Entry: EAX = message ID ; ESI -> PortInformation ; Exit: ; None ; Uses: ; None. ;============================================================================== BeginProc Notify_Owner, PUBLIC SaveReg or [esi.NotifyFlagsHI],al ; AL has notifications to send mov ebx,[esi.AddrEvtDWord] push dword ptr [ebx] ; sub-event push eax ; event cmp eax,CN_EVENT jne NO_NotEvent push [esi.ReferenceData] ; reference data push esi ; hPort call [esi.NotifyHandle] jmp NO_Done NO_NotEvent: cmp eax,CN_RECEIVE jne NO_NotReceive push [esi.ReadNotifyRefData] ; reference data push esi ; hPort call [esi.ReadNotifyHandle] SaveReg Debug_Printf "BPQVC Notify_Owner CN_RECEIVE" RestoreReg jmp NO_Done NO_NotReceive: cmp eax,CN_TRANSMIT jne NO_Done push [esi.WriteNotifyRefData]; reference data push esi ; hPort call [esi.WriteNotifyHandle] SaveReg Debug_Printf "BPQVC Notify_Owner CN_TRANSMIT" RestoreReg NO_Done: add esp,16 ; clear stack RestoreReg ret EndProc Notify_Owner ;****************************************************************************** ; ; TimerProc ; ; Description: ; Checks if any port is active. If a port owner wants a receive ; notification and none has been sent and at least one byte is in the ; queue, then it will call Notify_Owner for that port ; ; Entry: ; EDX -> PortInfoHandle ; Exit: ; None ; Uses: ; ALL ;============================================================================== BeginProc TimerProc, PUBLIC mov [TimeOutHandle],0 ; reset timer handle mov esi,edx ; ESI -> PortinfoHandle VMMCall List_Get_First TP_Lp: jz TP_Done ; end of list ; TestMem [eax.MiscFlags],ClrTimer ; Q: Owner wants timer notifications? ; jnz TP_Next ; N: cmp [eax.RecvTrigger],-1 ; Q: Owner wants notification? je TP_Next ; N: skip notify cmp [eax.pData.QInCount],0 ; Q: anything in input queue ? je TP_Next ; N: skip notify test [eax.NotifyFlagsHI],CN_RECEIVE ;Q: timeout notify sent ? jnz TP_Next ; N: skip notify xor [eax.NotifyFlagsHI],CN_IDLE ; Q: first timer call ? js TP_Next ; Y: skip notify push eax push esi mov esi,eax ; ESI -> PortInformation struct mov eax,CN_RECEIVE ; message id. call Notify_Owner pop esi pop eax TP_Next: VMMCall List_Get_Next jmp TP_Lp TP_Done: mov eax,100 mov edx,esi ; RefData = PortInfoHandle mov esi,OFFSET32 TimerProc VMMCall Set_Global_Time_Out mov [TimeOutHandle],esi ; save timer handle ret EndProc TimerProc VxD_Locked_Code_Ends end