gendrv.c

来自「ReactOS是一些高手根据Windows XP的内核编写出的类XP。内核实现机」· C语言 代码 · 共 1,827 行 · 第 1/4 页

C
1,827
字号
    RtlAnsiStringToUnicodeString(&symb_link, &string, TRUE);
    IoDeleteSymbolicLink(&symb_link);
    RtlFreeUnicodeString(&symb_link);

    if (pdev_ext->desc_buf)
    {
        usb_dbg_print(DBGLVL_MAXIMUM, ("gendrv_delete_device(): delete desc_buf\n"));
        usb_free_mem(pdev_ext->desc_buf);
        pdev_ext->desc_buf = NULL;

    }

    if (dev_obj->ReferenceCount == 0)
    {
        ExAcquireFastMutex(&pdrvr_ext->drvr_ext_mutex);
        RemoveEntryList(&pdev_ext->dev_obj_link);
        pdev_ext->ext_drvr_entry->ref_count--;  //the ext_drvr_entry is actually in pdrvr_ext, so lock it.
        ExReleaseFastMutex(&pdrvr_ext->drvr_ext_mutex);

        IoDeleteDevice(dev_obj);
        return TRUE;
    }

    // borrow from umss's work routine
    return umss_schedule_workitem(dev_obj, gendrv_deferred_delete_device, NULL, 0);
}

// must have the drvr_ext_mutex acquired.
PGENDRV_EXT_DRVR_ENTRY
gendrv_alloc_ext_drvr_entry(PGENDRV_DRVR_EXTENSION pdrvr_ext)
{
    LONG i;
    if (pdrvr_ext == NULL)
        return NULL;
    if (pdrvr_ext->ext_drvr_count == GENDRV_MAX_EXT_DRVR)
        return NULL;
    for(i = 0; i < GENDRV_MAX_EXT_DRVR; i++)
    {
        if (pdrvr_ext->ext_drvr_array[i].drvr_link.Flink == NULL &&
            pdrvr_ext->ext_drvr_array[i].drvr_link.Blink == NULL)
        {
            return &pdrvr_ext->ext_drvr_array[i];
        }
    }
    return NULL;
}

// must have the drvr_ext_mutex acquired.
VOID
gendrv_release_ext_drvr_entry(PGENDRV_DRVR_EXTENSION pdrvr_ext, PGENDRV_EXT_DRVR_ENTRY pentry)
{
    if (pdrvr_ext == NULL || pentry == NULL)
        return;
    RtlZeroMemory(pentry, sizeof(GENDRV_EXT_DRVR_ENTRY));
    InitializeListHead(&pentry->dev_list);
    return;
}

NTSTATUS
gendrv_open_reg_key(OUT PHANDLE handle,
                    IN HANDLE base_handle OPTIONAL,
                    IN PUNICODE_STRING keyname, IN ACCESS_MASK desired_access, IN BOOLEAN create)
/*++

Routine Description:

    Opens or creates a VOLATILE registry key using the name passed in based
    at the BaseHandle node.

Arguments:

    Handle - Pointer to the handle which will contain the registry key that
        was opened.

    BaseHandle - Handle to the base path from which the key must be opened.

    KeyName - Name of the Key that must be opened/created.

    DesiredAccess - Specifies the desired access that the caller needs to
        the key.

    Create - Determines if the key is to be created if it does not exist.

Return Value:

   The function value is the final status of the operation.

--*/
{
    OBJECT_ATTRIBUTES object_attr;
    ULONG disposition;

    //
    // Initialize the object for the key.
    //

    InitializeObjectAttributes(&object_attr,
                               keyname, OBJ_CASE_INSENSITIVE, base_handle, (PSECURITY_DESCRIPTOR) NULL);

    //
    // Create the key or open it, as appropriate based on the caller's
    // wishes.
    //

    if (create)
    {
        return ZwCreateKey(handle,
                           desired_access,
                           &object_attr, 0, (PUNICODE_STRING) NULL, REG_OPTION_VOLATILE, &disposition);
    }
    else
    {
        return ZwOpenKey(handle, desired_access, &object_attr);
    }
    return STATUS_INVALID_PARAMETER;
}

