static VOID XenBus_EvtIoWrite(WDFQUEUE queue, WDFREQUEST request, size_t length) { NTSTATUS status; PXENPCI_DEVICE_DATA xpdd = GetXpdd(WdfIoQueueGetDevice(queue)); WDFFILEOBJECT file_object = WdfRequestGetFileObject(request); PXENPCI_DEVICE_INTERFACE_DATA xpdid = GetXpdid(file_object); KIRQL old_irql; WDFREQUEST read_request; PUCHAR buffer; PUCHAR src_ptr; ULONG src_len; PUCHAR dst_ptr; ULONG copy_len; struct xsd_sockmsg *rep; xenbus_read_queue_item_t *list_entry; watch_context_t *watch_context; PCHAR watch_path; PCHAR watch_token; PCHAR msg; FUNCTION_ENTER(); status = WdfRequestRetrieveInputBuffer(request, length, &buffer, NULL); ASSERT(NT_SUCCESS(status)); src_ptr = (PUCHAR)buffer; src_len = (ULONG)length; dst_ptr = xpdid->xenbus.u.buffer + xpdid->xenbus.len; while (src_len != 0) { KdPrint((__DRIVER_NAME " %d bytes of write buffer remaining\n", src_len)); /* get a complete msg header */ if (xpdid->xenbus.len < sizeof(xpdid->xenbus.u.msg)) { copy_len = min(sizeof(xpdid->xenbus.u.msg) - xpdid->xenbus.len, src_len); if (!copy_len) continue; memcpy(dst_ptr, src_ptr, copy_len); dst_ptr += copy_len; src_ptr += copy_len; src_len -= copy_len; xpdid->xenbus.len += copy_len; } /* exit if we can't get that */ if (xpdid->xenbus.len < sizeof(xpdid->xenbus.u.msg)) continue; /* get a complete msg body */ if (xpdid->xenbus.len < sizeof(xpdid->xenbus.u.msg) + xpdid->xenbus.u.msg.len) { copy_len = min(sizeof(xpdid->xenbus.u.msg) + xpdid->xenbus.u.msg.len - xpdid->xenbus.len, src_len); if (!copy_len) continue; memcpy(dst_ptr, src_ptr, copy_len); dst_ptr += copy_len; src_ptr += copy_len; src_len -= copy_len; xpdid->xenbus.len += copy_len; } /* exit if we can't get that */ if (xpdid->xenbus.len < sizeof(xpdid->xenbus.u.msg) + xpdid->xenbus.u.msg.len) { continue; } switch (xpdid->xenbus.u.msg.type) { case XS_WATCH: case XS_UNWATCH: KeAcquireSpinLock(&xpdid->lock, &old_irql); watch_context = (watch_context_t *)ExAllocatePoolWithTag(NonPagedPool, sizeof(watch_context_t), XENPCI_POOL_TAG); watch_path = (PCHAR)(xpdid->xenbus.u.buffer + sizeof(struct xsd_sockmsg)); watch_token = (PCHAR)(xpdid->xenbus.u.buffer + sizeof(struct xsd_sockmsg) + strlen(watch_path) + 1); RtlStringCbCopyA(watch_context->path, ARRAY_SIZE(watch_context->path), watch_path); RtlStringCbCopyA(watch_context->token, ARRAY_SIZE(watch_context->path), watch_token); watch_context->file_object = file_object; if (xpdid->xenbus.u.msg.type == XS_WATCH) InsertTailList(&xpdid->xenbus.watch_list_head, &watch_context->entry); KeReleaseSpinLock(&xpdid->lock, old_irql); if (xpdid->xenbus.u.msg.type == XS_WATCH) msg = XenBus_AddWatch(xpdd, XBT_NIL, watch_path, XenPci_IoWatch, watch_context); else msg = XenBus_RemWatch(xpdd, XBT_NIL, watch_path, XenPci_IoWatch, watch_context); KeAcquireSpinLock(&xpdid->lock, &old_irql); if (msg != NULL) { rep = ExAllocatePoolWithTag(NonPagedPool, sizeof(struct xsd_sockmsg) + strlen(msg) + 1, XENPCI_POOL_TAG); rep->type = XS_ERROR; rep->req_id = xpdid->xenbus.u.msg.req_id; rep->tx_id = xpdid->xenbus.u.msg.tx_id; rep->len = (ULONG)(strlen(msg) + 0); RtlStringCbCopyA((PCHAR)(rep + 1), strlen(msg) + 1, msg); if (xpdid->xenbus.u.msg.type == XS_WATCH) RemoveEntryList(&watch_context->entry); } else { if (xpdid->xenbus.u.msg.type == XS_WATCH) { WdfObjectReference(file_object); } else { RemoveEntryList(&watch_context->entry); WdfObjectDereference(file_object); } rep = ExAllocatePoolWithTag(NonPagedPool, sizeof(struct xsd_sockmsg), XENPCI_POOL_TAG); rep->type = xpdid->xenbus.u.msg.type; rep->req_id = xpdid->xenbus.u.msg.req_id; rep->tx_id = xpdid->xenbus.u.msg.tx_id; rep->len = 0; } KeReleaseSpinLock(&xpdid->lock, old_irql); break; default: rep = XenBus_Raw(xpdd, &xpdid->xenbus.u.msg); break; } xpdid->xenbus.len = 0; KeAcquireSpinLock(&xpdid->lock, &old_irql); list_entry = (xenbus_read_queue_item_t *)ExAllocatePoolWithTag(NonPagedPool, sizeof(xenbus_read_queue_item_t), XENPCI_POOL_TAG); list_entry->data = rep; list_entry->length = sizeof(*rep) + rep->len; list_entry->offset = 0; InsertTailList(&xpdid->xenbus.read_list_head, (PLIST_ENTRY)list_entry); // Check if someone was waiting for the answer already status = WdfIoQueueRetrieveNextRequest(xpdid->xenbus.io_queue, &read_request); if (NT_SUCCESS(status)) { WDF_REQUEST_PARAMETERS parameters; KdPrint((__DRIVER_NAME " post-write: found pending read\n")); WDF_REQUEST_PARAMETERS_INIT(¶meters); WdfRequestGetParameters(read_request, ¶meters); XenBus_ProcessReadRequest(xpdid->xenbus.io_queue, read_request, parameters.Parameters.Read.Length); WdfRequestComplete(read_request, STATUS_SUCCESS); } else { KdPrint((__DRIVER_NAME " post-write: no pending read (%08x)\n", status)); } KeReleaseSpinLock(&xpdid->lock, old_irql); } KdPrint((__DRIVER_NAME " completing request with length %d\n", length)); WdfRequestCompleteWithInformation(request, STATUS_SUCCESS, length); FUNCTION_EXIT(); }
NTSTATUS NfcCxSCPresentAbsentDispatcherSetRequest( _In_ PNFCCX_FDO_CONTEXT FdoContext, _In_ PNFCCX_SC_PRESENT_ABSENT_DISPATCHER Dispatcher, _In_ WDFREQUEST Request ) /*++ Routine Description: Stores a request in the dispatcher. If a request is already pending in the dispatcher, the new request will be completed with STATUS_DEVICE_BUSY. Arguments: FdoContext - Pointer to the FDO Context Dispatcher - Pointer to the Dispatcher. Request - The request to store in the dispatcher. Return Value: NTSTATUS --*/ { NTSTATUS status; bool powerReferenceAcquired = false; bool cancelCallbackSet = false; TRACE_FUNCTION_ENTRY(LEVEL_VERBOSE); WDFFILEOBJECT fileObject = WdfRequestGetFileObject(Request); PNFCCX_FILE_CONTEXT fileContext = NfcCxFileGetContext(fileObject); // Pre-check if there is a current request. if (ReadPointerAcquire((void**)&Dispatcher->CurrentRequest) != nullptr) { status = STATUS_DEVICE_BUSY; TRACE_LINE(LEVEL_ERROR, "An Is Present/Absent request is already pending. %!STATUS!", status); goto Done; } // Allocate and initialize the request context PNFCCX_SC_REQUEST_CONTEXT requestContext = nullptr; WDF_OBJECT_ATTRIBUTES contextAttributes; WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&contextAttributes, NFCCX_SC_REQUEST_CONTEXT); status = WdfObjectAllocateContext(Request, &contextAttributes, (void**)&requestContext); if (!NT_SUCCESS(status)) { TRACE_LINE(LEVEL_ERROR, "Failed to set request's context. %!STATUS!", status); goto Done; } requestContext->Dispatcher = Dispatcher; // Add a power reference (if required). if (Dispatcher->PowerManaged) { status = NfcCxPowerFileAddReference(FdoContext->Power, fileContext, NfcCxPowerReferenceType_Proximity); if (!NT_SUCCESS(status)) { TRACE_LINE(LEVEL_VERBOSE, "Failed to acquire power reference. %!STATUS!", status); goto Done; } powerReferenceAcquired = true; } // Setup cancel callback. status = WdfRequestMarkCancelableEx(Request, NfcCxSCPresentAbsentDispatcherRequestCanceled); if (!NT_SUCCESS(status)) { TRACE_LINE(LEVEL_ERROR, "Failed to set request canceled callback. %!STATUS!", status); goto Done; } // Add a ref-count to the request, which is owned by the cancel callback. WdfObjectReference(Request); cancelCallbackSet = true; // Try to set the current request. void* previousRequest = InterlockedCompareExchangePointer((void**)&Dispatcher->CurrentRequest, /*exchange*/ Request, /*compare*/ nullptr); // Check if we already have a pending request. if (previousRequest != nullptr) { status = STATUS_DEVICE_BUSY; TRACE_LINE(LEVEL_ERROR, "An Is Present/Absent request is already pending. %!STATUS!", status); goto Done; } Done: if (!NT_SUCCESS(status)) { if (cancelCallbackSet) { NTSTATUS unmarkStatus = WdfRequestUnmarkCancelable(Request); if (unmarkStatus != STATUS_CANCELLED) { // Cancel callback will not be called. // So release cancel callback's request ref-count. WdfObjectDereference(Request); } } if (powerReferenceAcquired) { NfcCxPowerFileRemoveReference(FdoContext->Power, fileContext, NfcCxPowerReferenceType_Proximity); } } TRACE_FUNCTION_EXIT_NTSTATUS(LEVEL_VERBOSE, status); return status; }