/* semfisdd.c */ /* Device Driver for IBM SDLC adapter. */ #ifndef IMADRIVER #error - driver code requires IMADRIVER be defined on command line #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Temporary Usage : function prototype declaration should be pulled in extern NTSTATUS ZwOpenKey( OUT PHANDLE KeyHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes ); /* semfiddd.c - NT Device Driver for IBM SDLC adapter */ /**PROC+**********************************************************************/ /* */ /* Name AdapterExistenceCheck ( */ /* PCONFIGDATA pConfigData */ /* ); */ /* */ /* Purpose Hardware-specific of initialising */ /* */ /* Params IN pConfigData: the config data record for adapter to be checked*/ /* */ /* Return Value BOOLean: TRUE if the adapter is present and responding */ /* */ /* Side Effect: For MPAA, sets dma channel in config data! This will then */ /* immediately be copied to the Device Object config data ( so */ /* no danger of it being used by another configdata record in */ /* error. */ /* */ /* Operation: Tries to read ports confirming the presence of the adapter */ /* This is a trivial check of one port, suitable for start of */ /* day checks just to tell us if the device should be created. */ /* Real hardware initialisation is done on open. */ /* */ /* Notes We do as little as we can get away with here because a) we */ /* don't have a full device extension set up and b) to avoid */ /* causing interrupts hopefully. */ /* */ /**PROC-**********************************************************************/ BOOLean AdapterExistenceCheck ( PCONFIGDATA pConfigData ) { BOOLean FoundMPA; if (pConfigData->AdapterType EQ AT_SDLC || pConfigData->AdapterType EQ AT_MPCA1 || pConfigData->AdapterType EQ AT_MPCA2) { if (pConfigData->MPCAModePort NE 0) { /***********************************************************************/ /* This is an MPCA adapter, we hope. Disable and reenable it */ /***********************************************************************/ WR_N_DELAY (pConfigData->MPCAModePort, AC_MPCAD); WR_N_DELAY (pConfigData->MPCAModePort, pConfigData->MPCAModeValue); TRACE_DATABYTE (Mpc, pConfigData->MPCAModeValue); } /*************************************************************************/ /* There isn't a reliable way of determining if the MPCA/SDLC cards */ /* are present. So at this stage say they are and let non-existence */ /* show up later as an Open error. */ /*************************************************************************/ return (TRUE); } if (pConfigData->AdapterType EQ AT_MPAA1 || pConfigData->AdapterType EQ AT_MPAA2) { IO_ADDRESS AdapterSelector; for ( FoundMPA = FALSE, AdapterSelector = (IO_ADDRESS) 8; AdapterSelector < (IO_ADDRESS) 0x10; AdapterSelector++ ) { WR_N_DELAY (POS_Adapter_Select, (UCHAR) AdapterSelector); if (IO_IN (POS_AdapterID_HiByte) EQ HIBYTE(POS_MPAATypeID) && IO_IN (POS_AdapterID_LoByte) EQ LOBYTE(POS_MPAATypeID) && ((UCHAR) (IO_IN(POS_ConfigByte2) & 0x1F) ) EQ pConfigData->MPAAAdapterIdentifier) { FoundMPA = TRUE; pConfigData->DMAChannel = (UCHAR) (IO_IN (POS_ConfigByte3) & 7); break; } } WR_N_DELAY (POS_Adapter_Select, 7); return (FoundMPA); } } /*****************************************************************************/ /* */ /* Name AdapterReset */ /* */ /* Purpose Reset an adapter and prime it */ /* */ /* Params IN pDX */ /* */ /* OUT None */ /* */ /* Modified: 31/03/88 Initial coding */ /* */ /*****************************************************************************/ void AdapterReset (PDX pDX) { TRACE_EVENT (HardwareError = FALSE; /* if user screwed up on hardware */ /* previously, we'll let him try */ /* again this time */ /***************************************************************************/ /* If an MPCA has been configured then write out the mode set byte. */ /***************************************************************************/ if (pDX->ConfigData.MPCAModePort NE 0) { WR_N_DELAY (pDX->ConfigData.MPCAModePort, pDX->ConfigData.MPCAModeValue); TRACE_DATABYTE (Mpc, pDX->ConfigData.MPCAModeValue); } /***************************************************************************/ /* Now mode set the 8255. */ /***************************************************************************/ WR_N_DELAY (pDX->ADAPTERBASE+AR_8255, A55_ModeSet); IO_OUT (pDX->ADAPTERBASE+AR_8255B,A55_Reset8273On); KeStallExecutionProcessor (10L); /* a long delay for reset on */ WR_N_DELAY (pDX->ADAPTERBASE+AR_8255B,A55_Reset8273Off); IO_OUT (pDX->ADAPTERBASE+AR_8255C, A55_InitPortC); TRACE_EVENT (ARs>); } /**PROC+**********************************************************************/ /* */ /* Name AllocateDMAMemory ( */ /* int BufferSize */ /* PVOID *pBufferPtr */ /* PMDL *pMdl */ /* PHYSICAL_A *pPhysAddr */ /* ULONG *ErrorInformation */ /* ); */ /* */ /* Purpose Modularise the steps to allocate/deallocate and check DMA mem*/ /* Also, make freeing up easy (like, free everything on failure)*/ /* */ /* Params IN BufferSize */ /* OUT BufferPtr pointer to actual memory */ /* OUT pMdl the allocated mdl */ /* OUT pPhysAddr physical memory's address */ /* OUT ErrorInformation: suitable for IoStatus.Information */ /* */ /* Return Value BOOLean: TRUE if everything allocated OK */ /* */ /**PROC-**********************************************************************/ BOOLean AllocateDMAMemory ( ULONG BufferSize, PVOID *pBufferPtr, PMDL *ppMdl, PHYSICAL_ADDRESS * pPhysAddr, ULONG *ErrorInformation ) { BOOLean ReturnCode = TRUE; TRACE_EVENT (LowPart | pPhysAddr->HighPart) NE 0L); /* check our understanding of MmAllocateContiguousMemory */ ASSERT (BITSOFF(pPhysAddr->LowPart, 0xFF000000L)); if (DMACrosses64K(pPhysAddr->LowPart, BufferSize)) { ReturnCode = FALSE; *ErrorInformation = IO_ERR_DMA_BUFFER_UNUSABLE; } } if (!ReturnCode) { if (*ppMdl NE NULL) { IoFreeMdl (*ppMdl); *ppMdl = NULL; } if (*pBufferPtr NE NULL) { MmFreeContiguousMemory (*pBufferPtr); *pBufferPtr = NULL; } } TRACE_EVENT (Dma>); return (ReturnCode); } /**PROC+**********************************************************************/ /* */ /* Name: Close8273Sequence (PDX pDX) */ /* */ /* Purpose: To gracefully close down the 8273 */ /* */ /* Params: IN pDX */ /* */ /* Implicit input: Must not be called at interrupt or Synchronised level!!! */ /* */ /* Return Value:none */ /* */ /**PROC-**********************************************************************/ void Close8273Sequence (PDX pDX) { TRACE_EVENT (Interrupt, SynchReset8273, (PVOID) pDX); /*************************************************************************/ /* Drop back down to non-interrupt level to allow pending interrupts in. */ /* The SynchReset8273 call above sets the Closing flag */ /*************************************************************************/ KeSynchronizeExecution(pDX->Interrupt, SynchTerminateAdapter, (PVOID) pDX); pDX->AdapterIsClosing = FALSE; TRACE_EVENT (C73>); } /**PROC+**********************************************************************/ /* */ /* Name: CompleteIoRequest (PIRP pIrp) */ /* */ /* Purpose: The Io request must be completed for each entry point. */ /* */ /* Params: IN pIrp The Io Request to be completed */ /* */ /* Return Value:none */ /* */ /* Operation: The Io request must be completed at despatch level */ /* */ /**PROC-**********************************************************************/ void CompleteIoRequest (PIRP pIrp) { /***************************************************************************/ /* IoCompleteRequest used to need a RaiseIrql ... but no longer does. */ /* Keep this as a subroutine for now - could become a macro later. */ /***************************************************************************/ //KIRQL PreviousIrql; //KeRaiseIrql (DISPATCH_LEVEL, &PreviousIrql); IoCompleteRequest (pIrp, 0); /* 0 => no priority boost for APC */ //KeLowerIrql (PreviousIrql); TRACE_EVENT (S+I:); TRACE_DWORD (pIrp->IoStatus.Status); TRACE_DWORD (pIrp->IoStatus.Information); } /**PROC+**********************************************************************/ /* */ /* Name: DeviceInit ( */ /* PDRIVER_OBJECT DriverObject */ /* CHAR * DeviceName, */ /* PCONFIGDATA pConfigData, */ /* ) */ /* */ /* Purpose: Initialise the next device (from GetDriverSpec) */ /* Called for each configured device from DriverEntry. */ /* */ /* Params: IN DriverObject: Us! */ /* IN DeviceName: COMDL$0x (from GetDriverSpec()) */ /* IN pConfigData: config data to use */ /* */ /* Return Value:BOOLean: TRUE if device successfully initialised */ /* */ /* Operation: The general principle is: do as little as possible to here - */ /* just establish the device exists and allocate a device */ /* object for it. Any allocation of resources is done on open */ /* to avoid using resources unnecessarily. This applies to */ /* interrupt objects, */ /* */ /* 0. On input, driver and flavour name read from cfg register */ /* */ /* 1. If */ /* |--> HardwareExistenceCheck */ /* fails, */ /* return failure */ /* */ /* 2. Init for NT */ /* - allocate DeviceObject */ /* - DO_DIRECT_IO */ /* - IoInitializeDpcRequest */ /* */ /* 3. Init the device extension fields we need to know now: */ /* - DeviceIsOpen = FALSE */ /* - pointer to config data */ /* */ /* Note: The parallel driver returns an NT status from an equivalent */ /* routine. Eventually, we may do the same via ErrorCode param.*/ /* But for now we don't set error code, because the NTSTATUS */ /* returned from the parallel driver routine is ignored. */ /* */ /**PROC-**********************************************************************/ BOOLean DeviceInit ( PDRIVER_OBJECT pDriverObject, CHAR * DeviceName, PCONFIGDATA pConfigData ) { STRING NameString; /* counted string for NT */ PDEVICE_OBJECT pDeviceObject; /* allocated by NT for our device */ PDX pDX; /* pointer to our device extension */ NTSTATUS Status; UNICODE_STRING UniNameString; /* same thing in UniCode */ if (!AdapterExistenceCheck(pConfigData)) { TRACE_EVENT (DiNa); /* DeviceInit: No adapter */ DEBUG_PRINT (("IBMSYNC: Existence check failed for Adapter %s\n", pConfigData->FlavourName)); return (FALSE); /* no device so can't initialise devc*/ } RtlInitString (&NameString, DeviceName); Status = RtlAnsiStringToUnicodeString (&UniNameString, &NameString, TRUE); ASSERT(NT_SUCCESS(Status)); /* UniNameString now has unicode dvnm*/ Status = IoCreateDevice( pDriverObject, sizeof( IBMSYNC_DEVICE_EXTENSION ), &UniNameString, FILE_DEVICE_DATALINK, 0, TRUE, /* open exclusively */ &pDeviceObject ); RtlFreeUnicodeString (&UniNameString); /***************************************************************************/ /* Note that we cannot log an error here since we do not hace a Device */ /* Object. */ /***************************************************************************/ if (!NT_SUCCESS(Status)) { TRACE_EVENT (DiNd); /* DeviceInit: No device */ DEBUG_PRINT (("IBMSYNC: Cannot create Device : %s\n", DeviceName)); return (FALSE); } /***************************************************************************/ /* We have a device object OK - which is the main thing to do at device */ /* init time. Other initialisation stuff we leave till device opened. */ /***************************************************************************/ pDeviceObject->Flags |=DO_DIRECT_IO; pDX = pDeviceObject->DeviceExtension; pDX->DeviceIsOpen = FALSE; pDX->AdapterIsClosing = FALSE; pDX->PowerFailed = FALSE; /***************************************************************************/ /* In theory, we don't have to do this config data copying (we could just */ /* use a pointer to it, but that would be a) slower; b) tedious to code */ /***************************************************************************/ pDX->pDeviceObject = pDeviceObject; pDX->ConfigData = *pConfigData; pDX->Name[0] = 'A'; /* adapter */ pDX->Name[1] = pConfigData->FlavourName[4]; IoInitializeDpcRequest(pDeviceObject, DPCRoutine); DEBUG_PRINT (("IBMSYNC: Created Device : %s\n", DeviceName)); return (TRUE); } /**PROC+**********************************************************************/ /* */ /* Name: DPCRoutine */ /* */ /* Purpose: */ /* */ /* Params: */ /* */ /* Return Value:None */ /* */ /**PROC-**********************************************************************/ VOID DPCRoutine( IN PKDPC pDpc, IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp, IN PVOID DeferredContext ) { PDX pDX = pDeviceObject->DeviceExtension; TRACE_EVENT (DeviceIsOpen); if (pDX->DeviceIsOpen && pDX->DPCAction & DPC_ACTION_PULSE && pDX->pUserEvent NE NULL) { // KePulseEvent (pDX->pUserEvent, 0, FALSE); /* 0 pri boost, no wait */ // KePulseEvent missing from API? KeSetEvent (pDX->pUserEvent, 0, FALSE); } pDX->DPCAction = 0; /* we've done all requested actions */ TRACE_EVENT (DPC>); } /**PROC+**********************************************************************/ /* */ /* Name: DriverEntry( */ /* PDRIVER_OBJECT DriverObject */ /* IN PUNICODE_STRING RegistryPath */ /* ) */ /* */ /* Purpose: As defined by NT. Called once only when NT loads driver */ /* */ /* Params: PDRIVER_OBJECT: pointer to us */ /* */ /* Return Value:NTSTATUS: STATUS_SUCCESS / STATUS_UNSUCCESSFUL */ /* Unsuccessful if no devices initialised */ /* */ /**PROC-**********************************************************************/ NTSTATUS DriverEntry (IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath ) { CHAR DeviceName [80]; BOOLean GotADevice = FALSE; NTSTATUS RetStatus = STATUS_UNSUCCESSFUL; CONFIGDATA *pConfigData; // _asm int 3; TRACE_INIT(); TRACE_EVENT(Entr); while (GetDriverSpec (DeviceName, &pConfigData)) { if (DeviceInit (DriverObject, DeviceName, pConfigData)) { GotADevice = TRUE; TRACE_EVENT (DevI); } } if ((GotADevice) && (GetInterfaceType (DriverObject))) { DriverObject->MajorFunction[IRP_MJ_CLOSE] = EntryPointClose; DriverObject->MajorFunction[IRP_MJ_CREATE] = EntryPointOpen; DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = EntryPointDevIoctl; DriverObject->DriverUnload = EntryPointUnload; RetStatus = STATUS_SUCCESS; } else { TRACE_EVENT (DrvF); } return (RetStatus); } /*****************************************************************************/ /* */ /* Name EntryPointClose */ /* */ /* Purpose Close Request Packet Handler */ /* */ /* This is the CLOSE processor. It flushes pending requests */ /* and ensures that the hardware and LCB fields are tidily */ /* closed down. */ /* */ /* Params IN None */ /* */ /* OUT Link hardware closed and pending requests flushed. */ /* */ /*****************************************************************************/ NTSTATUS EntryPointClose ( PDEVICE_OBJECT pDeviceObject, PIRP pIrp ) { PDX pDX = pDeviceObject->DeviceExtension; NTSTATUS Status; TRACE_NEWLINE(); TRACE_EVENT ({EPC); pIrp->IoStatus.Information = 0; if (pDX->DeviceIsOpen) /* use can close OK */ { pIrp->IoStatus.Status = STATUS_SUCCESS; /*************************************************************************/ /* Clear down the transmitter and receiver and release the h/w. */ /*************************************************************************/ Close8273Sequence (pDX); /* orderly close-down of adapter */ IoDisconnectInterrupt (pDX->Interrupt); if (pDX->ConfigData.Irql2) { IoDisconnectInterrupt (pDX->Interrupt2); } TRACE_EVENT (EPC1); MmUnlockPages (pDX->RcvInfo.pRcvMdl); TRACE_EVENT (EPC2); IoFreeMdl (pDX->RcvInfo.pRcvMdl); TRACE_EVENT (EPC3); MmFreeContiguousMemory (pDX->RcvInfo.pRcvBufArray); TRACE_EVENT (EPC4); MmUnlockPages (pDX->pSendMdl); TRACE_EVENT (EPC5); IoFreeMdl (pDX->pSendMdl); TRACE_EVENT (EPC6); pDX->pSendMdl = NULL; MmFreeContiguousMemory (pDX->pSendBuf); TRACE_EVENT (EPC7); pDX->pSendBuf = NULL; TRACE_EVENT (EPC8); // if (pDX->pIRMdl != NULL) /*IRMdl?*/ // { /*IRMdl?*/ // MmUnmapLockedPages (pDX->pIR, pDX->pIRMdl); /*IRMdl?*/ // MmUnlockPages (pDX->pIRMdl); /*IRMdl?*/ // TRACE_EVENT (EPC9); /*IRMdl?*/ // IoFreeMdl (pDX->pIRMdl); /*IRMdl?*/ // TRACE_EVENT (EPC0); /*IRMdl?*/ // pDX->pIRMdl = NULL; /*IRMdl?*/ // } /*IRMdl?*/ pDX->pIR = &pDX->OurIR; pDX->GrabbedResources = 0; pDX->DeviceIsOpen = FALSE; pDX->pUserEvent = NULL; } else { pIrp->IoStatus.Status = STATUS_FILE_CLOSED; } Status = pIrp->IoStatus.Status; CompleteIoRequest (pIrp); TRACE_EVENT (EPC}); return (Status); } /**PROC+**********************************************************************/ /* */ /* Name: EntryPointDevIoctl ( */ /* PDEVICE_OBJECT pDeviceObject */ /* PIRP pIrp */ /* ) */ /* */ /* Purpose: Main ioctl entry point. */ /* */ /* Params: IN DeviceObject: Our device. */ /* IN PIrp The IRP in question. */ /* */ /* Return Value:depends on called routine */ /* */ /* Operation: */ /* */ /**PROC-**********************************************************************/ NTSTATUS EntryPointDevIoctl ( PDEVICE_OBJECT pDeviceObject, PIRP pIrp ) { BOOLean OKToContinue = TRUE; PDX pDX = pDeviceObject->DeviceExtension; PIO_STACK_LOCATION pIrpSp = IoGetCurrentIrpStackLocation(pIrp); UCHAR * UserBufferPtr; #ifdef IBMSYNC_TRACE UCHAR * OriginalTrcPtr = TrcPtr; BOOLEAN ResetTrcPtr = FALSE; #endif ASSERT (pDX->DeviceIsOpen); /* open before any Ioctls please */ TRACE_NEWLINE(); TRACE_EVENT({EPD); TRACE_DWORD (pDeviceObject); TRACE_DWORD (pDX); TRACE_DWORD (pIrp); TRACE_DWORD (pIrpSp->IRS_CODE); TRACE_ACTION (Op,CAST(pDX->ADAPTERBASE,ULONG) & 0xFFFF); pDX->IoctlRetStatus = STATUS_SUCCESS; pDX->Information = 0L; pDX->IoctlCurrentIrp= pIrp; /***************************************************************************/ /* The subroutines return the success/fail indication as their return */ /* value, and additional information in pDX->Information */ /***************************************************************************/ switch (pIrpSp->IRS_CODE) { // case IoctlCodeSetInterfaceRecord /*IRMdl?*/ // : IoctlSetInterfaceRecord(pDX); /*IRMdl?*/ // break; /*IRMdl?*/ case IoctlCodeReadInterfaceRecord /*IRMdl?*/ : if (pIrpSp->IRS_OUTLEN NE sizeof(IR)) { pDX->IoctlRetStatus = STATUS_INVALID_PARAMETER; pDX->Information = IO_ERR_READ_IR_BUFFER_WRONG_SIZE; } else { #ifdef IBMSYNC_TRACE ResetTrcPtr = TRUE; #endif UserBufferPtr = MmMapLockedPages (pIrp->MdlAddress, KernelMode); memcpy (UserBufferPtr, pDX->pIR, sizeof(IR)); } break; case IoctlCodeSetEvent : if (pIrp->UserEvent NE NULL) { pDX->pUserEvent = pIrp->UserEvent; KeSetEvent (pDX->pUserEvent, 0, FALSE); } else { pDX->IoctlRetStatus = STATUS_INVALID_PARAMETER; pDX->Information = IO_ERR_SET_EVENT_NO_EVENT; } break; case IoctlCodeSetLinkChar : KeSynchronizeExecution ( pDX->Interrupt, (PKSYNCHRONIZE_ROUTINE) IoctlSetLinkConfig, (PVOID) pDX ); break; case IoctlCodeRxFrame : KeSynchronizeExecution ( pDX->Interrupt, (PKSYNCHRONIZE_ROUTINE) IoctlRxFrame, (PVOID) pDX ); break; case IoctlCodeAbortReceiver : IoctlAbortReceiver (pDX); break; case IoctlCodeGetV24 : KeSynchronizeExecution ( pDX->Interrupt, (PKSYNCHRONIZE_ROUTINE) GetV24Input, (PVOID) pDX ); /* data-> pDX->IR; no return status */ #ifdef IBMSYNC_TRACE { static UCHAR LastV24In = 0; ResetTrcPtr = CAST( pDX->pIR->V24In EQ LastV24In, BOOLEAN); LastV24In = pDX->pIR->V24In; } #endif break; case IoctlCodeTxFrame : KeSynchronizeExecution ( pDX->Interrupt, (PKSYNCHRONIZE_ROUTINE) IoctlTxFrame, (PVOID) pDX ); break; case IoctlCodeAbortTransmit : KeSynchronizeExecution ( pDX->Interrupt, (PKSYNCHRONIZE_ROUTINE) IoctlAbortTransmitter, (PVOID) pDX ); break; case IoctlCodeSetV24 : if (!KeSynchronizeExecution ( pDX->Interrupt, (PKSYNCHRONIZE_ROUTINE) IoctlSetV24Output, (PVOID) pDX ) ) { pDX->IoctlRetStatus = STATUS_DATA_ERROR; pDX->Information = IO_ERR_HARDWARE_CMD_TIMEOUT_1; } break; default: // _asm int 3; // break into kernel debugger pDX->IoctlRetStatus = STATUS_INVALID_DEVICE_REQUEST; pDX->Information = IO_ERR_INVALID_IOCTL_CODE; } if (NT_SUCCESS(pDX->IoctlRetStatus)) { // The Ioctl worked OK, but how about 8273 commands - check the // timeout flag, which is set by 8273 WAITUNTIL macro. Signal a fatal // hardware error if it's set. if (pDX->HardwareError) { pDX->IoctlRetStatus = STATUS_DATA_ERROR; pDX->Information = IO_ERR_HARDWARE_CMD_TIMEOUT_2; } } else { TRACE_NTFAILURE(pDX->IoctlRetStatus); TRACE_ACTION (Ii,pDX->Information & 0xFFFF); } /***************************************************************************/ /* Note that at this point a non-zero value of pDX->Information does not */ /* mean an error - it could be a byte count of info returned in a buffer. */ /***************************************************************************/ if (!NT_SUCCESS(pDX->IoctlRetStatus)) { LogDriverError( pDeviceObject, pDX->IoctlRetStatus, pDX->Information, pIrpSp->MajorFunction, pIrpSp->IRS_CODE ); } pIrp->IoStatus.Status = pDX->IoctlRetStatus; pIrp->IoStatus.Information = pDX->Information; TRACE_EVENT(EPD}); CompleteIoRequest (pIrp); #ifdef IBMSYNC_TRACE if (ResetTrcPtr) TrcPtr = OriginalTrcPtr; #endif return(pDX->IoctlRetStatus); } /**PROC+**********************************************************************/ /* */ /* Name: EntryPointISR ( */ /* PKINTERRUPT pInterrupt */ /* PVOID Context = pDeviceObject */ /* ) */ /* */ /* Purpose: Handle interrupt. */ /* */ /* Params: IN pInterrupt */ /* IN Context */ /* */ /* Return Value:BOOLean: TRUE if device successfully initialised */ /* */ /* Operation: */ /* */ /* Notes 1. The OS/2 device driver shares interrupts; this version does */ /* not because the interrupt object needs to be attached to a */ /* device. I hope we can get away it. */ /* 2. Return value is meant to indicate some powerfail stuff ... */ /* not bothering with it for now. */ /* */ /**PROC-**********************************************************************/ BOOLEAN EntryPointISR ( PKINTERRUPT pInterrupt, PVOID Context ) { PDEVICE_OBJECT pDeviceObject= (PDEVICE_OBJECT) Context; PDX pDX = pDeviceObject->DeviceExtension; BOOLean OriginalDPCAction = pDX->DPCAction; UCHAR Status; /* the 8273 status, not the NT Status*/ BOOLEAN ReturnValue; UNREFERENCED_PARAMETER (pInterrupt); //TRACE_NEWLINE(); //TRACE_EVENT (DeviceIsOpen); if (pDX->AdapterIsClosing || !pDX->DeviceIsOpen) { // We reset the 8273 but there was an interrupt still pending... ignore it. // This is safe because by resetting the 8273 we should have removed the // cause of the interrupt. return(TRUE); } ReturnValue = CAST ( \ (IO_IN (pDX->ADAPTERBASE+AR_8273S) & (AS_RXINT|AS_TXINT)) != 0,\ BOOLEAN \ ); /* return true if we expected the interrupt */ do /* while we have an interrupt result avail. */ { /*************************************************************************/ /* To be on the safe side, don't do anything further until the command */ /* busy bit in the status register has gone off. */ /*************************************************************************/ WAITUNTIL (pDX, AS_CMBSY, EQ, 0); /* wait for busy bit to go off */ Status = IO_IN (pDX->ADAPTERBASE+AR_8273S); ASSERT (BITSOFF(Status, AS_CRBFF | AS_CPBFF | AS_CMBFF | AS_CMBSY)); if (Status & AS_RXINT) /* receiver action */ { /***********************************************************************/ /* Note we don't stop DMA on receiver. The design of the buffers has */ /* taken into account receiving a full 8 frames+RR in DMA mode - so */ /* that we can take account of the 8273 feature of continuing to */ /* recieve when in DMA mode. */ /***********************************************************************/ if (Status & AS_RXIRA) { /*********************************************************************/ /* We have a receiver result. Read the result bytes into the */ /* device extension and after reading, action immediately */ /*********************************************************************/ for (pDX->RxResultCount = 0; pDX->RxResultCount < (sizeof (pDX->RxResultBuffer) - 1); /* last byte never gets filled in ! */ ) { pDX->RxResultBuffer[pDX->RxResultCount++] = IO_IN (pDX->ADAPTERBASE + AR_8273R); KeStallExecutionProcessor (1L); /* let the chip settle down again*/ WAITUNTIL(pDX, (AS_RXIRA | AS_RXINT), NE, AS_RXINT); TRACE_DATABYTE (RxR, pDX->RxResultBuffer[pDX->RxResultCount]); /*******************************************************************/ /* either the INT bit went off or IRA went on */ /* If INT went off, ignore the rest - even if IRA went on */ /* (shouldn't have) */ /*******************************************************************/ if (BITSOFF(LastWaitUntilStatus, AS_RXINT)) break; } /*********************************************************************/ /* We do nothing if receiver is not active. This sitn. arises when */ /* */ /* - we prepare to disable the recver (start into synchronized execn)*/ /* - a result becomes available */ /* - the synchronized execution carries on and disables the receiver */ /* */ /* This only arises when we are in DMA mode - for SDLC. (For PIO, */ /* receiver kept active). Make life simple by ignoring result */ /* otherwise we get FSM's knickers in a twist. */ /*********************************************************************/ if (pDX->RxFSMCurState EQ RxFSMStateReady) { while (pDX->RxResultCount > 0) { /*****************************************************************/ /* There will either be one or two result groups set. The case */ /* of two groups arises when one result has occurred but not */ /* been read by the time another result comes in. */ /* */ /* The second result is only one byte so we special case this to */ /* make it easier get the rxresults down to the start again. */ /*****************************************************************/ if (pDX->RxResultBuffer[0] EQ ARxR_A1OK ||/* A1/A2 match, rcv OK */ pDX->RxResultBuffer[0] EQ ARxR_A2OK) { RxFSMEvent (pDX, RxFSMInputGoodReceive); pDX->RxResultBuffer[0] = pDX->RxResultBuffer[5]; pDX->RxResultCount -= 5; } else { RxFSMEvent (pDX, RxFSMInputReceiverError); pDX->RxResultBuffer[0] = pDX->RxResultBuffer[1]; pDX->RxResultCount -= 1; } } } } else { /*********************************************************************/ /* IRA not set - no result available, must be for received character */ /* */ /* Not that neither here nor below in tx do check for overrunning */ /* the buffer - we just believe 8273 is OK (and it hasn't failed in */ /* this area yet. ASSERTions to guard against buffer overruns */ /* should be done in completion code. */ /*********************************************************************/ ASSERT (BITSOFF(pDX->GrabbedResources, GRABBEDRESOURCE_GOTDMA)); /* must be PIO then */ *(pDX->pRxPIOData++) = IO_IN (pDX->ADAPTERBASE+AR_8273D); // TRACE_DATABYTE(RxD, *(pDX->pRxPIOData-1)); } } if (Status & AS_TXINT) /* transmitter action */ { if (Status & AS_TXIRA) { /*********************************************************************/ /* We have a transmitter result. Read the result bytes into the */ /* device extension and after reading, request a DPC. */ /* */ /* The DPCAction tells the DPC why it is being called */ /* */ /* This implementation also assumes that only one transmitter result */ /* will be outstanding at any one time, so there is only one */ /* TxResult byte. */ /*********************************************************************/ pDX->TxResult = IO_IN (pDX->ADAPTERBASE + AR_8273T); TRACE_DATABYTE (TxR, pDX->TxResult); if (pDX->TxResult EQ ATxR_TxCompleteOK) { TxFSMEvent (pDX, TxFSMInputEOTx); } else if (pDX->TxResult EQ ATxR_ErrTxFrameAborted) { TxFSMEvent (pDX, TxFSMInputAbortd); } else { TxFSMEvent (pDX, TxFSMInputEOTx_Err); } } else { /*********************************************************************/ /* IRA not set - no result available, must be to tx next character */ /*********************************************************************/ ASSERT (BITSOFF(pDX->GrabbedResources, GRABBEDRESOURCE_GOTDMA)); /* must be PIO then */ // TRACE_DATABYTE (TxD, *pDX->pTxPIOData); IO_OUT(pDX->ADAPTERBASE+AR_8273D, *(pDX->pTxPIOData++)); } } Status = IO_IN (pDX->ADAPTERBASE + AR_8273S); } while (Status & (AS_TXINT|AS_RXINT)); if (!OriginalDPCAction && /* on entry, no dpc actions */ pDX->DPCAction) /* but on exit, we have DPC actions */ { IoRequestDpc (pDeviceObject, NULL, NULL); /* DPC will pulse event for us */ } //XTRACE_EVENT (ISR>); return(ReturnValue); } /**PROC+**********************************************************************/ /* */ /* Name: EntryPointOpen ( */ /* PDEVICE_OBJECT pDeviceObject */ /* PIRP pIrp */ /* ) */ /* */ /* Purpose: Initialise the next device (from GetDriverSpec) */ /* */ /* Params: IN DeviceObject: Our device. */ /* IN PIrp The IRP in question. */ /* */ /* Return Value:BOOLean: TRUE if device successfully initialised */ /* */ /* Operation: */ /* 1. Copy over pre-initialised data sequences */ /* */ /* 2. Init for NT */ /* - allocate transmit and receive buffers DMA-able */ /* - DeviceObject */ /* - DO_DIRECT_IO */ /* - IoInitializeDpcRequest */ /* - ConnectInterrupt */ /* */ /* 3. Init our own data */ /* - DeviceIsOpen = FALSE */ /* */ /**PROC-**********************************************************************/ NTSTATUS EntryPointOpen ( PDEVICE_OBJECT pDeviceObject, PIRP pIrp ) { //ULONG AddressSpace; KAFFINITY Affinity; int Information = 0; KIRQL InterruptLevel; CCHAR InterruptVector; BOOLean OKToContinue = TRUE; PDX pDX = pDeviceObject->DeviceExtension; NTSTATUS Status = STATUS_UNSUCCESSFUL; int WindDownLevel= 0; ASSERT (!pDX->DeviceIsOpen); /* device defined as exclusive usage!*/ TRACE_NEWLINE(); TRACE_EVENT({EPO); TRACE_ACTION (Op,CAST(pDX->ADAPTERBASE, ULONG) & 0xFFFF); pIrp->IoStatus.Information = 0L; pDX->GrabbedResources = 0; /* clear the list of resources */ if (InterfaceType == MicroChannel) { if (!pDX->ConfigData.MPAAAdapterIdentifier) { OKToContinue = FALSE; Status = STATUS_INSUFFICIENT_RESOURCES; Information = IO_ERR_NEEDS_ISA_BUS; TRACE_EVENT (OcWb); } } else { if (pDX->ConfigData.MPAAAdapterIdentifier) { OKToContinue = FALSE; Status = STATUS_INSUFFICIENT_RESOURCES; Information = IO_ERR_NEEDS_MCA_BUS; TRACE_EVENT (OcWb); } } // // Map the memory for the control registers for the parallel device // into virtual memory. This code needs to be activated when moving to // a platform with memory-mapped I/O. // // AddressSpace = pDX->ConfigData.AddressSpace; // CardAddress = HalTranslateBusAddress( // InterfaceType, // pDX->ConfigData->BusNumber, // pDX->ConfigData->AdapterBaseAddress, // &AddressSpace // ); // // if (!AddressSpace) // { // ParDeviceObject->UnMapRegisters = TRUE; // ParDeviceObject->DeviceRegisters = MmMapIoSpace( // CardAddress, // PARALLEL_REGISTER_LENGTH, // FALSE // ); // // } else { // // ParDeviceObject->UnMapRegisters = FALSE; // ParDeviceObject->DeviceRegisters = (PVOID)CardAddress.LowPart; // // } // // if (!ParDeviceObject->DeviceRegisters) { // // DbgPrint("Couldn't map the device registers.\n"); // IoDeleteDevice(DeviceObject); // return STATUS_NONE_MAPPED; // // } /***************************************************************************/ /* 1. NT-related device initialisation */ /***************************************************************************/ if (OKToContinue) { InterruptVector = HalGetInterruptVector( InterfaceType, pDX->ConfigData.BusNumber, pDX->ConfigData.Irql, pDX->ConfigData.Vector, &InterruptLevel, &Affinity ); Status = IoConnectInterrupt ( &pDX->Interrupt, EntryPointISR, pDeviceObject, NULL, InterruptVector, InterruptLevel, /* interrupt IRQ level */ InterruptLevel, /* synchronize IRQ level */ pDX->ConfigData.InterruptMode, pDX->ConfigData.Shareable, Affinity, /* processor number */ FALSE /* no save floating pt contxt*/ ); if (!NT_SUCCESS(Status)) { OKToContinue = FALSE; Status = STATUS_INSUFFICIENT_RESOURCES; Information = IO_ERR_CANT_CONNECT_INTERRUPT_1; TRACE_EVENT (OcCi); } } if (OKToContinue) { WindDownLevel = 15; if (pDX->ConfigData.Irql2) { /***********************************************************************/ /* A second interrupt is required */ /***********************************************************************/ ASSERT (pDX->ConfigData.FlavourName[0] EQ 'S'); /* only the SDLC card is so dumb */ if (OKToContinue) { InterruptVector = HalGetInterruptVector( InterfaceType, pDX->ConfigData.BusNumber, pDX->ConfigData.Irql2, pDX->ConfigData.Vector2, &InterruptLevel, &Affinity ); Status = IoConnectInterrupt ( &pDX->Interrupt2, EntryPointRogueInterrupt, pDeviceObject, NULL, InterruptVector, InterruptLevel, /* interrupt IRQ level */ InterruptLevel, /* synchronize IRQ level */ pDX->ConfigData.InterruptMode2, pDX->ConfigData.Shareable2, 1, /* processor number */ FALSE /* no save floating pt contxt*/ ); if (!NT_SUCCESS(Status)) { OKToContinue = FALSE; Status = STATUS_INSUFFICIENT_RESOURCES; Information = IO_ERR_CANT_CONNECT_INTERRUPT_2; TRACE_EVENT (OcCi); } } } } /***************************************************************************/ /* 2. Buffer allocation and init */ /***************************************************************************/ if (OKToContinue) { WindDownLevel = 20; OKToContinue = AllocateDMAMemory (sizeof(RCVBUFARRAY), (PVOID *)&pDX->RcvInfo.pRcvBufArray, &pDX->RcvInfo.pRcvMdl, &pDX->RcvInfo.RcvBufPhysAddr, &Information ); if (!OKToContinue) { Status = STATUS_INSUFFICIENT_RESOURCES; // Information set in above call to AllocateDmaMemory } } if (OKToContinue) { WindDownLevel = 30; RCVINFO_INIT(pDX); pDX->RxFSMCurState = RxFSMStateIdle; OKToContinue = AllocateDMAMemory (SENDBUF_SIZE, (PVOID *)&pDX->pSendBuf, &pDX->pSendMdl, &pDX->SendBufPhysAddr, &Information ); if (!OKToContinue) { Status = STATUS_INSUFFICIENT_RESOURCES; // Information set in above call to AllocateDmaMemory } } if (OKToContinue) { WindDownLevel = 40; pDX->TxFSMCurState = TxFSMStateIdle; pDX->TxConsecutiveUnderrunCount = 0; COPY8273CMD (pDX, CmdStringReadPortA ); COPY8273CMD (pDX, CmdStringResetOpMode ); COPY8273CMD (pDX, CmdStringResetSerialIOMode); COPY8273CMD (pDX, CmdStringSetOpMode ); COPY8273CMD (pDX, CmdStringSetSerialIOMode ); COPY8273CMD (pDX, CmdStringDataTransferMode ); COPY8273CMD (pDX, CmdStringResetPortB ); COPY8273CMD (pDX, CmdStringSetPortB ); COPY8273CMD (pDX, CmdStringReceive ); COPY8273CMD (pDX, CmdStringTransmit ); COPY8273CMD (pDX, CmdStringAbortTransmit ); COPY8273CMD (pDX, CmdStringDisableReceiver ); pDX->pIR = &pDX->OurIR; /* use dummy IR for now until real */ pDX->pIR->V24In = 0; pDX->pIR->V24Out = 0; pDX->pIR->RxFrameCount= 0; pDX->pIR->TxMaxFrSizeNow = INIT_MAXFRSIZENOW; // pDX->pIRMdl = NULL; /*IRMdl?*/ pDX->pUserEvent = NULL; /* null until Ioctl: SetEvent */ } /***************************************************************************/ /* SDLC device initialisation */ /***************************************************************************/ if (OKToContinue) { if (!KeSynchronizeExecution(pDX->Interrupt, SynchEntryPointOpen, (PVOID)pDX)) { Status = STATUS_DATA_ERROR; Information = pDX->Information; OKToContinue = FALSE; } } if (OKToContinue) { if (pDX->HardwareError) { Status = STATUS_DATA_ERROR; Information = IO_ERR_HARDWARE_CMD_TIMEOUT_3; OKToContinue = FALSE; } } if (!OKToContinue) { TRACE_ACTION (Wl, WindDownLevel & 0xFFFF); if (WindDownLevel >= 15) IoDisconnectInterrupt( pDX->Interrupt); if (pDX->ConfigData.Irql2) { if (WindDownLevel >= 20) IoDisconnectInterrupt( pDX->Interrupt2); } if (WindDownLevel >= 30) { MmUnlockPages (pDX->RcvInfo.pRcvMdl); IoFreeMdl (pDX->RcvInfo.pRcvMdl); MmFreeContiguousMemory (pDX->RcvInfo.pRcvBufArray); } if (WindDownLevel >= 40) { MmUnlockPages (pDX->pSendMdl); IoFreeMdl (pDX->pSendMdl); pDX->pSendMdl = NULL; MmFreeContiguousMemory (pDX->pSendBuf); pDX->pSendBuf = NULL; } LogDriverError( pDeviceObject, Status, Information, IoGetCurrentIrpStackLocation(pIrp)->MajorFunction, 0L ); } pIrp->IoStatus.Status = Status; pIrp->IoStatus.Information = Information; if (Status EQ STATUS_SUCCESS) { pDX->DeviceIsOpen = TRUE; } TRACE_EVENT(EPO}); TRACE_NTFAILURE (Status); CompleteIoRequest (pIrp); return (Status); } /*****************************************************************************/ /* */ /* Name EntryPointRogueInterrupt */ /* */ /* Purpose Mode Status ISR. */ /* */ /* Should never be called!!! This ISR is used to handle int4 */ /* on SDLC cards (modem status and timers). Since the int is */ /* explicitly masked off in adapter set-up, it is a severe err */ /* if we actually get one! */ /* */ /* */ /* Params IN None */ /* */ /* OUT DLC appl hardware status updated. */ /* */ /*****************************************************************************/ BOOLEAN EntryPointRogueInterrupt ( PKINTERRUPT pInterrupt, PVOID Context ) { PDEVICE_OBJECT pDeviceObject= (PDEVICE_OBJECT) Context; PDX pDX = pDeviceObject->DeviceExtension; UNREFERENCED_PARAMETER (pInterrupt); TRACE_EVENT ({EP?); /***************************************************************************/ /* Update hardware error status and trigger an event for Link process via */ /* DPC */ /***************************************************************************/ pDX->pIR->StatusArray[SA_HardwareError]++; pDX->pIR->StatusCount++; pDX->DPCAction |= DPC_ACTION_PULSE; IoRequestDpc (pDeviceObject, NULL, NULL); /* DPC will pulse event for us */ /***************************************************************************/ /* Reset the modem status logic and turn the 8273 off - it is being too */ /* disruptive to keep active! */ /***************************************************************************/ WR_N_DELAY (pDX->ADAPTERBASE + AR_8255B, A55_ResetModemStatusCh+ A55_Reset8273Off); WR_N_DELAY (pDX->ADAPTERBASE + AR_8255B, A55_Reset8273Off); TRACE_EVENT (EP?}); return(FALSE); /* interrupt wasn't expected */ } /**PROC+**********************************************************************/ /* */ /* Name: EntryPointUnload ( */ /* PDRIVER_OBJECT pDriverObject */ /* ) */ /* */ /* Purpose: Unload the device - don't need to do anything? */ /* */ /* Params: IN DriverObject: Our device. */ /* */ /* Return Value:None */ /* */ /* Operation: */ /* */ /**PROC-**********************************************************************/ VOID EntryPointUnload ( IN PDRIVER_OBJECT pDriverObject ) { IoDeleteDevice( pDriverObject->DeviceObject ); return; } /**PROC+**********************************************************************/ /* */ /* Name GetInterfaceType ( */ /* PDRIVER_OBJECT pDriverObject */ /* ) */ /* */ /* Purpose Hide the vagaries of roasting squirrels */ /* */ /* Return Value BOOLean : TRUE if InterfaceType (global) set up OK */ /* */ /**PROC-**********************************************************************/ BOOLean GetInterfaceType ( IN PDRIVER_OBJECT pDriverObject ) { PUNICODE_STRING RegistryPath = pDriverObject->HardwareDatabase; PRTL_QUERY_REGISTRY_TABLE Parameters = NULL; UNICODE_STRING ParametersPath; OBJECT_ATTRIBUTES ParametersAttributes; HANDLE ParametersKey; UNICODE_STRING Identifier; UNICODE_STRING MCAString; NTSTATUS Status = STATUS_SUCCESS; ULONG Information; TRACE_EVENT([GIT); Identifier.Buffer = NULL; /***************************************************************************/ /* Set up the Registry path to check if \EisaAdapter\0 key exists. */ /***************************************************************************/ RtlInitUnicodeString (&ParametersPath, NULL); ParametersPath.MaximumLength = RegistryPath->Length + sizeof(L"\\MultifunctionAdapter\\0") + 4; ParametersPath.Buffer = ExAllocatePool( PagedPool, ParametersPath.MaximumLength ); if (!ParametersPath.Buffer) { TRACE_EVENT (GIT1); Information = IO_ERR_GET_IF_TYPE_1; Status = STATUS_UNSUCCESSFUL; } else { RtlZeroMemory( ParametersPath.Buffer, ParametersPath.MaximumLength ); RtlAppendUnicodeStringToString( &ParametersPath, RegistryPath ); RtlAppendUnicodeToString( &ParametersPath, L"\\EisaAdapter\\0" ); /*************************************************************************/ /* Attempt to open the key - if we can this is Eisa bus. */ /*************************************************************************/ InitializeObjectAttributes( &ParametersAttributes, &ParametersPath, OBJ_CASE_INSENSITIVE, NULL, NULL ); if (NT_SUCCESS(ZwOpenKey( &ParametersKey, MAXIMUM_ALLOWED, &ParametersAttributes ))) { InterfaceType = Eisa; TRACE_EVENT (Eisa); DEBUG_PRINT (("IBMSYNC: Bus type is Eisa\n")); TRACE_EVENT(GIT]); ExFreePool(ParametersPath.Buffer); return(TRUE); } } if (NT_SUCCESS(Status)) { /*************************************************************************/ /* Reset the Registry path to check for Identifier string in key */ /* \MultifunctionAdapter\0. */ /*************************************************************************/ RtlZeroMemory( ParametersPath.Buffer, ParametersPath.MaximumLength ); ParametersPath.Length = 0; RtlAppendUnicodeStringToString( &ParametersPath, RegistryPath ); RtlAppendUnicodeToString( &ParametersPath, L"\\MultifunctionAdapter\\0" ); /*************************************************************************/ /* Allocate the Rtl query table. */ /*************************************************************************/ Parameters = ExAllocatePool( PagedPool, sizeof(RTL_QUERY_REGISTRY_TABLE) * 2 ); if (!Parameters) { TRACE_EVENT (GIT2); Information = IO_ERR_GET_IF_TYPE_2; Status = STATUS_UNSUCCESSFUL; } else { /***********************************************************************/ /* Allocate memory to except Identifier type. */ /***********************************************************************/ RtlInitUnicodeString (&Identifier, NULL); Identifier.MaximumLength = 20; /* Room for "EISA", "MCA" or "ISA" */ Identifier.Buffer = ExAllocatePool( PagedPool, Identifier.MaximumLength ); if (!Identifier.Buffer) { TRACE_EVENT (GIT3); Information = IO_ERR_GET_IF_TYPE_3; Status = STATUS_UNSUCCESSFUL; } else { /*********************************************************************/ /* OK now finally pick up the Bus Type (Identifier) string from the */ /* Registry. */ /*********************************************************************/ RtlZeroMemory( Parameters, sizeof(RTL_QUERY_REGISTRY_TABLE) * 2 ); Parameters[0].Flags = RTL_QUERY_REGISTRY_DIRECT; Parameters[0].Name = L"Identifier"; Parameters[0].EntryContext = &Identifier; Parameters[0].DefaultType = REG_SZ; Parameters[0].DefaultData = L"None"; Parameters[0].DefaultLength = 8; if (!NT_SUCCESS(RtlQueryRegistryValues( RTL_REGISTRY_ABSOLUTE, ParametersPath.Buffer, Parameters, NULL, NULL ))) { TRACE_EVENT (GIT4); Information = IO_ERR_GET_IF_TYPE_4; Status = STATUS_UNSUCCESSFUL; } else { RtlInitUnicodeString (&MCAString, L"MCA"); if (!RtlCompareUnicodeString(&MCAString, &Identifier, TRUE)) { InterfaceType = MicroChannel; TRACE_EVENT (MCA ); DEBUG_PRINT (("IBMSYNC: Bus type is MicroChannel\n")); } else { InterfaceType = Isa; TRACE_EVENT (ISA ); DEBUG_PRINT (("IBMSYNC: Bus type is Isa\n")); } } } } } /***************************************************************************/ /* Free any allocated memory before returning. */ /***************************************************************************/ if (ParametersPath.Buffer) ExFreePool(ParametersPath.Buffer); if (Identifier.Buffer) ExFreePool(Identifier.Buffer); if (Parameters) ExFreePool(Parameters); /***************************************************************************/ /* OK, so which Device Object do we use here? The driver can have */ /* multiple Device Objects set up; also the reading of the bus type from */ /* the Registry is done just once (i.e not on a per-Device basis). */ /* */ /* We use the last one hanging from the list - to get to this stage we */ /* must have at least one. */ /***************************************************************************/ if (!NT_SUCCESS(Status)) { LogDriverError( pDriverObject->DeviceObject, Status, Information, 0L, 0L ); } TRACE_EVENT(GIT]); return(Status == STATUS_SUCCESS); } /**PROC+**********************************************************************/ /* */ /* Name GetDriverSpec ( */ /* CHAR *DriverName, */ /* PCONFIGDATA *pConfigData */ /* ); */ /* */ /* Purpose Hide the vagaries of initialising */ /* */ /* Params OUT DriverName: e.g. COM$DL01, null-terminated, 8+1 chars */ /* OUT FlavourName: e.g. SDLC or MPAA1, null-terminated, 5+1 chars */ /* OUT pConfigData: the config data record to be set up */ /* (a pointer is passed, not the structure itself) */ /* */ /* Return Value BOOLean: TRUE if there was another driver spec to get */ /* */ /* Operation At a guess, this will */ /* - enumerate all SDLC device drivers in CFG Reg. */ /* (RegEnumKeyEx(Key:Master\Software\CS\Drivers) */ /* or CS\SDLCDrivers? */ /* - if necessary, check the driver is an SDLC one */ /* - read config info to extract device name and type */ /* - get other config data into ConfigData */ /* */ /* Notes Uses static variables to indicate position in config; so */ /* no indication from user that this is first time is required. */ /* */ /* Called only during DriverEntry. */ /* */ /* The fact that a driver/flavour is returned does not imply */ /* the device is usable - this must be checked by calling */ /* DeviceInit. */ /* */ /**PROC-**********************************************************************/ BOOLean GetDriverSpec (CHAR *DeviceName, PCONFIGDATA *pConfigData ) { static int DeviceNumber = 0; /* very temporary - until we find out how to read config registry. */ if (++DeviceNumber > AT_COUNT) return (FALSE); strcpy (DeviceName, "\\Device\\COMDL$00"); DeviceName[strlen(DeviceName)-1] = ((char) ('0' + DeviceNumber)); *pConfigData = &ConfigData[DeviceNumber-1]; return(TRUE); } /**PROC+**********************************************************************/ /* */ /* Name GetV24Input ( */ /* PDX pDX */ /* ); */ /* */ /* Purpose Interpret pin inputs as presented by SDLC cards and stuff */ /* into IR fields */ /* */ /* Params IN pDX - the device extension */ /* */ /* Return Value None */ /* */ /**PROC-**********************************************************************/ void GetV24Input (PDX pDX) { UCHAR o; TRACE_EVENT (pIR->V24In = 0; /* reset IR value. */ /***************************************************************************/ /* Reset the 'modem status changed' bit because after we read this */ /* hasn't changed */ /***************************************************************************/ WR_N_DELAY (pDX->ADAPTERBASE+AR_8255B, IO_IN (pDX->ADAPTERBASE+AR_8255B) & CAST(~8,UCHAR)); /* 8 = modem status changed */ /***************************************************************************/ /* Read the 8255 Port C settings to get the current state of Test */ /***************************************************************************/ if (BITSOFF(IO_IN (pDX->ADAPTERBASE+AR_8255C), 0x40)) { pDX->pIR->V24In |= IR_IV24Test; /* &40 = 0 => Test is Active */ } /***************************************************************************/ /* Read the 8255 Port A settings. */ /***************************************************************************/ o = (IO_IN (pDX->ADAPTERBASE+AR_8255A)); if (!(o&1)) /* &1=0 implies RI on at interface */ { pDX->pIR->V24In |= IR_IV24RI; } if (!(o&2)) /* &2=0 implies DCD on at interface */ { pDX->pIR->V24In |= IR_IV24DCD; } if (!(o&8)) /* &8=0 implies CTS on at interface */ { pDX->pIR->V24In |= IR_IV24CTS; } /***************************************************************************/ /* Used to do the 8273 Port A Reading to get hold of DSR here. There are */ /* slight problems with doing that */ /* - the 8273 can get upset if given three commands at once (e.g Rx, Tx */ /* AND CmdStringReadPortA) (this is surmountable) */ /***************************************************************************/ if (pDX->RxFSMCurState NE RxFSMStateReady || /* not receiving ... or */ pDX->TxFSMCurState EQ TxFSMStateIdle) /* transmitter idle */ { pDX->LastPortA = Write8273Cmd (pDX, pDX->CmdStringReadPortA); } if (pDX->LastPortA & 4) /* is the DSR bit set??? */ { pDX->pIR->V24In |= IR_IV24DSR; } TRACE_EVENT (GV2>); } /*****************************************************************************/ /* */ /* Name InitialiseAdapter */ /* */ /* Purpose Initialises and checks the hardware for a link. */ /* */ /* Params IN pDX, plus Synchronised, plus interrupt routines set up */ /* */ /* Return Value False on failure, plus SynchInformation set */ /* */ /*****************************************************************************/ BOOLean InitialiseAdapter (PDX pDX) { BOOLean rc; TRACE_EVENT (LinkMaxFrameSize = DEFAULT_FRAME_SIZE; pDX->LinkOptionsByte = 0; if (SetLinkConfig(pDX)) { SetV24Output (pDX); GetV24Input (pDX); rc = TRUE; /* OK return code */ } else { SynchTerminateAdapter(pDX); pDX->Information = IO_ERR_HARDWARE_INIT_FAILURE; rc = FALSE; TRACE_EVENT (HwIF); TRACE_RC(rc); } TRACE_EVENT (InA>); return (rc); } /*****************************************************************************/ /* */ /* Name IoctlAbortReceiver */ /* */ /* Purpose Abort Receiver IOCtl Processor */ /* */ /* Params IN pDX */ /* */ /* OUT Requested function is processed. No return code */ /* */ /*****************************************************************************/ void IoctlAbortReceiver (PDX pDX) { TRACE_EVENT ([IAR); RxFSMEvent (pDX, RxFSMInputStop); /* terminate with extreme prejudice */ /***************************************************************************/ /* Then reset the buffer pointers, discarding all the data in the receive */ /* buffer. */ /***************************************************************************/ RCVINFO_INIT(pDX); TRACE_EVENT (IAR]); } /*****************************************************************************/ /* */ /* Name IoctlAbortTransmitter */ /* */ /* Purpose Abort Transmitter IOCtl Processor */ /* */ /* Params IN pDX */ /* */ /* OUT None */ /* */ /*****************************************************************************/ void IoctlAbortTransmitter (PDX pDX) { TRACE_EVENT ([IAT); /***************************************************************************/ /* First stop the transmitter (if it is running). */ /***************************************************************************/ TxFSMEvent (pDX, TxFSMInputStop); /*0025****************************************************************/ /*0025* Clear the underrun count. */ /*0025****************************************************************/ pDX->TxConsecutiveUnderrunCount = 0; /***************************************************************************/ /* Then reset the transmit buffer pointers, discarding all the data in the */ /* buffer. */ /***************************************************************************/ pDX->TxNextToTransmit = 0; pDX->TxNextToBuffer = 0; /*0025**********************************************************************/ /*0025* Set up the maximum buffer size and the interface record */ /*0025* 'initialisation' buffer size. */ /*0025**********************************************************************/ pDX->TxStartUnusedArea = SENDBUF_SIZE; pDX->pIR->TxMaxFrSizeNow = INIT_MAXFRSIZENOW; XTRACE_ACTION (Tz, pDX->pIR->TxMaxFrSizeNow); TRACE_EVENT (IAT]); } /*****************************************************************************/ /* */ /* Name IoctlSetInterfaceRecord */ /* */ /* Purpose / *IRMdl?* / Obsolete */ /* Set Interface Record Address handler */ /* */ /* Params IN pDX */ /* Implicit: the input buffer address (IrpSp->InputBufferLength */ /* + Irp->AssociatedIrp->SystemBuffer) describes the */ /* interface record */ /* */ /* OUT None */ /* */ /* Side Effect pDX->IoctlRetStatus & Information */ /* */ /* Return Value void */ /* */ /*****************************************************************************/ //void IoctlSetInterfaceRecord (PDX pDX) //{ // PIRP pIrp = pDX->IoctlCurrentIrp; // PIO_STACK_LOCATION // pIrpSp = IoGetCurrentIrpStackLocation(pIrp); // // TRACE_EVENT ([IoI); // // /***************************************************************************/ // /* The old story: allocate an MDL, probe and lock, map to system address */ // /* space and save the resulting system address in pIR. */ // /* */ // /* Free up Mdl and unlock pages if this is a subsequent call. */ // /***************************************************************************/ // // if (pDX->pIRMdl != NULL) // { // /*************************************************************************/ // /* Protect ourselves from bozo users calling IoctlSetInterfaceRecrd twice*/ // /*************************************************************************/ // ASSERT (pDX->pIR != NULL); // MmUnmapLockedPages (pDX->pIR, pDX->pIRMdl); /*IRMdl?*/ // IoFreeMdl (pDX->pIRMdl); // } // // pDX->pIRMdl = IoAllocateMdl(pIrp->AssociatedIrp.SystemBuffer, // pIrpSp->IRS_INLEN, // sizeof (IRP), // FALSE, /* this is not a secondary buffer */ // FALSE, /* no charge to quota */ // (PIRP)NULL/* not attached to IRP */ // ); // // if (pDX->pIRMdl NE NULL) /* We could do the mapping */ // { // MmProbeAndLockPages (pDX->pIRMdl, KernelMode, IoModifyAccess); // // pDX->pIR = MmMapLockedPages(pDX->pIRMdl, KernelMode); // pDX->pIR->RxFrameCount = 0; // pDX->pIR->TxMaxFrSizeNow = INIT_MAXFRSIZENOW; // pDX->pIR->StatusCount = 0; // pDX->pIR->V24In = 0; // pDX->pIR->V24Out = 0; // RtlZeroMemory (pDX->pIR->StatusArray, sizeof (pDX->pIR->StatusArray)); // TRACE_EVENT (pIR_); // TRACE_DWORD (pDX->pIR); // XTRACE_ACTION (Tz, pDX->pIR->TxMaxFrSizeNow); // } // else // { // pDX->pIR = NULL; // pDX->Information = INFO_CANT_ALLOCATE_MDL; // pDX->IoctlRetStatus = STATUS_INSUFFICIENT_RESOURCES; // } // TRACE_EVENT (IoI]); //} /*****************************************************************************/ /* */ /* Name IoctlSetLinkConfig */ /* */ /* Purpose Set Link Characteristics IOCtl Processor */ /* */ /* Sets the link configuration on the basis of the supplied */ /* parameters. This routine also aborts any outstanding */ /* activity in case there is a conflict between old and new */ /* parameters (switching to DMA for instance!). */ /* */ /* Params IN pDX */ /* */ /* OUT */ /* */ /* Side Effect pDX->IoctlRetStatus & Information */ /* */ /*****************************************************************************/ BOOLean IoctlSetLinkConfig (PDX pDX) { PIRP pIrp = pDX->IoctlCurrentIrp; PIO_STACK_LOCATION pIrpSp = IoGetCurrentIrpStackLocation(pIrp); SLPARMS *pParms = pIrp->AssociatedIrp.SystemBuffer; UCHAR NewOpt = pParms->SLLinkOptionsByte; /* easier to type */ BOOLean rc = FALSE; /* assume the worst! */ TRACE_EVENT ([IoC); /***************************************************************************/ /* Issue the abort calls to get Tx and Rx synchronised with this. */ /***************************************************************************/ IoctlAbortReceiver (pDX); IoctlAbortTransmitter(pDX); /***************************************************************************/ /* Read the parameter data. Check that frame will fit the buffer. */ /***************************************************************************/ if (pIrpSp->IRS_INLEN NE sizeof(SLPARMS)) { pDX->IoctlRetStatus = STATUS_INVALID_PARAMETER; pDX->Information = IO_ERR_LINKCHAR_BUF_WRONG_SIZE; } else if (pParms->SLFrameSize < 267) /* don't really want < 2+6+3+256 bytes!? */ { pDX->IoctlRetStatus = STATUS_INVALID_PARAMETER; pDX->Information = IO_ERR_FRAME_BUF_TOO_SMALL; } else if (pParms->SLFrameSize > 2048) /* really SDLC frame bigger than 2Kb? */ { pDX->IoctlRetStatus = STATUS_INVALID_PARAMETER; pDX->Information = IO_ERR_FRAME_BUF_TOO_BIG; } else if (pParms->SLLinkOptionsByte & LinkOption_InternalClock) { /*************************************************************************/ /* We have been asked to supply internal clocks (not supported by these */ /* adapters). */ /*************************************************************************/ pDX->IoctlRetStatus = STATUS_INVALID_PARAMETER; pDX->Information = IO_ERR_NO_CLOCKS; } else { pDX->LinkMaxFrameSize = pParms->SLFrameSize; /*************************************************************************/ /* Pick up the addresses and put them in the LCB. Then set General or */ /* specific receive depending on whether the addresses are zero. */ /*************************************************************************/ pDX->OurAddress1 = pParms->SLOurAddress1; pDX->OurAddress2 = pParms->SLOurAddress2; pDX->CmdStringReceive[4] = pParms->SLOurAddress1; pDX->CmdStringReceive[5] = pParms->SLOurAddress2; if (pParms->SLOurAddress1 EQ 0 && pParms->SLOurAddress2 EQ 0) { /***********************************************************************/ /* if both addresses are zero, we are SDLC primary and can accept */ /* frames targetted at any address */ /***********************************************************************/ pDX->CmdStringReceive[0] = 0xC0; /* set 3-byte General Rx command */ pDX->CmdStringReceive[1] = 2; } else { pDX->CmdStringReceive[0] = 0xC1; /* set 5-byte Specific Rx command */ pDX->CmdStringReceive[1] = 4; } /*************************************************************************/ /* Next, pick up the Link Options and make any necessary changes. */ /*************************************************************************/ if (NewOpt & LinkOption_FullDuplex /* turn off DMA if the link will */ && NewOpt & LinkOption_DMA) /* be full duplex. */ { /***********************************************************************/ /* Check whether the caller asked for DMA, but we couldn't support it */ /* - return warning error if so. */ /***********************************************************************/ NewOpt &= ~LinkOption_DMA; pDX->Information = IO_ERR_NO_DMA_FDX; /* but r/c is OK. */ } pDX->LinkOptionsByte = NewOpt; /* set the new options byte */ /*************************************************************************/ /* Clear the statistics data on the IF record if the appl wants to. */ /*************************************************************************/ if (NewOpt & LinkOption_ResetStatistics) { /* clear the statistics ?? */ RtlZeroMemory (pDX->pIR->StatusArray, sizeof(pDX->pIR->StatusArray)); pDX->pIR->StatusCount = 0; } /*************************************************************************/ /* Now set the options and start the receiver. */ /*************************************************************************/ if (SetLinkConfig (pDX)) /* call hardware to set options */ { RxFSMEvent (pDX, RxFSMInputStart);/* start receiver if possible */ rc = TRUE; /* set good return code */ } else { pDX->IoctlRetStatus = STATUS_DATA_ERROR; pDX->Information = IO_ERR_HARDWARE_CMD_TIMEOUT_4; } } TRACE_EVENT (IoC]); return(rc); } /**PROC+**********************************************************************/ /* */ /* Name IoctlRxFrame */ /* */ /* Purpose Receive Request processor */ /* */ /* Copies next available frame to the application buffer. */ /* */ /* Params IN IRP: buffer length in Stack->OutputBufferLength (IRS_OUTLEN)*/ /* buffer pointer in UserBuffer */ /* */ /* OUT Available data, if any */ /* */ /* Side Effect pDX->IoctlRetStatus & Information */ /* */ /* Return Value TRUE - ignored */ /* */ /**PROC-**********************************************************************/ BOOLean IoctlRxFrame (PDX pDX) { PIRP pIrp = pDX->IoctlCurrentIrp; PIO_STACK_LOCATION pIrpSp = IoGetCurrentIrpStackLocation(pIrp); RFD *pRFD; short RxLength; UCHAR * GiveMeACharPtr; TRACE_EVENT ([IoR); /***************************************************************************/ /* Check to see if there are any frames waiting in the buffer. The ISR */ /* fills the buffer by incrementing the 'head' pointer but stops before it */ /* catches up with the 'tail'. */ /***************************************************************************/ if (RFD_CAN_GET(pDX)) /* then there is data present */ { pRFD = &pDX->RcvInfo.RFDArray[pDX->RcvInfo.RFDNextToGet]; /*************************************************************************/ /* We need to copy the data from our buffer into the user's buffer. */ /* The actual output length goes into the Information field */ /*************************************************************************/ RxLength = pRFD->RcvdDataLength; if (CAST(RxLength, ULONG) + 2 > pIrpSp->IRS_OUTLEN) { /***********************************************************************/ /* Output buffer not as big as A + C + Data received */ /***********************************************************************/ pDX->IoctlRetStatus = STATUS_BUFFER_TOO_SMALL; } else { ASSERT (RxLength >= 0); /* n.b. but length = 0 is OK */ /* (RR's will have length of 0 in */ /* buffer, which we report to user */ /* as length of 0 */ if (RxLength >= 0) /* != -1 => really something there */ { GiveMeACharPtr = MmMapLockedPages(pIrp->MdlAddress, KernelMode); GiveMeACharPtr[0] = pRFD->SDLCAddressByte; GiveMeACharPtr[1] = pRFD->SDLCControlByte; RtlMoveMemory (&GiveMeACharPtr[2], pRFD->StartAddr, RxLength); } } /*************************************************************************/ /* We signal the actual received length back in the IoStatusBlock, via */ /* pDX. */ /*************************************************************************/ pDX->Information = RxLength+2; RFD_GOT(pDX); /* release the buffer we got */ /*************************************************************************/ /* Lastly decrement the count of received frames available in the shared */ /* data area. */ /*************************************************************************/ pDX->pIR->RxFrameCount--; } else { pDX->Information = 0; TRACE_EVENT (IoRN); } TRACE_EVENT (IoR]); return (TRUE); } /*****************************************************************************/ /* */ /* Name IoctlSetV24Output */ /* */ /* Purpose Handles the IoCtl to set the V24 Output */ /* */ /* Params IN pDX */ /* Implicit input: V24Out byte in IR is what is to be pumped out*/ /* */ /* OUT TRUE */ /* */ /* Side Effect pDX->IoctlRetStatus & Information */ /* */ /*****************************************************************************/ BOOLean IoctlSetV24Output (PDX pDX) { TRACE_EVENT ([IVO); // temporary hack !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! pDX->pIR->V24Out = *(CAST (pDX->IoctlCurrentIrp->UserBuffer, UCHAR *)); // temporary hack !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! if (!SetV24Output(pDX)) /* call hardware processor */ { return (FALSE); } TRACE_EVENT (IVO]); return (TRUE); } /*****************************************************************************/ /* */ /* Name IoctlTxFrame */ /* */ /* Purpose Transmit frame request handler. Puts frames into the Tx */ /* buffer ready for the Tx FSM to use. */ /* */ /* Transmit Request Processor. */ /* */ /* Params IN pIrp parameters point to request packet data and length */ /* */ /* OUT Data transferred to Tx buffer ready to send. */ /* Transmitter FSM kicked. */ /* */ /* Side Effect pDX->IoctlRetStatus & Information */ /* */ /*****************************************************************************/ BOOLean IoctlTxFrame (PDX pDX) { ULONG BufferSpaceNeeded; ULONG BufferSpaceAvailable; PIRP pIrp = pDX->IoctlCurrentIrp; PIO_STACK_LOCATION pIrpSp = IoGetCurrentIrpStackLocation(pIrp); BOOLean rc = TRUE; /* normally OK */ TRACE_EVENT ([IoT); BufferSpaceNeeded = pIrpSp->IRS_OUTLEN + 2; /* allow for frame length header */ XTRACE_ACTION (Tn, BufferSpaceNeeded); XTRACE_ACTION (Tx, pDX->LinkMaxFrameSize); XTRACE_ACTION (Tb, pDX->TxNextToBuffer); XTRACE_ACTION (Tt, pDX->TxNextToTransmit); if (CAST(pIrpSp->IRS_OUTLEN, int) < 2) { /*************************************************************************/ /* The minimum frame size is 2 */ /*************************************************************************/ pDX->IoctlRetStatus = STATUS_INVALID_PARAMETER; pDX->Information = IO_ERR_TX_FRAME_TOO_SMALL; rc = FALSE; TRACE_RC(rc); return(rc); } if (CAST(pIrpSp->IRS_OUTLEN, int) > pDX->LinkMaxFrameSize) { /*************************************************************************/ /* The frame is bigger than the maximum specified */ /*************************************************************************/ pDX->IoctlRetStatus = STATUS_INVALID_PARAMETER; pDX->Information = IO_ERR_TX_FRAME_TOO_BIG; rc = FALSE; TRACE_RC(rc); return(rc); } /***************************************************************************/ /* Check to see if there is room for the data in the transmit buffer. */ /* This code fills the buffer by incrementing the 'ToTransmit' pointer but */ /* stops before it catches up with the 'tail'. The ISR takes frames from */ /* the tail pointer, incrementing it until it matches ToTransmit. */ /***************************************************************************/ /***************************************************************************/ /* The ToBuffer and ToTransmit pointers could be anywhere. This code */ /* checks for ToTransmit < ToBuffer and works out the difference -> AX. */ /* If the ToTransmit is greater than the ToBuffer, there could be space at */ /* either end if the buffer - the top end is checked and, if too small, */ /* the ToTransmit is moved round to the start and then checked as */ /* ToTransmit < ToBuffer. */ /***************************************************************************/ if (pDX->TxNextToBuffer >= pDX->TxNextToTransmit) { /* ToTransmit is in front of ToBuffer*/ /*************************************************************************/ /* The free space is at the end of the buffer. If this is too small, */ /* set the 'end' for the ISR to the current ToTransmit and then move */ /* ToTransmit round to the front. Note: we don't go up to SENDBUF_SIZE */ /* because the TxStartUnusedArea must start on that */ /*************************************************************************/ BufferSpaceAvailable = SENDBUF_SIZE - 1 - pDX->TxNextToBuffer; if (BufferSpaceNeeded > BufferSpaceAvailable) /* not enough space at the end ? */ { pDX->TxStartUnusedArea = pDX->TxNextToBuffer; /* ISR will wrap at this point */ pDX->TxNextToBuffer = 0; BufferSpaceAvailable = pDX->TxNextToTransmit-1;/* free up to bfr area*/ XTRACE_ACTION (Tu, pDX->TxStartUnusedArea); XTRACE_ACTION (Tb, pDX->TxNextToBuffer); XTRACE_ACTION (Ta, BufferSpaceAvailable); } } else { /*************************************************************************/ /* the free space is from NextToTransmit up to NextToBuffer */ /*************************************************************************/ BufferSpaceAvailable = pDX->TxNextToBuffer - 1 - pDX->TxNextToTransmit; XTRACE_ACTION (Ta, BufferSpaceAvailable); } if (BufferSpaceAvailable >= BufferSpaceNeeded) { /*************************************************************************/ /* The 'Max Tx Frame is initialised to be a little less than half the */ /* total size of the transmit buffer and is decremented by half the size */ /* of each frame put in (rounded UP). It is kept at half because the */ /* bfr can get split into two pieces and we need a contiguous area. */ /*************************************************************************/ pDX->pIR->TxMaxFrSizeNow -= (BufferSpaceNeeded + 1) >> 1; XTRACE_ACTION (Tz, pDX->pIR->TxMaxFrSizeNow); pDX->pSendBuf [pDX->TxNextToBuffer] = LOBYTE(BufferSpaceNeeded-2); pDX->pSendBuf [pDX->TxNextToBuffer+1] = HIBYTE(BufferSpaceNeeded-2); /* set length to data length */ /* (-2 => less length itself)*/ RtlMoveMemory (&(pDX->pSendBuf[pDX->TxNextToBuffer+2]), pIrp->UserBuffer, BufferSpaceNeeded-2); /*************************************************************************/ /* Now set up the new buffer ToTransmit position. */ /*************************************************************************/ pDX->TxNextToBuffer += BufferSpaceNeeded; XTRACE_ACTION (Tb, pDX->TxNextToBuffer); ASSERT (pDX->TxNextToBuffer < SENDBUF_SIZE); /*************************************************************************/ /* Kick the Transmit FSM to start it off. */ /*************************************************************************/ TxFSMEvent (pDX, TxFSMInputStart); // and returned status is TRUE; } else { /*************************************************************************/ /* There is no room for the frame - return an error. */ /*************************************************************************/ pDX->IoctlRetStatus = STATUS_BUFFER_TOO_SMALL; pDX->Information = IO_ERR_TX_BUFFER_FULL; rc = FALSE; TRACE_RC(rc); } TRACE_EVENT (IoT]); return (rc); } /*****************************************************************************/ /* */ /* Name RxFSMActionInvalid */ /* */ /* Purpose Error input to FSM */ /* */ /* This routine should never be called as it implies an */ /* 'impossible state/input combination. This is taken to mean */ /* that adapter status has been incorrectly reported and is */ /* treated as a hardware error. */ /* */ /* Params IN pDX */ /* */ /* OUT Error processing started by RQ FSM input */ /* */ /*****************************************************************************/ void RxFSMActionInvalid (PDX pDX) { TRACE_EVENT (RAI:); pDX->pIR->StatusArray[SA_HardwareError]++; pDX->pIR->StatusCount ++; pDX->DPCAction |= DPC_ACTION_PULSE; TRACE_EVENT (RAI;); } /*****************************************************************************/ /* */ /* Name RxFSMActionRestart */ /* */ /* Purpose Coming back after having held rx so transmitter can go */ /* so - we can switch buffers so DMA can run for the whole buf */ /* */ /* Params IN pDX */ /* */ /* Implicit input: that when the receiver was stopped that the */ /* 'NowBeingPut' RFD for this adapter was allocated and init- */ /* ialised properly. So this is just a 'start reciver'. */ /* */ /* OUT Receiver started */ /* */ /* */ /*****************************************************************************/ void RxFSMActionRestart (PDX pDX) { RFD * pRFD = & pDX->RcvInfo.RFDArray[pDX->RcvInfo.RFDNowBeingPut]; TRACE_EVENT (RRs:); pDX->LastPortA = Write8273Cmd (pDX, pDX->CmdStringReadPortA); if (pDX->GrabbedResources & GRABBEDRESOURCE_GOTDMA) { StopDMA (pDX); XASSERT (!pDX->DMAIsActive); /*************************************************************************/ /* only worry about this if we using DMA and if other buffer not in use */ /*************************************************************************/ if (!(RCVBUF_INUSE(pRFD->BufPtr->OtherBuffer))) { /* use other buffer */ pRFD->BufPtr = pRFD->BufPtr->OtherBuffer; pRFD->StartIndex= 0; pRFD->StartAddr = &pRFD->BufPtr->Data[0]; } } RxFSMActionStart (pDX); TRACE_EVENT (RRs;); } /*****************************************************************************/ /* */ /* Name RxFSMActionStart */ /* */ /* Purpose Starts the Receiver and DMA (if configured). */ /* */ /* Params IN pDX */ /* */ /* Implicit input: that when the receiver was stopped that the */ /* 'NowBeingPut' RFD for this adapter was allocated and init- */ /* ialised properly. So this is just a 'start reciver'. */ /* */ /* OUT Receiver started */ /* */ /* */ /*****************************************************************************/ void RxFSMActionStart (PDX pDX) { RFD * pRFD = & pDX->RcvInfo.RFDArray[pDX->RcvInfo.RFDNowBeingPut]; int AvailableByteCount = RCVDATABUF_SIZE - 1 - pRFD->StartIndex; /* allocate all the rest of the buffr*/ /* This really is the available bytes*/ USHORT CmdByteCount = CAST (AvailableByteCount - 2, USHORT); /* number of bytes to command 8273 */ TRACE_EVENT (RSt:); XASSERT (AvailableByteCount >= CAST (pDX->LinkMaxFrameSize, int)); /* frame must fit into rest of buffer*/ /* or previous code screwed up */ XTRACE_ACTION (Ra, AvailableByteCount); /***************************************************************************/ /* Prime the DMA if necessary. */ /***************************************************************************/ if (pDX->GrabbedResources & GRABBEDRESOURCE_GOTDMA) { if (!pDX->DMAIsActive) { /***********************************************************************/ /* If the DMA is already running we just leave it running. Otherwise */ /* need to start it */ /***********************************************************************/ PHYSICAL_ADDRESS L; L = pRFD->BufPtr->DataPhysAddr; L.LowPart += (ULONG) pRFD->StartIndex; StartDMA (pDX, L, CmdByteCount, /* should be one less, but we've left*/ /* two bytes slop above anyway */ DMACmdWrite ); } XASSERT (pDX->DMAIsActive); } else { pDX->pRxPIOData = pRFD->StartAddr; XTRACE_EVENT (RPIO); XTRACE_DWORD (pDX->pRxPIOData); } pDX->CmdStringReceive[2] = LOBYTE(CmdByteCount); pDX->CmdStringReceive[3] = HIBYTE(CmdByteCount); Write8273Cmd (pDX, pDX->CmdStringReceive); TRACE_EVENT (RSt;); } /****************************************************************************/ /* */ /* Name RxFSMActionStop */ /* */ /* Purpose Stops the Receiver and DMA (if configured). */ /* */ /* Params IN pDX */ /* */ /* OUT Receiver stopped */ /* */ /****************************************************************************/ void RxFSMActionStop (PDX pDX) { TRACE_EVENT (RSo:); /*********************************************************************/ /* Check whether DMA is supported and kill the DMA Channel if so. */ /*********************************************************************/ if (pDX->GrabbedResources & GRABBEDRESOURCE_GOTDMA) { StopDMA (pDX); XASSERT (!pDX->DMAIsActive); } /*********************************************************************/ /* Issue a Disable command to the 8273. */ /*********************************************************************/ Write8273Cmd (pDX, pDX->CmdStringDisableReceiver); /***************************************************************************/ /* Following IO_DELAY is an attempt to prevent a hang reading port A in */ /* this situation */ /***************************************************************************/ IO_DELAY(5L); pDX->LastPortA = Write8273Cmd (pDX, pDX->CmdStringReadPortA); TRACE_EVENT (RSo;); } /****************************************************************************/ /* */ /* Name RxFSMActionRcvError */ /* */ /* Purpose Updates the received data statistics and restarts the Rcvr */ /* */ /* Carries out end-of-frame processing on error results. */ /* */ /* Params IN BX -> error result from the 8273 */ /* */ /* OUT Updated Rx stats - receiver restarted. */ /* */ /****************************************************************************/ void RxFSMActionRcvError (PDX pDX) { /***************************************************************************/ /* Clear the top nibble of result so we can detect non-octet boundary errs */ /***************************************************************************/ UCHAR ResultCode = CAST(pDX->RxResultBuffer[0] & 0x0F, UCHAR); static USHORT ErrorMapArray[] = /* f: 8273code -> index in StatusArry*/ { /* 0General */ SA_Spare + 256, /* +256->stop receiver */ /* 1Selective */ SA_Spare + 256, /* 2 */ SA_HardwareError, /* shouldn't be generated */ /* 3CRC */ SA_CRC_Error + 256, /* 4Abort */ SA_RxAbort + 256, /* 5Idle */ 0, /* don't use */ /* 6EOP */ SA_Spare, /* 7Short */ SA_RxFrameTooShort + 256, /* 8DMAOverrun */ SA_RxOverrun, /* 9BufO */ SA_RxFrameTooBig, /* 10RLSD */ SA_DCDDrop, /* 11RxIntOverrun */ SA_HardwareError, /* 12 tx */ SA_HardwareError, /* 13 tx */ SA_HardwareError, /* 14 tx */ SA_HardwareError, /* 15 tx */ SA_HardwareError, /* 15 for 0x0F & value */ }; XASSERT (ResultCode < 12); XTRACE_ACTION (Rm, ErrorMapArray[ResultCode]); /***************************************************************************/ /* Filter out 'idle' indications before going through checks */ /***************************************************************************/ if (ResultCode NE ARxR_ErrIdle) /* no StopRequired - idle stop Rcvr */ { /*************************************************************************/ /* Increment the statistics and set the flag to pulse event */ /*************************************************************************/ pDX->pIR->StatusArray[LOBYTE(ErrorMapArray[ResultCode])]++; pDX->pIR->StatusCount++; pDX->DPCAction |= DPC_ACTION_PULSE; } /*********************************************************************/ /* Check whether DMA is supported and kill the DMA Channel if so. */ /*********************************************************************/ if (pDX->GrabbedResources & GRABBEDRESOURCE_GOTDMA) { StopDMA (pDX); XASSERT (!pDX->DMAIsActive); } /***************************************************************************/ /* For those situations that need it (i.e. where the error has left the */ /* receiver active), stop the receiver. As a result, the receiver will */ /* always be stopped after an error. This is just being paranoid - */ /* shouldn't really need to. */ /***************************************************************************/ if (HIBYTE(ErrorMapArray[ResultCode])) /* high byte->stop required */ { RxFSMActionStop (pDX); } /***************************************************************************/ /* Now call the action routine to restart the receiver. */ /***************************************************************************/ RxFSMActionStart (pDX); } /*****************************************************************************/ /* */ /* Name RxFSMActionRcvOK */ /* */ /* Purpose Updates the Receiver buffer data to reflect new data, does */ /* some statistics adjustment and kicks the Rx sema4. */ /* */ /* Carries out end-of-received frame processing. */ /* */ /* Params IN pDX */ /* Implicit:pDX->ResultBuffer has all results */ /* [0] = OK result code */ /* [1] = Length (lo) */ /* [2] = Length (hi) */ /* [3] = Address */ /* [4] = Control */ /* */ /* OUT Updated Rx buffer and stats - receiver restarted. */ /* */ /*****************************************************************************/ void RxFSMActionRcvOK (PDX pDX) { /* this is the 'Old' one */ /* New NowBeingPut */ BOOLean CanMoveOn = TRUE; /* won't be true if no more RFDs OR */ /* no space in this buffer and other */ /* buffer in use */ BOOLean MustStopReceiver = TRUE; short NewNBP = CAST((pDX->RcvInfo.RFDNowBeingPut+1) % RFDARRAY_SIZE, short); RFD * pNewRFD= & pDX->RcvInfo.RFDArray[NewNBP]; USHORT RcvdLength; RFD * pRFD = & pDX->RcvInfo.RFDArray[pDX->RcvInfo.RFDNowBeingPut]; TRACE_EVENT (ROk:); XTRACE_OBJECT(Rp, (*pRFD)); XTRACE_DWORD (pRFD); XTRACE_OBJECT(Rb, (*(pRFD->BufPtr))); XTRACE_ACTION(Rs, pRFD->StartIndex); /***************************************************************************/ /* First, copy results into existing RFD */ /***************************************************************************/ XASSERT (pRFD->RcvdDataLength EQ -1); /* this buffer hasn't been 'filled' */ pRFD->SDLCAddressByte = pDX->RxResultBuffer[3]; pRFD->SDLCControlByte = pDX->RxResultBuffer[4]; pRFD->RcvdDataLength = MAKEUSHORT (pDX->RxResultBuffer[1], pDX->RxResultBuffer[2]); RcvdLength = pRFD->RcvdDataLength; XTRACE_ACTION (Rl,RcvdLength); /***************************************************************************/ /* Double-check the length the 8273 tells us compared to what we rcvd on */ /* the interrupt side */ /***************************************************************************/ #ifdef XDEBUG if (BITSOFF(pDX->GrabbedResources,GRABBEDRESOURCE_GOTDMA)) XASSERT (CAST(pRFD->RcvdDataLength, int) EQ (pDX->pRxPIOData - pRFD->StartAddr)); #endif /***************************************************************************/ /* Now figure out a) if we can fit next frame into this buffer, and b) */ /* failing that if we can start using the other buffer */ /***************************************************************************/ if (NewNBP EQ pDX->RcvInfo.RFDNextToGet) { CanMoveOn = FALSE; /* can't wrap round onto next to get */ } else { /*************************************************************************/ /* Set up the new startindex - this is OK even if we don't move onto new */ /* RFD because it's not being used. */ /* */ /* Then see if we can let the receiver run - if not, we'll have to stop */ /* it and switch DMA etc. */ /* */ /* If we let the receiver run, we must calculate exactly where the data */ /* for the next frame is going to go! */ /* */ /* Note that if we are receiving into a given buffer, we always have the */ /* rest of that buffer to play with, because we only strt using a new */ /* buffer if all received frames that were in it have been got out. */ /*************************************************************************/ pNewRFD->StartIndex = CAST(pRFD->StartIndex + RcvdLength, short); if (CAST(pNewRFD->StartIndex, int) + CAST(pDX->LinkMaxFrameSize, int) + 2 /* +2 for possible CRC dribble */ < RCVDATABUF_SIZE) /* (and some more paranoia). */ { /***********************************************************************/ /* next packet fits into this buffer - can let receiver run */ /***********************************************************************/ pNewRFD->BufPtr = pRFD->BufPtr; pNewRFD->StartAddr = &pNewRFD->BufPtr->Data[pNewRFD->StartIndex]; MustStopReceiver = FALSE; } else { /***********************************************************************/ /* This buffer is hopeless, so how about the other one - have all it's */ /* frams been pulled? If so, we can use it */ /***********************************************************************/ if (RCVBUF_INUSE(pRFD->BufPtr->OtherBuffer)) { CanMoveOn = FALSE; /* cant use him either */ } else { /* use other buffer */ pNewRFD->BufPtr = pRFD->BufPtr->OtherBuffer; pNewRFD->StartIndex= 0; pNewRFD->StartAddr = &pNewRFD->BufPtr->Data[0]; } } } /***************************************************************************/ /* If necessary, stop the receiver */ /***************************************************************************/ if (MustStopReceiver) { RxFSMActionStop (pDX); } /***************************************************************************/ /* If we will be moving on, do it & increment the count of waiting frames. */ /* Whatever happens, we must signal that RFD is empty via RcvdDataLength */ /***************************************************************************/ if (CanMoveOn) { RFD_PUT(pDX); pDX->RcvInfo.RFDNowBeingPut = NewNBP; pRFD = pNewRFD; pDX->pIR->RxFrameCount++; /* show Rx frame available */ } pRFD->RcvdDataLength = -1; /* empty ! */ TRACE_OBJECT(Rp, (*pRFD)); XTRACE_DWORD (pRFD); XTRACE_OBJECT(Rb, (*(pRFD->BufPtr))); XTRACE_ACTION(Rs, pRFD->StartIndex); /***************************************************************************/ /* A frame is ready for the application (or the buffer is full and the */ /* application should do some receiving) - set the flag so that its */ /* semaphore will be cleared by the ISR when we return. */ /***************************************************************************/ pDX->DPCAction |= DPC_ACTION_PULSE; /***************************************************************************/ /* Now call the action routine to restart the receiver. */ /***************************************************************************/ RxFSMActionStart (pDX); TRACE_EVENT (ROk;); } /*****************************************************************************/ /* */ /* Name RxFSMEvent */ /* */ /* Purpose Interprets inputs from the Request handlers and from the */ /* ISR and calls the relevant action routines. */ /* */ /* There are two fundamental states: */ /* */ /* - idle */ /* - ready (i.e. receiver is active) */ /* */ /* Each of these states, however, can also exist in two states */ /* depending on the activity of the transmitter which has to */ /* restrain the receiver when it is using the DMA. These sub- */ /* states are idle(held) and ready(held). */ /* */ /* Purpose Routes processing to the relevant action routines. The FSM */ /* action must be synchronized. The action routines are allowed*/ /* to call back to the FSM. */ /* */ /* Params IN pDX */ /* FSMInput - the input value */ /* Implicit - pDX->RxResultBuffer result bytes pulled from */ /* Rx status register on 8273 */ /* */ /* OUT Action routine called and new state set. */ /* */ /*****************************************************************************/ void RxFSMEvent (PDX pDX, int Input) { RXFSMENTRY *e = &RxFSM [Input] [pDX->RxFSMCurState]; /***************************************************************************/ /* The FSM consists of a 'state' which is actually the address of the */ /* current FSM 'column'. The 'input' is an offset down the column at */ /* which is located an action routine address and a new state. */ /***************************************************************************/ TRACE_EVENT (RxFSMCurState < RXFSMSTATECOUNT); ASSERT (Input < RXFSMINPUTCOUNT); TRACE_EVENTNAME(RxFSMStateNames[pDX->RxFSMCurState]); TRACE_EVENTNAME(RxFSMInputNames[Input]); /***************************************************************************/ /* Reset the state to the new state specified in the FSM table */ /* (Important to do this before the actions because some of them then do */ /* recursive calls to FSMEvent */ /***************************************************************************/ pDX->RxFSMCurState = e->NewState; ASSERT (e->NewState < RXFSMSTATECOUNT); TRACE_EVENTNAME(RxFSMStateNames[pDX->RxFSMCurState]); /***************************************************************************/ /* Do the action specified by the FSM table entry */ /***************************************************************************/ (*(e->pRxActionRoutine)) (pDX); TRACE_EVENT (RFE>); } /*****************************************************************************/ /* */ /* Name FSMNullAction */ /* */ /* Purpose Carries out a 'nul' action for both Tx and Rx FSM */ /* */ /*****************************************************************************/ void FSMNullAction (PDX pDX) { UNREFERENCED_PARAMETER (pDX); return; } /**PROC+**********************************************************************/ /* */ /* Name SetLinkConfig (pDX) */ /* */ /* Purpose Link Configuration Characteristics set-up. */ /* Primes the 8273 and V24 interface in the manner required */ /* required by a particular set of link configuration options. */ /* */ /* Params IN Link Options byte (pDX->LinkOptions) set up */ /* Implicit input: the adapter has been reset and is ready to go*/ /* */ /* OUT Hardware is set up as indicated or carry set. */ /* */ /* Return Value BOOLean: TRUE if options set up OK, otherwise FALSE */ /* */ /**PROC-**********************************************************************/ BOOLean SetLinkConfig (PDX pDX) { UCHAR o; BOOLean rc; TRACE_EVENT (HardwareError EQ FALSE); /* Reset function should turn off */ /* hardware error flag */ /***************************************************************************/ /* Check the configuration bits and use the data to set up the mode set */ /* and reset commands held on the LCB. Then run through the whole batch */ /* in a tight loop at the end. */ /***************************************************************************/ /***************************************************************************/ /* Operating mode */ /***************************************************************************/ o = AP_OMBFR; /* assume default (buffered) */ if (pDX->LinkOptionsByte & LinkOption_HDLC) { o |= AP_OMX25; /* set HDLC Abort */ } pDX->CmdStringSetOpMode[2] = CAST(o, UCHAR); pDX->CmdStringResetOpMode[2]= CAST(o | APR_OM, UCHAR); /***************************************************************************/ /* Serial IO Mode */ /***************************************************************************/ o = 0; /* assume SIO mode is NRZ */ if (pDX->LinkOptionsByte & LinkOption_NRZI) /* then check for NRZI selected */ { o |= AP_IONZI; /* set NRZI bit in the mask */ } pDX->CmdStringSetSerialIOMode[2]= CAST (o, UCHAR); pDX->CmdStringResetSerialIOMode[2]= CAST (o | APR_IO, UCHAR); /* set the unused bits for reset */ /***************************************************************************/ /* Sort out DMA. Assume for now we will be able to use an architectural */ /* NT-defined way of avoiding conflicts on DMA channels, */ /***************************************************************************/ pDX->CmdStringDataTransferMode[0]= 0x97;/* assume Data Transfer is PIO */ pDX->CmdStringDataTransferMode[2]= 1; pDX->GrabbedResources &= ~GRABBEDRESOURCE_GOTDMA; /*and clear DMA privelege*/ if (pDX->LinkOptionsByte & LinkOption_DMA && /* DMA Requested ? */ pDX->ConfigData.DMAChannel NE 0) /* and DMA possible for card */ { /*************************************************************************/ /* Read this link's configured DMA channel and check for non-0 */ /*************************************************************************/ /////////////////////////////////////////////////////////////////////////////// // guff to do with protecting ourselves from other guy using same DMA channel // reinstate later if necessary // // if > // // mov DI,offset SELCBL2 // // else // // mov DI,offset SELCBL1 // // } // // /**********************************************************************/ // /* Check whether the links use the same DMA and grab it if not. */ // /* Otherwise look to see whether the other link does not currently */ // /* have DMA. */ // /**********************************************************************/ // // if DMAChan> OR // test pDX->GrabbedResources & GRABBEDRESOURCE_GOTDMA // if z /////////////////////////////////////////////////////////////////////////////// pDX->GrabbedResources |= GRABBEDRESOURCE_GOTDMA; /* register ownership! */ pDX->DMAIsActive = FALSE; pDX->CmdStringDataTransferMode[0]= 0x57; /* and set DT mode to DMA */ pDX->CmdStringDataTransferMode[2]= 0xFE; /* ..rather than interrupts */ /*************************************************************************/ /* If this is MCA Extended DMA then we have to set up an I/O transfer */ /* address. */ /*************************************************************************/ if (pDX->ConfigData.DMAChannel NE StandardDMAChannel) { /////////////////////////////////////////////////////////////////////////////// //?ML? // from semdh.asm // and AL,0fh ;clear top four bits // out LDD_DMAEFR,AL ;write out the fncn cmnd // // mov AX,LCB.LCBPrtBase // add AX,LDD_73Data // out LDD_DMAEFE,AL ;write out the LOB // SEPDHDLY ;let it take effect // mov AL,AH // out LDD_DMAEFE,AL ;write out the HOB ////////////////////////////////////////////////////////////////////////////// USHORT Addr = CAST(pDX->ADAPTERBASE+AR_8273D, USHORT); WR_N_DELAY (DMAExtdFnRegister, CAST(pDX->ConfigData.DMAChannel & 0x0F, \ UCHAR)); WR_N_DELAY (DMAExtdFnRegister, LOBYTE(Addr)); /* write out the LOB */ WR_N_DELAY (DMAExtdFnRegister, HIBYTE(Addr)); /* and HOB */ } } /***************************************************************************/ /* Now actually issue the mode setting commands to the 8273. */ /***************************************************************************/ Write8273Cmd(pDX,pDX->CmdStringResetOpMode); Write8273Cmd(pDX,pDX->CmdStringResetSerialIOMode); Write8273Cmd(pDX,pDX->CmdStringSetOpMode); Write8273Cmd(pDX,pDX->CmdStringSetSerialIOMode); Write8273Cmd(pDX,pDX->CmdStringDataTransferMode); rc = !pDX->HardwareError; TRACE_RCFALSE(rc); TRACE_EVENT (SLC>); return (rc); } /**PROC+**********************************************************************/ /* */ /* Name SetV24Output ( */ /* PDX pDX */ /* ); */ /* */ /* Purpose Primes the 8273 and V24 interfaces as requested in IR->V24Out*/ /* */ /* Params IN pDX - the device extension */ /* Implicit input: V24Out byte in IR is what is to be pumped out*/ /* */ /* Return Value BOOLean: TRUE if commands etc. correctly written */ /* */ /* Operation */ /* */ /**PROC-**********************************************************************/ BOOLean SetV24Output (PDX pDX) { /**********************************************************************/ /* Do the 8255 Port B settings first. Note that their sense is */ /* logically inverted and 0 = ON. */ /*********************************************************************/ UCHAR o; UCHAR V24Out = pDX->pIR->V24Out; o = 8; /* NOT reset modem status logic */ o |= (V24Out & IR_OV24DSRS) /* user wants DSRS on? */ ? 0 /* 0 in port B turns on DSRS */ : 1; o |= (V24Out & IR_OV24SlSt) /* user wants Select Standby on? */ ? 0 /* 0 in port B turns on Select Stndby*/ : 2; o |= (V24Out & IR_OV24Test) /* user wants Test on? */ ? 0 /* 0 in port B turns on Test */ : 4; WR_N_DELAY (pDX->ADAPTERBASE + AR_8255B, o); /*********************************************************************/ /* Now do the 8273 Port B settings. */ /*********************************************************************/ o = 0; o |= (V24Out & IR_OV24RTS) /* user wants RTS up? */ ? 1 : 0; o |= (V24Out & IR_OV24DTR) /* user wants DTR up? */ ? 4 : 0; pDX->CmdStringSetPortB[2]= o; /* save value in 'set' cmd */ pDX->CmdStringResetPortB[2]= CAST(0xC0 | o, UCHAR); /* and also in 'reset' cmd */ Write8273Cmd (pDX,pDX->CmdStringSetPortB); /* turn on what should be on */ Write8273Cmd (pDX,pDX->CmdStringResetPortB); /*and turn off what shouldn't*/ return(!pDX->HardwareError); } /*****************************************************************************/ /* */ /* Name StartDMA ( */ /* PDX pDX */ /* PHYSICAL_ADDRESS PhysicalAddress */ /* USHORT BufferLength */ /* UCHAR OpCode */ /* ); */ /* */ /* Purpose Issues a Receive/Transmit command to the DMA. The address */ /* and length information are all in registers, other than the */ /* segment physical base address which is in global data. */ /* */ /* Params IN PhysicalAddress (not System VAS address!) */ /* BufferLength is maximum data length */ /* (although the 8273 controls exactly how many transfers are */ /* pulled). So this count really acts as a stopper and should */ /* (due to stupid design of 8273) be one less than overflow val */ /* OpCode is DMACmdRead/Write */ /* */ /* OUT Command issued to DMA. */ /* */ /* Modified: 31/03/88 Initial coding */ /* */ /*****************************************************************************/ void StartDMA(PDX pDX, PHYSICAL_ADDRESS PhysicalAddress, USHORT BufferLength, UCHAR OpCode) { PHYSICAL_ADDRESS a; USHORT b; TRACE_EVENT (ConfigData.DMAChannel EQ StandardDMAChannel)/* good ole channel one */ { /*************************************************************************/ /* Mask off the channel we're about to play with. */ /*************************************************************************/ IO_OUT (DMAMaskRegister, DMAMaskChannel1); /*************************************************************************/ /* The hard part is generating an address to give the DMA chip. We */ /* already know, because initialisation ensures it, that the buffer will */ /* not span a 64k boundary, but we have to generate the full physical */ /* address from the offset supplied in DI and the physical address of */ /* DGROUP read in during initialisation. */ /*************************************************************************/ IO_OUT (DMAFirstByteFlipFlop,0); /* data ignored, OUT resets FF */ a = PhysicalAddress; WR_N_DELAY (DMAPhysAddress, a.LowPart & 0xFF); /* first, set physaddr low byte */ a.LowPart = a.LowPart >> 8; WR_N_DELAY (DMAPhysAddress, a.LowPart & 0xFF); /* next, set physaddr high byte */ a.LowPart = a.LowPart >> 8; WR_N_DELAY (DMAPageRegister,a.LowPart & 0xFF) /* finally 64k page register value*/ /*************************************************************************/ /* Next give the chip the maximum size count passed in BufferLength. */ /*************************************************************************/ WR_N_DELAY (DMAFirstByteFlipFlop,0); /* ML did this so I will too ? */ b = BufferLength; WR_N_DELAY (DMACountRegister, a.LowPart & 0xFF); b = b >> 8; WR_N_DELAY (DMACountRegister, a.LowPart & 0xFF); /*************************************************************************/ /* Set up the mode value as passed in. */ /*************************************************************************/ WR_N_DELAY (DMAModeRegister, OpCode); /*************************************************************************/ /* Finally kick the chip off by clearing the channel mask bit. */ /*************************************************************************/ IO_OUT (DMAMaskRegister, DMAClearChannel1); } else { /*************************************************************************/ /* This must be an MCA machine with programmable DMA channels. The only */ /* actual option at present is '7' for the second MPA/A. */ /*************************************************************************/ ASSERT (pDX->ConfigData.DMAChannel EQ 7); /*************************************************************************/ /* Set the mask for this channel. */ /*************************************************************************/ IO_OUT (DMAExtdFnRegister, pDX->ConfigData.DMAChannel | MCADMASetMask); /*************************************************************************/ /* Give the DMA registers the data xfer start address. */ /*************************************************************************/ IO_OUT (DMAExtdFnRegister, pDX->ConfigData.DMAChannel | MCADMAAddress); a = PhysicalAddress; WR_N_DELAY (DMAExtdFnRegister, a.LowPart & 0xFF); /* and then set address bits 0-7 */ a.LowPart = a.LowPart >> 8; WR_N_DELAY (DMAExtdFnRegister, a.LowPart & 0xFF); /* set address bits 8 - 15 */ a.LowPart = a.LowPart >> 8; WR_N_DELAY (DMAExtdFnRegister, a.LowPart & 0xFF); /* set address bits 16 - 23 */ /*************************************************************************/ /* Now set up the maximum xfer count. */ /*************************************************************************/ IO_OUT (DMAExtdFnRegister, pDX->ConfigData.DMAChannel | MCADMACount); a.LowPart = BufferLength; WR_N_DELAY (DMAExtdFnRegister, a.LowPart & 0xFF); /* length low byte */ a.LowPart = a.LowPart >> 8; WR_N_DELAY (DMAExtdFnRegister, a.LowPart & 0xFF); /* length high byte */ /*************************************************************************/ /* Now set the mode register for the channel (read/write). This is done */ /* on the basis of the original old-style mode being for read or for */ /* write. */ /*************************************************************************/ IO_OUT (DMAExtdFnRegister, pDX->ConfigData.DMAChannel | MCADMAMode); WR_N_DELAY (DMAExtdFnRegister, (OpCode EQ DMACmdWrite) ? 0x0D : 0x05); /*************************************************************************/ /* Last job is to reset the mask register for the channel. */ /*************************************************************************/ IO_OUT (DMAExtdFnRegister, pDX->ConfigData.DMAChannel | MCADMAClearMask); } pDX->DMAIsActive = TRUE; TRACE_EVENT (SDM>); } /*****************************************************************************/ /* */ /* Name StopDMA */ /* */ /* Purpose Link Device Driver Stop DMA Routine. */ /* */ /* Kills the DMA by masking the appropriate channel. */ /* */ /* Params IN pDX */ /* Implicit: DMA channel allocated */ /* */ /* OUT Command issued to DMA. */ /* */ /*****************************************************************************/ void StopDMA (PDX pDX) { TRACE_EVENT (GrabbedResources & GRABBEDRESOURCE_GOTDMA); if (pDX->ConfigData.DMAChannel EQ StandardDMAChannel) { /*************************************************************************/ /* Channel #1 - set the mask directly. */ /*************************************************************************/ IO_OUT (DMAMaskRegister, DMAMaskChannel1); } else { /*************************************************************************/ /* Not channel 1 - set the mask using the Extended function addressing */ /* register. */ /*************************************************************************/ IO_OUT (DMAExtdFnRegister, pDX->ConfigData.DMAChannel | MCADMASetMask); } pDX->DMAIsActive = FALSE; TRACE_EVENT (ZDM>); } /**PROC+**********************************************************************/ /* */ /* Name: SynchReset8273 ( */ /* PDEVICE_OBJECT pDeviceObject */ /* ) */ /* */ /* Purpose: Reset the 8273 and set the 'Closing' flag so that following */ /* interrupts are ignored (until we disable) */ /* with interrupt processing */ /* */ /* Params: IN Context is really pDX */ /* */ /* Return Value:None */ /* */ /* Operation: */ /* */ /* */ /**PROC-**********************************************************************/ BOOLEAN SynchReset8273 (PVOID Context) { PDX pDX = (PDX) Context; TRACE_EVENT (AdapterIsClosing = TRUE; AdapterReset (pDX); TRACE_EVENT(XR8>); return (FALSE); } /**PROC+**********************************************************************/ /* */ /* Name: SynchTerminateAdapter( */ /* PDEVICE_OBJECT pDeviceObject */ /* ) */ /* */ /* Purpose: Close a particular device - stuff that needs to be interlockd*/ /* with interrupt processing */ /* */ /* Params: IN Context is really pDX */ /* */ /* Return Value:None */ /* */ /* Operation: */ /* */ /* */ /**PROC-**********************************************************************/ BOOLEAN SynchTerminateAdapter (PVOID Context) { PDX pDX = (PDX) Context; TRACE_EVENT (); return (FALSE); } /**PROC+**********************************************************************/ /* */ /* Name: SynchEntryPointOpen ( */ /* PDEVICE_OBJECT pDeviceObject */ /* ) */ /* */ /* Purpose: Initialise the next device (from GetDriverSpec) */ /* */ /* Params: IN Context is really pDX */ /* */ /* Return Value:BOOLEAN: True if object opened OK */ /* */ /* Operation: */ /* 1. Copy over pre-initialised data sequences */ /* */ /* */ /**PROC-**********************************************************************/ BOOLEAN SynchEntryPointOpen (PVOID Context) { PDX pDX = (PDX) Context; BOOLEAN rc; TRACE_EVENT (); return (rc); } /*****************************************************************************/ /* */ /* Name TerminateAdapter */ /* */ /* Purpose Link Device Driver Hardware Termination. */ /* */ /* Does the physical termination, but leaves assigned resurces */ /* still assigned */ /* */ /* Params IN pDX */ /* */ /* OUT Hardware is cleared down. */ /* */ /*****************************************************************************/ void TerminateAdapter (PDX pDX) { /***************************************************************************/ /* Reset the 8255 and 8273. */ /***************************************************************************/ IO_OUT (pDX->ADAPTERBASE + AR_8255B, A55_Reset8273On); /***************************************************************************/ /* Documentation says use a long delay while resetting! */ /* (but I'm blowed if I can find where it says it. Try 10 microsecs.) */ /***************************************************************************/ KeStallExecutionProcessor (10L); WR_N_DELAY (pDX->ADAPTERBASE + AR_8255B, A55_Reset8273Off); WR_N_DELAY (pDX->ADAPTERBASE + AR_8255B, A55_Reset8273Off); IO_OUT (pDX->ADAPTERBASE + AR_8255C, A55_ResetPortC); /***************************************************************************/ /* Ensure that an MPCA is completely 'turned off'. */ /***************************************************************************/ if (pDX->ConfigData.MPCAModePort NE 0) { IO_OUT (pDX->ConfigData.MPCAModePort, AC_MPCAD); /* MPCAD = disable */ TRACE_DATABYTE (Mpc, AC_MPCAD); } /***************************************************************************/ /* Check whether this channel was using the DMA and clear it down. */ /***************************************************************************/ if (pDX->GrabbedResources & GRABBEDRESOURCE_GOTDMA) { StopDMA(pDX); /* stop the channel doing anything */ pDX->GrabbedResources &= ~GRABBEDRESOURCE_GOTDMA; } } /****************************************************************************/ /* */ /* Name TxFSMActionAbort */ /* */ /* Purpose Aborts the current transmission. */ /* */ /* Params IN pDX. Transmitter going presumably. */ /* */ /* OUT Receiver started */ /* */ /****************************************************************************/ void TxFSMActionAbort (PDX pDX) { TRACE_EVENT (TAb:); Write8273Cmd (pDX, pDX->CmdStringAbortTransmit); TRACE_EVENT (TAb;); } /****************************************************************************/ /* */ /* Name TxFSMActionEndError */ /* */ /* Purpose Handles Tx error conditions by recording and retrying */ /* */ /* Updates statistics, */ /* Restarts the transmitter. */ /* */ /* Params IN pDX, 8273 result code in TxResult */ /* */ /* OUT Tx Stats updated, transmitter restarted */ /* */ /****************************************************************************/ void TxFSMActionEndError (PDX pDX) { TRACE_EVENT (TEr:); /*********************************************************************/ /* Check whether DMA is supported and kill the DMA Channel if so. */ /*********************************************************************/ if (pDX->GrabbedResources & GRABBEDRESOURCE_GOTDMA) { StopDMA (pDX); XASSERT (!pDX->DMAIsActive); } /***************************************************************************/ /* Increment the stats count and set the flag to pulse the event */ /***************************************************************************/ pDX->pIR->StatusCount++; pDX->DPCAction |= DPC_ACTION_PULSE; /***************************************************************************/ /* Basically we translate the error to an index into the stats array held */ /* in the interface record and increment it. */ /***************************************************************************/ TRACE_DATABYTE (TEb, pDX->TxResult); if (pDX->TxResult EQ ATxR_ErrTxUnderrun) { /*************************************************************************/ /* Check how many times we have tried to resend this frame. If this was */ /* the third attempt then give up, otherwise try again. */ /*************************************************************************/ pDX->TxConsecutiveUnderrunCount++; /* increment count of underruns */ if (pDX->TxConsecutiveUnderrunCount > 2) { TxFSMActionEndOK(pDX); /* forget this frame - pretend OK*/ } else { TxFSMEvent (pDX, TxFSMInputStart); /* tell FSM to send again */ } pDX->pIR->StatusArray[SA_TxUnderrun]++; /* transmitter underrun */ } else if (pDX->TxResult EQ ATxR_ErrTxCTSDrop) { TxFSMActionEndOK (pDX); /* forget this frame - pretend OK*/ pDX->pIR->StatusArray[SA_CTSDrop]++; /* clear to send dropped */ } else { TxFSMActionEndOK (pDX); /* forget this frame - pretend OK*/ pDX->pIR->StatusArray[SA_HardwareError]++; /* unknown result byte! */ } TRACE_EVENT (TEr;); } /****************************************************************************/ /* */ /* Name TxFSMActionEndOK */ /* */ /* Purpose Frame transmission complete. */ /* */ /* Handles the frame transmission completion actions. */ /* Updates the Tx buffer data. */ /* Starts the next transmission if data is available. */ /* */ /* Params IN pDX */ /* */ /* OUT Tx Data Buffer updated */ /* */ /****************************************************************************/ void TxFSMActionEndOK (PDX pDX) { USHORT FreedLength; /* how many bytes freed up from bufr */ TRACE_EVENT (TOk:); /*********************************************************************/ /* Check whether DMA is supported and kill the DMA Channel if so. */ /*********************************************************************/ if (pDX->GrabbedResources & GRABBEDRESOURCE_GOTDMA) { StopDMA (pDX); XASSERT (!pDX->DMAIsActive); } /*********************************************************************/ /* Clear the count of consecutive transmitter underruns. */ /*********************************************************************/ pDX->TxConsecutiveUnderrunCount = 0; /***************************************************************************/ /* Read the 'tail' and work out the length of the area freed up by the */ /* completion of this transmission. Divide that by two and add it to the */ /* 'maximum' Tx frame size on the interface. The 'max' is in fact half */ /* the total size of the transmit buffer (with some allowance for length */ /* words and rounding up) because the free area within the buffer can be */ /* broken into at most two non-contiguous sections. */ /***************************************************************************/ FreedLength = CAST(MAKEUSHORT (pDX->pSendBuf[pDX->TxNextToTransmit ], \ pDX->pSendBuf[pDX->TxNextToTransmit+1]) + 2, /* +2 : freeing up length as well */ USHORT); pDX->pIR->TxMaxFrSizeNow += (FreedLength + 1) / 2; /* making sure we round UP! */ XTRACE_ACTION (Tz, pDX->pIR->TxMaxFrSizeNow); /***************************************************************************/ /* Calculate the new next-to-transmit point, wrapping if necessary */ /***************************************************************************/ pDX->TxNextToTransmit += FreedLength; if (pDX->TxNextToTransmit >= pDX->TxStartUnusedArea) { /*************************************************************************/ /* The last buffer we sent was the right-most in the buffer - so we must */ /* reset the unused area and NextToTranmsit */ /*************************************************************************/ pDX->TxStartUnusedArea = SENDBUF_SIZE; pDX->TxNextToTransmit = 0; XTRACE_ACTION (Tu, pDX->TxStartUnusedArea); } XTRACE_ACTION (Tt, pDX->TxNextToTransmit); /***************************************************************************/ /* Now check whether there is more data in the buffer and send it. If */ /* there are no more frames, check whether the application has finished */ /* the current transmission and stop the transmitter if so. */ /***************************************************************************/ if (pDX->TxNextToTransmit NE pDX->TxNextToBuffer) { TxFSMEvent (pDX, TxFSMInputStart); } else { /*************************************************************************/ /* The buffer is empty so we need to stop the transmitter if we are half */ /* duplex and we have indeed sent the poll/final bit (as defined in the */ /* 'C' byte of the last transmit command (ooh, it's so tricky). */ /*************************************************************************/ if (BITSOFF(pDX->LinkOptionsByte, LinkOption_FullDuplex) && pDX->CmdStringTransmit[5] & POLLFINL ) { TxFSMEvent (pDX, TxFSMInputStop); } /*************************************************************************/ /* We have to tell the DLC software when the buffer goes empty. */ /*************************************************************************/ pDX->DPCAction |= DPC_ACTION_PULSE; /* do a PULSE-EVENT from the DPC */ } TRACE_EVENT (TOk;); } /*****************************************************************************/ /* */ /* Name TxFSMActionInvalid */ /* */ /* Purpose Error input to FSM */ /* */ /* This routine should never be called as it implies an */ /* 'impossible state/input combination. This is taken to mean */ /* that adapter status has been incorrectly reported and is */ /* treated as a hardware error. */ /* */ /* Params IN pDX */ /* */ /* OUT Error processing started by RQ FSM input */ /* */ /*****************************************************************************/ void TxFSMActionInvalid (PDX pDX) { TRACE_EVENT(T!!!); pDX->pIR->StatusArray[SA_HardwareError]++; pDX->pIR->StatusCount++; pDX->DPCAction |= DPC_ACTION_PULSE; } /****************************************************************************/ /* */ /* Name TxFSMActionStart */ /* */ /* Purpose Activates the Transmitter. */ /* */ /* Raise RTS (if not raised already). */ /* Start flag streaming. */ /* Stop the receiver (if not full duplex). */ /* Initiate the first transmission. */ /* */ /* Params IN Data held in Device Object */ /* */ /* OUT Transmitter Active and Transmit command issued. */ /* */ /****************************************************************************/ void TxFSMActionStart (PDX pDX) { TRACE_EVENT (TSt:); /***************************************************************************/ /* Put the transmitter into Flag Stream mode while it is active. */ /***************************************************************************/ pDX->CmdStringSetOpMode[2] |= 1; /* turn on flag stream mode */ Write8273Cmd (pDX, pDX->CmdStringSetOpMode); /***************************************************************************/ /* Ensure that RTS is up. */ /***************************************************************************/ pDX->CmdStringSetPortB[2] |= 1; /* 1 = RTS line - turn it up */ Write8273Cmd (pDX, pDX->CmdStringSetPortB); pDX->pIR->V24Out |= IR_OV24RTS; /* record fact that RTS is on! */ /***************************************************************************/ /* We are just starting the transmitter up. If the link is not full */ /* duplex then stop the receiver while we are transmitting. */ /***************************************************************************/ if (BITSOFF(pDX->LinkOptionsByte, LinkOption_FullDuplex)) { RxFSMEvent (pDX, RxFSMInputHold); } /***************************************************************************/ /* Lastly, start the first transmission. */ /***************************************************************************/ TxFSMActionXmitNext (pDX); TRACE_EVENT (TSt;); } /*****************************************************************************/ /* */ /* Name TxFSMActionStop */ /* */ /* Purpose Closes down the transmitter */ /* */ /* Drops RTS (if 2-wire) */ /* Stops flag stream and (if not HDLC) */ /* Re-enables the receiver (if half duplex) */ /* */ /* Params IN pDX */ /* */ /* OUT Receiver started if HDX */ /* */ /*****************************************************************************/ void TxFSMActionStop (PDX pDX) { TRACE_EVENT (TZap); /*********************************************************************/ /* Check whether DMA is supported and kill the DMA Channel if so. */ /*********************************************************************/ if (pDX->GrabbedResources & GRABBEDRESOURCE_GOTDMA) { StopDMA (pDX); XASSERT (!pDX->DMAIsActive); } if (BITSOFF(pDX->LinkOptionsByte, LinkOption_4Wire)) { /*************************************************************************/ /* Modem 2-wire : Turn off RTS and drop it in the interface flags. */ /*************************************************************************/ pDX->CmdStringSetPortB[2] &= ~1; pDX->CmdStringResetPortB[2] &= ~1; Write8273Cmd (pDX, pDX->CmdStringResetPortB); pDX->pIR->V24Out &= ~IR_OV24RTS; /* tell appl that RTS is off! */ } /*********************************************************************/ /* Turn off Flag Stream Mode. */ /*********************************************************************/ pDX->CmdStringSetOpMode[2] &= ~1; pDX->CmdStringResetOpMode[2]&= ~1; Write8273Cmd (pDX, pDX->CmdStringResetOpMode); pDX->LastPortA = Write8273Cmd (pDX, pDX->CmdStringReadPortA); /*********************************************************************/ /* Release the receiver if this is a half duplex link. */ /*********************************************************************/ if (BITSOFF(pDX->LinkOptionsByte, LinkOption_FullDuplex)) { RxFSMEvent (pDX, RxFSMInputRelease); } } /*****************************************************************************/ /* */ /* Name TxFSMActionXmitNext */ /* */ /* Purpose Transmits the Next Frame in the Buffer */ /* */ /* Set up the transmit command */ /* Set up the DMA (if used) */ /* Issue the transmit command. */ /* */ /* Params IN pDX, plus xmitter already active */ /* */ /* NextToTransmit: +0) length of buffered frame (A+C+data) */ /* +1) */ /* +2 A(ddress) */ /* +3 C(ontrol) */ /* */ /* Total buffer taken up is Length + 2. */ /* */ /* OUT Transmission under way. */ /* */ /*****************************************************************************/ void TxFSMActionXmitNext (PDX pDX) { USHORT LengthFor8273; UCHAR *p; UCHAR TempLo; UCHAR TempHi; TRACE_EVENT (TSn:); /***************************************************************************/ /* Before issuing the transmit command, read in the current Port A value */ /* as we won't be able to if both the transmitter and receiver become */ /* active concurrently. */ /***************************************************************************/ pDX->LastPortA = Write8273Cmd (pDX, pDX->CmdStringReadPortA); /***************************************************************************/ /* Read the current tail position and check for a buffer wrap (tail = */ /* current length of buffer). */ /***************************************************************************/ if (pDX->TxNextToTransmit >= pDX->TxStartUnusedArea) { /*************************************************************************/ /* The last buffer we sent was the right-most in the buffer - so we must */ /* reset the unused area and NextToTranmsit */ /*************************************************************************/ pDX->TxStartUnusedArea = SENDBUF_SIZE; pDX->TxNextToTransmit = 0; XTRACE_ACTION (Tu, pDX->TxStartUnusedArea); XTRACE_ACTION (Tt, pDX->TxNextToTransmit); } /***************************************************************************/ /* Found the next frame - read its length and set up Tx Command. */ /***************************************************************************/ p = &pDX->pSendBuf[pDX->TxNextToTransmit]; TempLo = *(p++); /* first two bytes are length*/ TempHi = *(p++); LengthFor8273 = CAST (TempLo + (TempHi <<8)-2,/* allow for the buffered A+C*/ USHORT); ASSERT (CAST(LengthFor8273, int ) < pDX->LinkMaxFrameSize); ASSERT (CAST(LengthFor8273, short) >= 0); pDX->CmdStringTransmit[2] = LOBYTE(LengthFor8273); /* and put it into the cmd */ pDX->CmdStringTransmit[3] = HIBYTE(LengthFor8273); /***************************************************************************/ /* and set up the (buffered) A & C in the command */ /***************************************************************************/ TRC_IN_DW (*p); pDX->CmdStringTransmit[4] = *(p++); /* next two bytes are A&C */ pDX->CmdStringTransmit[5] = *(p++); pDX->pTxPIOData = p; /* next is PIO data start pos*/ TRACE_EVENT (TPIO); TRACE_DWORD (pDX->pTxPIOData); /***************************************************************************/ /* Now prime the DMA if necessary */ /***************************************************************************/ if (pDX->GrabbedResources & GRABBEDRESOURCE_GOTDMA) { PHYSICAL_ADDRESS L; if (pDX->DMAIsActive) { StopDMA (pDX); XASSERT (!pDX->DMAIsActive); } L = pDX->SendBufPhysAddr; L.LowPart += pDX->TxNextToTransmit + 4; StartDMA(pDX, L, LengthFor8273, /* but 8273 will control how many snt*/ DMACmdRead /* set the mode */ ); XASSERT (pDX->DMAIsActive); } /******************************************************************/ /* Lastly, issue the transmit command to the 8273. */ /******************************************************************/ Write8273Cmd (pDX, pDX->CmdStringTransmit); TRACE_EVENT (TSn;); } /****************************************************************************/ /* */ /* Name TxFSMEvent */ /* */ /* Purpose Interprets inputs from the Request handlers and from the */ /* ISR and calls the relevant action routines. */ /* */ /* Params IN pDX, and FSM input */ /* */ /* OUT Action routine called and new state set. */ /* */ /****************************************************************************/ void TxFSMEvent (PDX pDX, int Input) { TXFSMENTRY *e = &TxFSM [Input] [pDX->TxFSMCurState]; /***************************************************************************/ /* The FSM consists of a 'state' which is actually the address of the */ /* current FSM 'column'. The 'input' is an offset down the column at */ /* which is located an action routine address and a new state. */ /***************************************************************************/ TRACE_EVENT (TxFSMCurState < TXFSMSTATECOUNT); ASSERT (Input < TXFSMINPUTCOUNT); TRACE_EVENTNAME(TxFSMStateNames[pDX->TxFSMCurState]); TRACE_EVENTNAME(TxFSMInputNames[Input]); /***************************************************************************/ /* Reset the state to the new state specified in the FSM table */ /* (Important to do this before the actions because some of them then do */ /* recursive calls to FSMEvent */ /***************************************************************************/ pDX->TxFSMCurState = e->NewState; ASSERT (e->NewState < TXFSMSTATECOUNT); TRACE_EVENTNAME(TxFSMStateNames[pDX->TxFSMCurState]); (*(e->pTxActionRoutine)) (pDX); TRACE_EVENT (TFE>); } /**PROC+**********************************************************************/ /* */ /* Name Write8273Cmd ( */ /* PDX pDX */ /* UCHAR *Cmd; */ /* ); */ /* */ /* Purpose Send out a command array (in , , format*/ /* */ /* Params IN pDX - the device extension */ /* IN Cmd - ptr to first char of command */ /* */ /* Return Value If command is READ 8273 PORT A/B return value read, otherwise*/ /* return value is meaningless. */ /* */ /* Operation None */ /* */ /* Notes 1. may be 0 */ /* 2. Write8273Cmd (OS/2 DD precursor of this routine used to */ /* return carry on error. We don't bother as this was only */ /* check in one place. */ /* */ /**PROC-**********************************************************************/ UCHAR Write8273Cmd (PDX pDX, UCHAR *Cmd) { int ParameterCount; UCHAR Result = 0; TRACE_EVENT (HardwareError) /* stop if previous error or error */ return(0); /* this time */ IO_OUT (pDX->ADAPTERBASE+AR_8273S, /* status reg also command register */ *Cmd++); /* command is first byte */ if (*(Cmd-1) EQ 0x22 || *(Cmd-1) EQ 0x23) { /* what we sent out was Read Port A/B*/ WAITUNTIL(pDX,AS_CRBFF,EQ,AS_CRBFF);/* wait for result buff full to go on*/ Result = IO_IN (pDX->ADAPTERBASE+AR_8273P); } ParameterCount = *(Cmd++); /* parameter count is next */ while (ParameterCount--) { WAITUNTIL (pDX, /* wait for busy to go on, but */ (AS_CMBSY|AS_CMBFF|AS_CPBFF), /* command and param buffs are clear */ EQ, AS_CMBSY); IO_OUT (pDX->ADAPTERBASE+AR_8273P, /* write params to parameter port */ (UCHAR)*(Cmd++)); } TRACE_EVENT (Cmd>); return(Result); } /*****************************************************************************/ /* */ /* Name LogDriverError */ /* */ /* Purpose Allocates an error log entry, copies over the given info */ /* and writes it to the error log file. */ /* */ /* Params IN pDeviceObject */ /* IN FinalStatus : NT Status returned to calling service */ /* IN UniqueErrorValue : bottom word is the resource message id */ /* IN MajorFunctionCode : for the current Irp */ /* IN IoControlCode : Ioctl code */ /* */ /*****************************************************************************/ VOID LogDriverError ( PDEVICE_OBJECT pDeviceObject, NTSTATUS FinalStatus, ULONG UniqueErrorValue, UCHAR MajorFunctionCode, ULONG IoControlCode ) { PIO_ERROR_LOG_PACKET ErrorLogEntry; TRACE_EVENT (ErrorCode = UniqueErrorValue; ErrorLogEntry->FinalStatus = FinalStatus; ErrorLogEntry->UniqueErrorValue = UniqueErrorValue; ErrorLogEntry->MajorFunctionCode = MajorFunctionCode; ErrorLogEntry->IoControlCode = IoControlCode; ErrorLogEntry->SequenceNumber = pDeviceObject->ReferenceCount; IoWriteErrorLogEntry(ErrorLogEntry); } TRACE_EVENT (LDE>); } /*****************************************************************************/ /* Notes. */ /* */ /* 1. The OS/2 device driver registers the MPA interrupt 3 as exclusive but */ /* then shares it internally. We can't do this unfortunately on NT because */ /* IoConnectInterrupt requires the device object as a parameter. We will */ /* therefore have to register MPAA interrupts as shared. The problem with */ /* the shared interrupts on OS/2 was that phantom interrupts could be */ /* generated (and, because we didn't have to service any cards, the */ /* interrupt request stayed asserted). As NT is well-designed and tested, */ /* this won't occur so we can use shared interrupts. */ /* */ /* 2. Should we action receive results immediately? As background, we do */ /* action all events immediately except for Transmitter results which are */ /* passed to the DPC routine. NT religious dogma says we should not action */ /* the rx result immediately either, but pass them to the DPC routine. The */ /* danger in doing this is that we will miss the start of the next frame. */ /* This isn't a problem in DMA, because we're just letting it rip. But in */ /* PIO mode, we can be some while getting to restart the receiver if we drop */ /* down to DPC level. So... action al rx interrupts immediately. */ /* */ /* */ /*****************************************************************************/ //on termination, unlock any locked memory and free the MDLs (e.g. //InterfaceRecordMdl) // //need to initialise InterfaceRecordMdl = NULL