/*++ Copyright (c) 1991 Microsoft Corporation Module Name: dlcinfo.c Abstract: The module implements the Query/Set information commands. It also provides the statistics services for DLC api. Contents: GetDlcErrorCounters DlcQueryInformation DlcSetInformation GetOpenSapAndStationCount SetupGroupSaps Author: Antti Saarenheimo (o-anttis) 29-AUG-1991 Revision History: --*/ #include static ULONG aTokenringLogOid[ADAPTER_ERROR_COUNTERS] = { OID_802_5_LINE_ERRORS, 0, OID_802_5_BURST_ERRORS, OID_802_5_AC_ERRORS, OID_802_5_ABORT_DELIMETERS, 0, OID_802_5_LOST_FRAMES, OID_GEN_RCV_NO_BUFFER, OID_802_5_FRAME_COPIED_ERRORS, OID_802_5_FREQUENCY_ERRORS, OID_802_5_TOKEN_ERRORS }; static ULONG aEthernetLogOid[ADAPTER_ERROR_COUNTERS] = { OID_802_3_XMIT_TIMES_CRS_LOST, 0, OID_802_3_RCV_ERROR_ALIGNMENT, 0, OID_GEN_XMIT_ERROR, 0, OID_802_3_XMIT_MAX_COLLISIONS, OID_GEN_RCV_NO_BUFFER, 0, 0, 0 }; static ULONG aFddiLogOid[ADAPTER_ERROR_COUNTERS] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; VOID GetDlcErrorCounters( IN PDLC_FILE_CONTEXT pFileContext, IN PUCHAR pAdapterErrors ) /*++ Routine Description: Procedure reads the cumulative 32-bit adapter error counters from ethernet or token-ring adapter and returns 8-bit DLC error counters, that supports read and read & reset commands. It also maintains local copies of the NDIS error counters in the process specific adapter context, because NDIS counters cannot be reset. Arguments: pFileContext - DLC address object pAdapterErrors - DLC errors counters, if NULL => NDIS values are copied to file context. Return Value: None. --*/ { LLC_NDIS_REQUEST Request; PULONG pOidBuffer; ULONG counter; UINT i; UINT Status; ASSUME_IRQL(DISPATCH_LEVEL); // // Token-ring and ethernet uses different error counters // switch (pFileContext->ActualNdisMedium) { case NdisMedium802_3: pOidBuffer = aEthernetLogOid; break; case NdisMedium802_5: pOidBuffer = aTokenringLogOid; break; case NdisMediumFddi: pOidBuffer = aFddiLogOid; break; } // // read all error counters having non null NDIS OID and // decrement the previous error count value from it. // Overflowed DLC error counter is set 255 (the maximum). // Request.Ndis.RequestType = NdisRequestQueryInformation; Request.Ndis.DATA.QUERY_INFORMATION.InformationBuffer = &counter; Request.Ndis.DATA.QUERY_INFORMATION.InformationBufferLength = sizeof(counter); for (i = 0; i < ADAPTER_ERROR_COUNTERS; i++) { if (pOidBuffer[i] != 0) { Request.Ndis.DATA.QUERY_INFORMATION.Oid = pOidBuffer[i]; LEAVE_DLC(pFileContext); RELEASE_DRIVER_LOCK(); Status = LlcNdisRequest(pFileContext->pBindingContext, &Request); ACQUIRE_DRIVER_LOCK(); ENTER_DLC(pFileContext); if (Status != STATUS_SUCCESS) { #if DBG if ( Status != STATUS_NOT_SUPPORTED ){ // Only print real errors. DbgPrint("DLC.GetDlcErrorCounters: LlcNdisRequest returns %x\n", Status); } #endif counter = 0; } else if ((counter - pFileContext->NdisErrorCounters[i] > 255) && (pAdapterErrors != NULL)) { counter = 255; } else { counter -= pFileContext->NdisErrorCounters[i]; } if (pAdapterErrors != NULL) { pAdapterErrors[i] = (UCHAR)counter; } pFileContext->NdisErrorCounters[i] += counter; } } } NTSTATUS DlcQueryInformation( IN PIRP pIrp, IN PDLC_FILE_CONTEXT pFileContext, IN PNT_DLC_PARMS pDlcParms, IN ULONG InputBufferLength, IN ULONG OutputBufferLength ) /*++ Routine Description: The routine returns the DLC specific information of any DLC object. Arguments: pIrp - current io request packet pFileContext - DLC address object pDlcParms - the current parameter block InputBufferLength - the length of input parameters OutputBufferLength - the length of output parameters Return Value: NTSTATUS: STATUS_SUCCESS STATUS_INVALID_COMMAND --*/ { NTSTATUS Status = STATUS_SUCCESS; PVOID hClientHandle = pFileContext->pBindingContext; PDLC_OBJECT pDlcObject; PLLC_ADAPTER_DLC_INFO pDlcAdapter; UNREFERENCED_PARAMETER(pIrp); UNREFERENCED_PARAMETER(InputBufferLength); ASSUME_IRQL(DISPATCH_LEVEL); // // NOTE: DlcQueryBuffer output buffer size was not checked by // DlcDeviceIoControl. For each class we check the size // based on what it will copy. Although it did check that // the input buffer size was at least NT_DLC_QUERY_INFORMATION_INPUT // size. // switch (pDlcParms->DlcGetInformation.Header.InfoClass) { case DLC_INFO_CLASS_DLC_ADAPTER: // union NT_DLC_PARMS // LLC_ADAPTER_DLC_INFO if (OutputBufferLength < sizeof(LLC_ADAPTER_DLC_INFO)) { Status = STATUS_BUFFER_TOO_SMALL; break; } // // The output data is just written to the // beginning of the current system buffer. // pDlcAdapter = (PLLC_ADAPTER_DLC_INFO)pDlcParms; GetOpenSapAndStationCount(pFileContext, &pDlcAdapter->OpenSaps, (PUCHAR)&pDlcAdapter->OpenStations ); pDlcAdapter->MaxSap = 127; pDlcAdapter->MaxStations = 255; pDlcAdapter->AvailStations = (UCHAR)255 - pDlcAdapter->OpenStations; break; case DLC_INFO_CLASS_ADAPTER_LOG: // union NT_DLC_PARMS (pDlcParms) // union NT_DLC_QUERY_INFORMATION_PARMS DlcGetInformation // union NT_DLC_QUERY_INFORMATION_OUTPUT Info // union LLC_ADAPTER_LOG AdapterLog if (OutputBufferLength < sizeof(LLC_ADAPTER_LOG)) { Status = STATUS_BUFFER_TOO_SMALL; break; } GetDlcErrorCounters(pFileContext, (PUCHAR)&pDlcParms->DlcGetInformation); break; case DLC_INFO_CLASS_LINK_STATION: // union NT_DLC_PARMS (pDlcParms) // union NT_DLC_QUERY_INFORMATION_PARMS DlcGetInformation // union NT_DLC_QUERY_INFORMATION_OUTPUT Info // struct _DlcLinkInfoGet // USHORT MaxInformationField if (OutputBufferLength < sizeof(USHORT)) { Status = STATUS_BUFFER_TOO_SMALL; break; } Status = GetLinkStation(pFileContext, pDlcParms->DlcGetInformation.Header.StationId, &pDlcObject ); if (Status == STATUS_SUCCESS) { // // Round always the information field length to the full // dword even number => some copy operations may be much // faster (usually not, but worth of effor in any way) // pDlcParms->DlcGetInformation.Info.Link.MaxInformationField = (USHORT)(pDlcObject->u.Link.MaxInfoFieldLength & -3); } break; case DLC_INFO_CLASS_STATISTICS: case DLC_INFO_CLASS_STATISTICS_RESET: Status = GetStation(pFileContext, pDlcParms->DlcGetInformation.Header.StationId, &pDlcObject ); if (Status != STATUS_SUCCESS) { return Status; } hClientHandle = pDlcObject->hLlcObject; // // **** FALL THROUGH **** // default: // // LLC will return invalid command, if it is not supported // LEAVE_DLC(pFileContext); RELEASE_DRIVER_LOCK(); // // LlcQueryInformation validates buffer size before copying. // Status = LlcQueryInformation(hClientHandle, pDlcParms->DlcGetInformation.Header.InfoClass, (PLLC_QUERY_INFO_BUFFER)&(pDlcParms->DlcGetInformation), (UINT)OutputBufferLength ); ACQUIRE_DRIVER_LOCK(); ENTER_DLC(pFileContext); break; } return (NTSTATUS)Status; } NTSTATUS DlcSetInformation( IN PIRP pIrp, IN PDLC_FILE_CONTEXT pFileContext, IN PNT_DLC_PARMS pDlcParms, IN ULONG InputBufferLength, IN ULONG OutputBufferLength ) /*++ Routine Description: The routine sets new parameter values for the DLC objects. Arguments: pIrp - current io request packet pFileContext - DLC address object pDlcParms - the current parameter block InputBufferLength - the length of input parameters Return Value: NTSTATUS STATUS_SUCCESS STATUS_INVALID_COMMAND --*/ { NTSTATUS Status = STATUS_SUCCESS; PDLC_OBJECT pDlcObject; UNREFERENCED_PARAMETER(pIrp); UNREFERENCED_PARAMETER(InputBufferLength); UNREFERENCED_PARAMETER(OutputBufferLength); ASSUME_IRQL(DISPATCH_LEVEL); switch (pDlcParms->DlcSetInformation.Header.InfoClass) { case DLC_INFO_CLASS_LINK_STATION: case DLC_INFO_CLASS_DIRECT_INFO: Status = GetStation(pFileContext, pDlcParms->DlcSetInformation.Header.StationId, &pDlcObject ); if (Status != STATUS_SUCCESS) { return Status; } LEAVE_DLC(pFileContext); Status = LlcSetInformation(pDlcObject->hLlcObject, pDlcParms->DlcSetInformation.Header.InfoClass, (PLLC_SET_INFO_BUFFER)&( pDlcParms->DlcSetInformation.Info.LinkStation), sizeof(DLC_LINK_PARAMETERS) ); break; case DLC_INFO_CLASS_DLC_TIMERS: LEAVE_DLC(pFileContext); Status = LlcSetInformation(pFileContext->pBindingContext, pDlcParms->DlcSetInformation.Header.InfoClass, (PLLC_SET_INFO_BUFFER)&(pDlcParms->DlcSetInformation.Info.TimerParameters), sizeof(LLC_TICKS) ); break; case DLC_INFO_CLASS_SET_FUNCTIONAL: case DLC_INFO_CLASS_RESET_FUNCTIONAL: case DLC_INFO_CLASS_SET_GROUP: case DLC_INFO_CLASS_SET_MULTICAST: LEAVE_DLC(pFileContext); Status = LlcSetInformation(pFileContext->pBindingContext, pDlcParms->DlcSetInformation.Header.InfoClass, (PLLC_SET_INFO_BUFFER)&(pDlcParms->DlcSetInformation.Info.Broadcast), sizeof(TR_BROADCAST_ADDRESS) ); break; case DLC_INFO_CLASS_GROUP: // // Setup DLC group SAPs. Group saps are used as a common address // of a SAP group. They can only receive frames. // Status = GetStation(pFileContext, pDlcParms->DlcSetInformation.Header.StationId, &pDlcObject ); if (Status != STATUS_SUCCESS) { return Status; } Status = SetupGroupSaps(pFileContext, pDlcObject, (UINT)pDlcParms->DlcSetInformation.Info.Sap.GroupCount, (PUCHAR)pDlcParms->DlcSetInformation.Info.Sap.GroupList ); LEAVE_DLC(pFileContext); break; default: LEAVE_DLC(pFileContext); Status = DLC_STATUS_INVALID_COMMAND; break; }; ENTER_DLC(pFileContext); return Status; } // // Function returns the number of open sap and link // stations for a dlc application. // VOID GetOpenSapAndStationCount( IN PDLC_FILE_CONTEXT pFileContext, OUT PUCHAR pOpenSaps, OUT PUCHAR pOpenStations ) { UINT i, SapCount = 0; for (i = 1; i < MAX_SAP_STATIONS; i++) { if (pFileContext->SapStationTable[i] != NULL) { SapCount++; } } *pOpenSaps = (UCHAR)SapCount; if (pFileContext->SapStationTable[0] != NULL) { SapCount++; } *pOpenStations = (UCHAR)(pFileContext->DlcObjectCount - SapCount); } NTSTATUS SetupGroupSaps( IN PDLC_FILE_CONTEXT pFileContext, IN PDLC_OBJECT pDlcObject, IN UINT GroupSapCount, IN PUCHAR pGroupSapList ) /*++ Routine Description: The routine deletes the current group saps list and and new group saps. Fi the new group sap list is empty, then we just delete all current group saps. Arguments: pFileContext - DLC context pDlcObject - SAP object GroupSapCount - number of new group saps pGroupSapList - list of new group saps Return Value: NTSTATUS: STATUS_SUCCESS STATUS_INVALID_COMMAND --*/ { UINT i; UINT OpenOptions; // // We must first remove all old groups sap defined for the // sap station (if any) // if (pDlcObject->u.Sap.GroupSapHandleList != NULL) { for (i = 0; i < pDlcObject->u.Sap.GroupSapCount; i++) { if (pDlcObject->u.Sap.GroupSapHandleList[i] != NULL) { LEAVE_DLC(pFileContext); LlcCloseStation(pDlcObject->u.Sap.GroupSapHandleList[i], NULL); ENTER_DLC(pFileContext); } } FREE_MEMORY_FILE(pDlcObject->u.Sap.GroupSapHandleList); pDlcObject->u.Sap.GroupSapHandleList = NULL; } // // Note: the old group saps can be deleted with a null list! // pDlcObject->u.Sap.GroupSapCount = (UCHAR)GroupSapCount; if (GroupSapCount != 0) { pDlcObject->u.Sap.GroupSapHandleList = (PVOID*)ALLOCATE_ZEROMEMORY_FILE( GroupSapCount * sizeof(PVOID) ); if (pDlcObject->u.Sap.GroupSapHandleList == NULL) { return DLC_STATUS_NO_MEMORY; } // // The groups sap implementation is based on the fact, // that the lower module things to run a normal sap. // The routing of UI, TEST and XID frames for all // saps sends the incoming U-frames automatically // all real saps registered to a sap. This method // could theoretically use very much memory if there were // very many saps and group saps (eg: 50 * 50 = 2500 * 100), // but that situation is actually impossible. // SNA saps (3) have one command group sap and even SNA // sap is very rarely used (not used by CommServer) // for (i = 0; i < pDlcObject->u.Sap.GroupSapCount; i++) { UINT Status; OpenOptions = 0; if (~(pDlcObject->u.Sap.OptionsPriority & XID_HANDLING_BIT)) { OpenOptions = LLC_HANDLE_XID_COMMANDS; } LEAVE_DLC(pFileContext); Status = LlcOpenSap(pFileContext->pBindingContext, (PVOID)pDlcObject, (UINT)pGroupSapList[i] | 1, OpenOptions, &pDlcObject->u.Sap.GroupSapHandleList[i] ); ENTER_DLC(pFileContext); if (Status != STATUS_SUCCESS) { return Status; } } } return STATUS_SUCCESS; }