/*++ Copyright (c) 1995 Microsoft Corporation Module Name: control.c Abstract: Author: Shie-Lin Tzong (shielint) Apr-23-1995 Most of the code is adapted from PCI bus extender. Environment: Kernel mode only. Revision History: --*/ #include "busp.h" VOID MbpiCompleteDeviceControl ( NTSTATUS Status, PHAL_DEVICE_CONTROL_CONTEXT Context, PDEVICE_DATA DeviceData, PBOOLEAN Sync ); #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE,MbpControlWorker) #pragma alloc_text(PAGE,MbpCompleteDeviceControl) #endif VOID MbpStartWorker ( VOID ) /*++ Routine Description: This function is used to start a worker thread. Arguments: None. Return Value: None. --*/ { ULONG workerQueued; if (!MbpWorkerQueued) { workerQueued = ExInterlockedExchangeUlong ( &MbpWorkerQueued, 1, MbpSpinLock ); if (!workerQueued) { ExQueueWorkItem (&MbpWorkItem, DelayedWorkQueue); } } } VOID MbpQueueCheckBus ( IN PBUS_HANDLER BusHandler ) /*++ Routine Description: This function enqueues Bus check request to buscheck list. Arguments: BusHandler - supplies a pointer to the bus handler of the bus to be checked. Return Value: None. --*/ { ExInterlockedInsertTailList ( &MbpCheckBusList, &((PMB_BUS_EXTENSION)BusHandler->BusData)->CheckBus, &MbpSpinlock ); MbpStartWorker(); } VOID MbpControlWorker ( IN PVOID WorkerContext ) /*++ Routine Description: This function is called by a system worker thread. The worker thread dequeues any SlotControls which need to be processed and dispatches them. It then checks for any check bus request. Arguments: WorkerContext - supplies a pointer to a context for the worker. Here it is always NULL. Return Value: None. --*/ { PLIST_ENTRY entry; PMB_BUS_EXTENSION busExtension; PHAL_DEVICE_CONTROL_CONTEXT context; PAGED_CODE (); // // process check bus // for (; ;) { entry = ExInterlockedRemoveHeadList ( &MbpCheckBusList, &MbpSpinlock ); if (!entry) { break; } busExtension = CONTAINING_RECORD ( entry, MB_BUS_EXTENSION, CheckBus ); MbpCheckBus (busExtension->BusHandler); } // // Reset worker item for next time // ExInitializeWorkItem (&MbpWorkItem, MbpControlWorker, NULL); ExInterlockedExchangeUlong (&MbpWorkerQueued, 0, MbpSpinLock); // // Dispatch pending device controls // for (; ;) { entry = ExInterlockedRemoveHeadList ( &MbpControlWorkerList, &MbpSpinlock ); if (!entry) { // // All done, exit the loop. // break; } context = CONTAINING_RECORD ( entry, HAL_DEVICE_CONTROL_CONTEXT, ContextWorkQueue, ); MbpDispatchControl (context); } } VOID MbpDispatchControl ( PHAL_DEVICE_CONTROL_CONTEXT Context ) /*++ Routine Description: This function dispatches a DeviceControl to the appropiate handler. If the slot is busy, the DeviceControl may be queued for dispatching at a later time Arguments: Context - The DeviceControl context to dispatch Return Value: None. --*/ { PDEVICE_CONTROL_HANDLER deviceControlHandler; PMB_BUS_EXTENSION busExtension; PDEVICE_DATA deviceData; KIRQL oldIrql; BOOLEAN enqueueIt; PLIST_ENTRY link; deviceControlHandler = (PDEVICE_CONTROL_HANDLER) Context->ContextControlHandler; deviceData = DeviceHandler2DeviceData (Context->DeviceControl.DeviceHandler); busExtension = (PMB_BUS_EXTENSION)Context->Handler->BusData; // // Get access to the slot specific data. // ExAcquireFastMutex(&MbpMutex); // // Verify the device data is still valid // if (!deviceData->Flags & DEVICE_FLAGS_VALID) { // // Caller has invalid handle, or handle to a different device // DebugPrint ((DEBUG_MESSAGE, "PnpBios: DeviceControl has invalid device handler \n" )); Context->DeviceControl.Status = STATUS_NO_SUCH_DEVICE; ExReleaseFastMutex(&MbpMutex); HalCompleteDeviceControl (Context); return ; } // // Check to see if this request can be begun now // link = (PLIST_ENTRY) &Context->ContextWorkQueue; enqueueIt = deviceControlHandler->BeginDeviceControl (deviceData, Context); if (enqueueIt) { // // Enqueue this command to be handled when the slot is no longer busy. // KeAcquireSpinLock (&MbpSpinlock, &oldIrql); InsertTailList (&busExtension->DeviceControl, link); KeReleaseSpinLock (&MbpSpinlock, oldIrql); ExReleaseFastMutex(&MbpMutex); return ; } // // Dispatch the function to it's handler // ExReleaseFastMutex(&MbpMutex); deviceControlHandler->ControlHandler (deviceData, Context); } VOID MbpiCompleteDeviceControl ( NTSTATUS Status, PHAL_DEVICE_CONTROL_CONTEXT Context, PDEVICE_DATA DeviceData, PBOOLEAN Sync ) /*++ Routine Description: This function is used to complete a SlotControl. If another SlotControl was delayed on this device, this function will dispatch them Arguments: Status - supplies a NTSTATUS code for the completion. Context - supplies a pointer to the original device control context. DeviceData - supplies a pointer to the device data to be completed. Sync - supplies a BOOLEAN variable to indicate Return Value: --*/ { KIRQL oldIrql; PLIST_ENTRY link; PBOOLEAN busyFlag; BOOLEAN startWorker = FALSE; PMB_BUS_EXTENSION busExtension; PDEVICE_HANDLER_OBJECT deviceHandler; busyFlag = (PBOOLEAN) Context->ContextBusyFlag; deviceHandler = DeviceData2DeviceHandler(DeviceData); busExtension = (PMB_BUS_EXTENSION)Context->Handler->BusData; // // Pass it to the hal for completion // Context->DeviceControl.Status = Status; HalCompleteDeviceControl (Context); // // Get access to the slot specific data. // KeAcquireSpinLock (&MbpSpinlock, &oldIrql); // // Clear appropiate busy flag // *busyFlag = FALSE; // // Check to see if there are any pending device controls for // this device. If so, requeue them to the worker thread // for (link = busExtension->DeviceControl.Flink; link != &busExtension->DeviceControl; link = link->Flink) { Context = CONTAINING_RECORD (link, HAL_DEVICE_CONTROL_CONTEXT, ContextWorkQueue); if (Context->DeviceControl.DeviceHandler == deviceHandler) { RemoveEntryList (link); InsertTailList (&MbpControlWorkerList, link); startWorker = TRUE; break; } } KeReleaseSpinLock (&MbpSpinlock, oldIrql); if (startWorker) { MbpStartWorker (); } } VOID MbpCompleteDeviceControl ( NTSTATUS Status, PHAL_DEVICE_CONTROL_CONTEXT Context, PDEVICE_DATA DeviceData ) /*++ Routine Description: This function is used to complete a DeviceControl. If another DeviceControl was delayed on this device, this function will dispatch them Arguments: Return Value: --*/ { PAGED_CODE(); MbpiCompleteDeviceControl ( Status, Context, DeviceData, &DeviceData->SyncBusy ); } BOOLEAN FASTCALL MbBCtlNone ( PDEVICE_DATA DeviceData, PHAL_DEVICE_CONTROL_CONTEXT Context ) /*++ Routine Description: This function is used to indicate there is no synchronization for this device control function. Arguments: Context - supplies a pointer to the device control context. DeviceData - supplies a pointer to the device data to be completed. Return Value: A boolean value to indicate if the request needs to be enqueued for later processing. --*/ { // // No synchronization needed for this SlotControl // Context->ContextBusyFlag = (ULONG) &MbpNoBusyFlag; return FALSE; } BOOLEAN FASTCALL MbBCtlSync ( PDEVICE_DATA DeviceData, PHAL_DEVICE_CONTROL_CONTEXT Context ) /*++ Routine Description: This function is used to synchronize device control request. it checks the state (busy/not busy) of the slot and returns a boolean flag to indicate whether the request can be serviced immediately or it needs to be enqueued for later processing. Arguments: DeviceData - supplies a pointer to the device data to be completed. Context - supplies a pointer to the device control context. Return Value: A boolean value to indicate if the request needs to be enqueued for later processing. --*/ { // // This is a sync command, verify the slot is not busy with a different // command. // if (DeviceData->SyncBusy) { // // Enqueue this command to be handled when the slot is no longer busy. // return TRUE; } // // Don't enqueue, dispatch it now // DeviceData->SyncBusy = TRUE; Context->ContextBusyFlag = (ULONG) &DeviceData->SyncBusy; return FALSE; } BOOLEAN FASTCALL MbBCtlEject ( PDEVICE_DATA DeviceData, PHAL_DEVICE_CONTROL_CONTEXT Context ) /*++ Routine Description: This function is used to synchronize Eject device control request. it checks the state (busy/not busy) of the device and returns a boolean flag to indicate whether the request can be serviced immediately or it needs to be enqueued for later processing. Arguments: DeviceData - supplies a pointer to the device data to be completed. Context - supplies a pointer to the slot control context. Return Value: A boolean value to indicate if the request needs to be enqueued for later processing. --*/ { BOOLEAN busy; // // If Slot is busy, then wait // busy = MbBCtlSync (DeviceData, Context); if (!busy) { // // Set no device in the slot // DeviceData->Flags &= ~DEVICE_FLAGS_VALID; DebugPrint ((DEBUG_MESSAGE, "PnpBios: set handle invalid - slot %x for Eject request.\n", DeviceDataSlot(DeviceData))); } return busy; } BOOLEAN FASTCALL MbBCtlLock ( PDEVICE_DATA DeviceData, PHAL_DEVICE_CONTROL_CONTEXT Context ) /*++ Routine Description: This function is used to synchronize LOCK device control request. it checks the state (busy/not busy) of the device and returns a boolean flag to indicate whether the request can be serviced immediately or it needs to be enqueued for later processing. Arguments: DeviceData - supplies a pointer to the device data to be completed. Context - supplies a pointer to the device control context. Return Value: A boolean value to indicate if the request needs to be enqueued for later processing. --*/ { BOOLEAN busy; // // If Slot is busy, then wait // busy = MbBCtlSync (DeviceData, Context); #if 0 if (!busy) { lock = ((PBCTL_SET_CONTROL) Context->DeviceControl.Buffer)->Control; if (!lock) { // // Set no device in the slot // DeviceData->Flags &= ~DEVICE_FLAGS_VALID; } } #endif return busy; } VOID MbCtlEject ( PDEVICE_DATA DeviceData, PHAL_DEVICE_CONTROL_CONTEXT Context ) /*++ Routine Description: This function is used to eject the docking station in the docking station slot. Arguments: Context - supplies a pointer to the device control context. DeviceData - supplies a pointer to the device data to be completed. Return Value: None. --*/ { NTSTATUS status = STATUS_INVALID_PARAMETER; if (DeviceData->Flags & DEVICE_FLAGS_DOCKING_STATION) { status = MbpReplyEjectEvent(DeviceDataSlot(DeviceData), TRUE); } MbpCompleteDeviceControl (status, Context, DeviceData); } VOID MbCtlLock ( PDEVICE_DATA DeviceData, PHAL_DEVICE_CONTROL_CONTEXT Context ) /*++ Routine Description: This function is used to reject the Pnp Bios About-to-undock event. To accept the about-to-undock event, the EJECT device control function should be used. If lock = TRUE, it will be handled as a reject eject request. If lock = FALSE, it will be handled as an accept reject rquest. Arguments: DeviceData - supplies a pointer to the device data to be completed. Context - supplies a pointer to the device control context. Return Value: None. --*/ { BOOLEAN lock; NTSTATUS status = STATUS_INVALID_PARAMETER; lock = ((PBCTL_SET_CONTROL) Context->DeviceControl.Buffer)->Control; if (DeviceData->Flags & DEVICE_FLAGS_DOCKING_STATION) { status = MbpReplyEjectEvent(DeviceDataSlot(DeviceData), (BOOLEAN)!lock); } MbpCompleteDeviceControl (status, Context, DeviceData); } VOID MbCtlQueryEject ( PDEVICE_DATA DeviceData, PHAL_DEVICE_CONTROL_CONTEXT Context ) /*++ Routine Description: This function returns a referenced pointer to a callback object which the bus extender will notify whenever a given device's eject botton is pressed. Arguments: DeviceData - supplies a pointer to the device data to be completed. Context - supplies a pointer to the device control context. Return Value: None. --*/ { NTSTATUS status = STATUS_INVALID_PARAMETER; PULONG callback; callback =(PULONG)((PBCTL_SET_CONTROL) Context->DeviceControl.Buffer); if (DeviceData->Flags == DEVICE_FLAGS_DOCKING_STATION) { if (MbpEjectCallbackObject) { status = STATUS_SUCCESS; *callback = (ULONG)MbpEjectCallbackObject; } else { status = STATUS_UNSUCCESSFUL; } } MbpCompleteDeviceControl (status, Context, DeviceData); } VOID MbCtlQueryDeviceCapabilities ( PDEVICE_DATA DeviceData, PHAL_DEVICE_CONTROL_CONTEXT Context ) /*++ Routine Description: This function returns the BCTL_DEVICE_CAPABILITIES structure to the caller specified buffer. Arguments: DeviceData - supplies a pointer to the device data to be completed. Context - supplies a pointer to the device control context. Return Value: None. --*/ { PBCTL_DEVICE_CAPABILITIES capabilities; capabilities = (PBCTL_DEVICE_CAPABILITIES) Context->DeviceControl.Buffer; capabilities->PowerSupported = FALSE; capabilities->ResumeSupported = FALSE; capabilities->LockSupported = FALSE; if (DeviceData->Flags && DEVICE_FLAGS_EJECT_SUPPORTED) { capabilities->EjectSupported = TRUE; } else { capabilities->EjectSupported = FALSE; } MbpCompleteDeviceControl (STATUS_SUCCESS, Context, DeviceData); }