NTSTATUS
gendrv_get_key_value(IN HANDLE KeyHandle, IN PWSTR ValueName, OUT PKEY_VALUE_FULL_INFORMATION * Information)
/*++

Routine Description:

    This routine is invoked to retrieve the data for a registry key's value.
    This is done by querying the value of the key with a zero-length buffer
    to determine the size of the value, and then allocating a buffer and
    actually querying the value into the buffer.

    It is the responsibility of the caller to free the buffer.

Arguments:

    KeyHandle - Supplies the key handle whose value is to be queried

    ValueName - Supplies the null-terminated Unicode name of the value.

    Information - Returns a pointer to the allocated data buffer.

Return Value:

    The function value is the final status of the query operation.

--*/
{
    UNICODE_STRING unicodeString;
    NTSTATUS status;
    PKEY_VALUE_FULL_INFORMATION infoBuffer;
    ULONG keyValueLength;

    PAGED_CODE();

    RtlInitUnicodeString(&unicodeString, ValueName);

    //
    // Figure out how big the data value is so that a buffer of the
    // appropriate size can be allocated.
    //

    status = ZwQueryValueKey(KeyHandle,
                             &unicodeString, KeyValueFullInformation, (PVOID) NULL, 0, &keyValueLength);
    if (status != STATUS_BUFFER_OVERFLOW && status != STATUS_BUFFER_TOO_SMALL)
    {
        return status;
    }

    //
    // Allocate a buffer large enough to contain the entire key data value.
    //

    infoBuffer = usb_alloc_mem(NonPagedPool, keyValueLength);
    if (!infoBuffer)
    {
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    //
    // Query the data for the key value.
    //

    status = ZwQueryValueKey(KeyHandle,
                             &unicodeString,
                             KeyValueFullInformation, infoBuffer, keyValueLength, &keyValueLength);
    if (!NT_SUCCESS(status))
    {
        usb_free_mem(infoBuffer);
        return status;
    }

    //
    // Everything worked, so simply return the address of the allocated
    // buffer to the caller, who is now responsible for freeing it.
    //

    *Information = infoBuffer;
    return STATUS_SUCCESS;
}

VOID
gendrv_startio(IN PDEVICE_OBJECT dev_obj, IN PIRP irp)
{
    PIO_STACK_LOCATION irp_stack;
    ULONG ctrl_code;
    PUSB_DEV_MANAGER dev_mgr;
    USE_NON_PENDING_IRQL;

    if (dev_obj == NULL || irp == NULL)
        return;

    // standard process from walter oney
    IoAcquireCancelSpinLock(&old_irql);
    if (irp != dev_obj->CurrentIrp || irp->Cancel)
    {
        // already move on to other irp
        IoReleaseCancelSpinLock(old_irql);
        return;
    }
    else
    {
        (void)IoSetCancelRoutine(irp, NULL);
    }
    IoReleaseCancelSpinLock(old_irql);

    irp->IoStatus.Information = 0;

    irp_stack = IoGetCurrentIrpStackLocation(irp);
    ctrl_code = irp_stack->Parameters.DeviceIoControl.IoControlCode;
    dev_mgr = ((PDEVEXT_HEADER) dev_obj->DeviceExtension)->dev_mgr;

    if (irp_stack->MajorFunction != IRP_MJ_DEVICE_CONTROL &&
        irp_stack->MajorFunction != IRP_MJ_INTERNAL_DEVICE_CONTROL)
    {
        GENDRV_COMPLETE_START_IO(dev_obj, STATUS_INVALID_DEVICE_REQUEST, irp);
    }

    switch (ctrl_code)
    {
        case IOCTL_SUBMIT_URB_RD:
        case IOCTL_SUBMIT_URB_NOIO:
        case IOCTL_SUBMIT_URB_WR:
        {
            PURB purb;
            ULONG endp_idx, if_idx, user_buffer_length = 0;
            PUCHAR user_buffer = NULL;
            PUSB_DEV pdev;
            DEV_HANDLE endp_handle;
            PUSB_ENDPOINT pendp;

            NTSTATUS status;

            if (irp_stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(URB))
            {
                GENDRV_COMPLETE_START_IO(dev_obj, STATUS_INVALID_PARAMETER, irp);
            }

            purb = (PURB) irp->AssociatedIrp.SystemBuffer;
            if (ctrl_code == IOCTL_SUBMIT_URB_RD || ctrl_code == IOCTL_SUBMIT_URB_WR)
            {
                if (irp_stack->MajorFunction == IRP_MJ_DEVICE_CONTROL)
                {
                    user_buffer = MmGetSystemAddressForMdl(irp->MdlAddress);
                    user_buffer_length = irp_stack->Parameters.DeviceIoControl.OutputBufferLength;
                    if (user_buffer_length == 0)
                        GENDRV_COMPLETE_START_IO(dev_obj, STATUS_INVALID_PARAMETER, irp);
                }
                else
                {
                    if (purb->data_buffer == NULL || purb->data_length == 0)
                        GENDRV_COMPLETE_START_IO(dev_obj, STATUS_INVALID_PARAMETER, irp);
                    user_buffer = purb->data_buffer;
                    user_buffer_length = purb->data_length;
                }
            }

            purb->reference = 0;
            endp_handle = purb->endp_handle;

            if (usb_query_and_lock_dev(dev_mgr, endp_handle & ~0xffff, &pdev) != STATUS_SUCCESS)
            {
                GENDRV_COMPLETE_START_IO(dev_obj, STATUS_IO_DEVICE_ERROR, irp);
            }

            lock_dev(pdev, FALSE);
            if (dev_state(pdev) == USB_DEV_STATE_ZOMB || (dev_state(pdev) < USB_DEV_STATE_ADDRESSED))

            {
                status = STATUS_INVALID_DEVICE_STATE;
                goto ERROR_OUT1;
            }

            if (dev_state(pdev) == USB_DEV_STATE_ADDRESSED && !default_endp_handle(endp_handle))
            {
                status = STATUS_DEVICE_NOT_READY;
                goto ERROR_OUT1;
            }

            if_idx = if_idx_from_handle(endp_handle);
            endp_idx = endp_idx_from_handle(endp_handle);

            //if_idx exceeds the upper limit
            if (pdev->usb_config)
            {
                if (if_idx >= pdev->usb_config->if_count
                    || endp_idx >= pdev->usb_config->interf[if_idx].endp_count)
                {
                    if (!default_endp_handle(endp_handle))
                    {
                        status = STATUS_INVALID_DEVICE_STATE;
                        goto ERROR_OUT1;
                    }
                }
            }

            endp_from_handle(pdev, endp_handle, pendp);

            // FIXME: don't know what evil will let loose
            if (endp_type(pendp) != USB_ENDPOINT_XFER_CONTROL)
            {
                if (user_buffer_length > 16)
                {
                    status = STATUS_INVALID_PARAMETER;
                    goto ERROR_OUT1;
                }
            }

            purb->pirp = irp;
            purb->context = dev_mgr;
            purb->reference = ctrl_code;

            if (ctrl_code == IOCTL_SUBMIT_URB_RD || ctrl_code == IOCTL_SUBMIT_URB_WR)
            {
                purb->data_buffer = user_buffer;
                purb->data_length = user_buffer_length;
                purb->completion = disp_urb_completion;
            }
            else
            {
                purb->completion = disp_noio_urb_completion;
            }

            unlock_dev(pdev, FALSE);

            //
            // we have to register irp before the urb is scheduled to
            // avoid race condition. 
            //
            ASSERT(dev_mgr_register_irp(dev_mgr, irp, purb));
            //
            // the irp can not be canceled at this point, since it is
            // now the current irp and not in any urb queue. dev_mgr_cancel_irp
            // can not find it and simply return.
            //
            //      FIXME: there is a time window that the irp is registered and 
            // the urb is not queued. In the meantime, the cancel
            // request may come and cause the irp removed from the irp 
            // queue while fail to cancel due to urb not in any urb queue .
            // Thus from that point on, the irp can not be canceled till it 
            // is completed or hanging there forever.
            //
            status = usb_submit_urb(dev_mgr, purb);
            if (status != STATUS_PENDING)
            {
                // unmark the pending bit
                IoGetCurrentIrpStackLocation((irp))->Control &= ~SL_PENDING_RETURNED;
                dev_mgr_remove_irp(dev_mgr, irp);
            }
            usb_unlock_dev(pdev);
            if (status != STATUS_PENDING)
            {
                irp->IoStatus.Status = status;
                GENDRV_COMPLETE_START_IO(dev_obj, status, irp);
            }
            return;

          ERROR_OUT1:
            unlock_dev(pdev, FALSE);
            usb_unlock_dev(pdev);
            irp->IoStatus.Information = 0;
            GENDRV_COMPLETE_START_IO(dev_obj, status, irp);
        }
    }
    GENDRV_COMPLETE_START_IO(dev_obj, STATUS_INVALID_DEVICE_REQUEST, irp);
}

VOID
gendrv_clean_up_queued_irps(PDEVICE_OBJECT dev_obj)
{
    // called when device may not function or about to be removed, need cleanup
    KIRQL cancelIrql;
    PIRP irp, cur_irp;
    PKDEVICE_QUEUE_ENTRY packet;
    LIST_ENTRY cancel_irps, *pthis;

    //
    // cancel all the irps in the queue
    //
    if (dev_obj == NULL)
        return;

    InitializeListHead(&cancel_irps);

    // remove the irps from device queue
    IoAcquireCancelSpinLock(&cancelIrql);
    cur_irp = dev_obj->CurrentIrp;
    while ((packet = KeRemoveDeviceQueue(&dev_obj->DeviceQueue)))
    {
        irp = struct_ptr(packet, IRP, Tail.Overlay.DeviceQueueEntry);
        InsertTailList(&cancel_irps, &irp->Tail.Overlay.DeviceQueueEntry.DeviceListEntry);
    }
    IoReleaseCancelSpinLock(cancelIrql);

    // cancel the irps in process
    // we did not cancel the current irp, it will be done by hcd when
    // disconnect is detected.
    // remove_irp_from_list( &dev_mgr->irp_list, cur_irp, dev_mgr );

    while (IsListEmpty(&cancel_irps) == FALSE)
    {
        pthis = RemoveHeadList(&cancel_irps);
        irp = struct_ptr(pthis, IRP, Tail.Overlay.DeviceQueueEntry.DeviceListEntry);
        irp->IoStatus.Information = 0;
        irp->IoStatus.Status = STATUS_CANCELLED;
        IoCompleteRequest(irp, IO_NO_INCREMENT);
    }
    return;
}

VOID
NTAPI
gendrv_cancel_queued_irp(PDEVICE_OBJECT dev_obj, PIRP pirp)
{
    // cancel routine for irps queued in the device queue
    PUSB_DEV_MANAGER dev_mgr;
    PDEVEXT_HEADER pdev_ext_hdr;

    pdev_ext_hdr = (PDEVEXT_HEADER) dev_obj->DeviceExtension;
    dev_mgr = pdev_ext_hdr->dev_mgr;

    if (dev_obj->CurrentIrp == pirp)
    {
        // just before start_io set the cancel routine to null
        IoReleaseCancelSpinLock(pirp->CancelIrql);
        // we did not IoStartNextPacket, leave it for dev_mgr_cancel_irp, that
        // is user have to call CancelIo again.
        return;
    }

    KeRemoveEntryDeviceQueue(&dev_obj->DeviceQueue, &pirp->Tail.Overlay.DeviceQueueEntry);
    IoReleaseCancelSpinLock(pirp->CancelIrql);

    pirp->IoStatus.Information = 0;
    pirp->IoStatus.Status = STATUS_CANCELLED;
    IoCompleteRequest(pirp, IO_NO_INCREMENT);
    // the device queue is moved on, no need to call IoStartNextPacket
    return;
}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?