bulkonly.c

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

C
793
字号
/**
 * bulkonly.c - USB driver stack project for Windows NT 4.0
 *
 * Copyright (c) 2002-2004 Zhiming  mypublic99@yahoo.com
 *
 * This program/include file is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as published
 * by the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program/include file is distributed in the hope that it will be
 * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program (in the main directory of the distribution, the file
 * COPYING); if not, write to the Free Software Foundation,Inc., 59 Temple
 * Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include "usbdriver.h"
#include <ntddscsi.h>

#define OLYMPUS_CSW( pdev_EXT, staTUS ) \
( ( ( pdev_EXT )->flags & UMSS_DEV_FLAG_OLYMPUS_DEV ) ? ( ( staTUS ) == CSW_OLYMPUS_SIGNATURE ) : FALSE )

BOOLEAN umss_clear_pass_through_length(PIO_PACKET io_packet);

NTSTATUS umss_bulkonly_send_sense_req(PUMSS_DEVICE_EXTENSION pdev_ext);

VOID umss_bulkonly_send_cbw_completion(IN PURB purb, IN PVOID context);

VOID umss_bulkonly_transfer_data(PUMSS_DEVICE_EXTENSION pdev_ext);

VOID umss_sync_submit_urb_completion(PURB purb, PVOID context);

NTSTATUS umss_sync_submit_urb(PUMSS_DEVICE_EXTENSION pdev_ext, PURB purb);

VOID umss_bulkonly_get_status(PUMSS_DEVICE_EXTENSION pdev_ext);

VOID umss_bulkonly_transfer_data_complete(PURB purb, PVOID reference);

VOID umss_bulkonly_reset_pipe_and_get_status(IN PVOID reference);

VOID umss_bulkonly_reset_recovery(IN PVOID reference);

VOID umss_bulkonly_get_status_complete(IN PURB purb, IN PVOID context);

/*++
Routine Description:

    Handler for all I/O requests using bulk-only protocol.

 Arguments:

    DeviceExtension - Device extension for our FDO.

Return Value:

    NONE

--*/
NTSTATUS
umss_bulkonly_startio(IN PUMSS_DEVICE_EXTENSION pdev_ext, IN PIO_PACKET io_packet)
{
    PCOMMAND_BLOCK_WRAPPER cbw;
    NTSTATUS status;

    if (pdev_ext == NULL || io_packet == NULL || io_packet->pirp == NULL)
        return STATUS_INVALID_PARAMETER;

    pdev_ext->retry = TRUE;
    RtlCopyMemory(&pdev_ext->io_packet, io_packet, sizeof(pdev_ext->io_packet));

    // Setup the command block wrapper for this request
    cbw = &pdev_ext->cbw;
    cbw->dCBWSignature = CBW_SIGNATURE;
    cbw->dCBWTag = 0;
    cbw->dCBWDataTransferLength = io_packet->data_length;
    cbw->bmCBWFlags = (io_packet->flags & USB_DIR_IN) ? 0x80 : 0;
    cbw->bCBWLun = io_packet->lun;
    cbw->bCBWLength = io_packet->cdb_length;
    RtlCopyMemory(cbw->CBWCB, io_packet->cdb, sizeof(cbw->CBWCB));

    RtlZeroMemory(&pdev_ext->csw, sizeof(pdev_ext->csw));
    // Send the command block wrapper to the device.
    // Calls UMSS_BulkOnlySendCBWComplete when transfer completes.
    status = umss_bulk_transfer(pdev_ext,
                                USB_DIR_OUT,
                                cbw, sizeof(COMMAND_BLOCK_WRAPPER), umss_bulkonly_send_cbw_completion);

    return status;
}


NTSTATUS
umss_bulk_transfer(IN PUMSS_DEVICE_EXTENSION pdev_ext,
                   IN UCHAR trans_dir, IN PVOID buf, IN ULONG buf_length, IN PURBCOMPLETION completion)
{
    PURB purb;
    NTSTATUS status;
    DEV_HANDLE endp_handle;

    if (pdev_ext == NULL || buf == NULL || completion == NULL)
        return STATUS_INVALID_PARAMETER;

    if (buf_length > (ULONG) MAX_BULK_TRANSFER_LENGTH)
        return STATUS_INVALID_PARAMETER;

    purb = usb_alloc_mem(NonPagedPool, sizeof(URB));

    if (purb == NULL)
        return STATUS_NO_MEMORY;

    if (trans_dir == USB_DIR_OUT)
    {
        endp_handle = usb_make_handle((pdev_ext->dev_handle >> 16), pdev_ext->if_idx, pdev_ext->out_endp_idx);
    }
    else
    {
        endp_handle = usb_make_handle((pdev_ext->dev_handle >> 16), pdev_ext->if_idx, pdev_ext->in_endp_idx);
    }

    UsbBuildInterruptOrBulkTransferRequest(purb, endp_handle, buf, buf_length, completion, pdev_ext, 0);
    dev_mgr_register_irp(pdev_ext->dev_mgr, pdev_ext->io_packet.pirp, purb);
    status = usb_submit_urb(pdev_ext->dev_mgr, purb);
    if (status == STATUS_PENDING)
    {
        return status;
    }

    dev_mgr_remove_irp(pdev_ext->dev_mgr, pdev_ext->io_packet.pirp);
    if (purb)
    {
        usb_free_mem(purb);
        purb = NULL;
    }
    return status;
}

VOID
umss_bulkonly_send_cbw_completion(IN PURB purb, IN PVOID context)
{
    NTSTATUS status;
    PUMSS_DEVICE_EXTENSION pdev_ext;

    pdev_ext = (PUMSS_DEVICE_EXTENSION) context;

    status = purb->status;
    dev_mgr_remove_irp(pdev_ext->dev_mgr, pdev_ext->io_packet.pirp);

    if ((pdev_ext->io_packet.flags & IOP_FLAG_STAGE_MASK) == IOP_FLAG_STAGE_SENSE)
    {
        usb_free_mem(purb->data_buffer);
        purb->data_buffer = NULL;
        purb->data_length = 0;
    }

    if (status != STATUS_SUCCESS)
    {
        if (usb_halted(status))
        {
            //Schedule a work-item to do a reset recovery
            if (!umss_schedule_workitem
                ((PVOID) pdev_ext, umss_bulkonly_reset_recovery, pdev_ext->dev_mgr, pdev_ext->dev_handle))
            {
                umss_complete_request(pdev_ext, STATUS_IO_DEVICE_ERROR);
            }
        }
        else
        {
            // Device failed CBW without stalling, so complete with error
            umss_complete_request(pdev_ext, status);
        }
    }
    else
    {
        // CBW was accepted by device, so start data phase of I/O operation
        umss_bulkonly_transfer_data(pdev_ext);
    }

    if (purb)
        usb_free_mem(purb);

    purb = NULL;
    return;
}

//can only be called at passive level
NTSTATUS
umss_sync_submit_urb(PUMSS_DEVICE_EXTENSION pdev_ext, PURB purb)
{
    NTSTATUS status;

    if (pdev_ext == NULL || purb == NULL)
        return STATUS_INVALID_PARAMETER;

    purb->completion = umss_sync_submit_urb_completion;
    purb->context = (PVOID) pdev_ext;

    dev_mgr_register_irp(pdev_ext->dev_mgr, pdev_ext->io_packet.pirp, purb);
    status = usb_submit_urb(pdev_ext->dev_mgr, purb);
    if (status == STATUS_PENDING)
    {
        KeWaitForSingleObject(&pdev_ext->sync_event, Executive, KernelMode, TRUE, NULL);
        status = purb->status;
    }
    else
        dev_mgr_remove_irp(pdev_ext->dev_mgr, pdev_ext->io_packet.pirp);

    return status;
}

VOID
umss_sync_submit_urb_completion(PURB purb, PVOID context)
{
    PUMSS_DEVICE_EXTENSION pdev_ext;

    if (purb == NULL || context == NULL)
        return;

    pdev_ext = (PUMSS_DEVICE_EXTENSION) context;
    dev_mgr_remove_irp(pdev_ext->dev_mgr, pdev_ext->io_packet.pirp);
    KeSetEvent(&pdev_ext->sync_event, 0, FALSE);
    return;
}

/*++
Routine Description:

    Worker function used to execute a reset recovery after a stall.

 Arguments:

    Reference - Our device extension.

Return Value:

    NONE

--*/
VOID
umss_bulkonly_reset_recovery(IN PVOID reference)
{
    PUMSS_DEVICE_EXTENSION pdev_ext;
    URB urb;
    NTSTATUS status;
    DEV_HANDLE endp_handle;

    pdev_ext = (PUMSS_DEVICE_EXTENSION) reference;
    usb_dbg_print(DBGLVL_MAXIMUM, ("umss_bulkonly_reset_recovery(): entering...\n"));
    // Steps for reset recovery:
    // 1. Send device a mass storage reset command on the default endpoint.
    // 2. Reset the bulk-in endpoint.
    // 3. Reset the bulk-out endpoint.
    // 4. Complete the original I/O request with error.


    // Build the mass storage reset command
    UsbBuildVendorRequest(&urb, pdev_ext->dev_handle | 0xffff,  //default pipe
                          NULL, //no extra data
                          0,    //no size
                          0x21, //class, interface
                          BULK_ONLY_MASS_STORAGE_RESET,
                          0,
                          pdev_ext->pif_desc->bInterfaceNumber,
                          NULL, //completion
                          NULL, //context
                          0);   //reference

    // Send mass storage reset command to device
    status = umss_sync_submit_urb(pdev_ext, &urb);

    if (status != STATUS_SUCCESS)
    {
        usb_dbg_print(DBGLVL_MINIMUM, ("umss_bulkonly_reset_recovery(): Reset Recovery failed!\n"));
    }
    else
    {
        //Reset Bulk-in endpoint
        endp_handle = usb_make_handle((pdev_ext->dev_handle >> 16), pdev_ext->if_idx, pdev_ext->in_endp_idx);
        status = umss_reset_pipe(pdev_ext, endp_handle);

        if (!NT_SUCCESS(status))
        {
            usb_dbg_print(DBGLVL_MINIMUM,
                          ("umss_bulkonly_reset_recovery(): Unable to clear Bulk-in endpoint\n"));
        }

        //Reset Bulk-out endpoint
        endp_handle = usb_make_handle((pdev_ext->dev_handle >> 16), pdev_ext->if_idx, pdev_ext->out_endp_idx);
        status = umss_reset_pipe(pdev_ext, endp_handle);

        if (!NT_SUCCESS(status))
        {
            usb_dbg_print(DBGLVL_MINIMUM,
                          ("umss_bulkonly_reset_recovery(): Unable to clear Bulk-out endpoint\n"));
        }
    }
    umss_complete_request(pdev_ext, status);
}

/*++
Routine Description:

    Schedules a bulk data transfer to/from the device.

 Arguments:

    DeviceExtension - Our FDO's device extension.

Return Value:

    NONE

--*/
VOID
umss_bulkonly_transfer_data(PUMSS_DEVICE_EXTENSION pdev_ext)
{
    PVOID data_buf;
    ULONG data_buf_length;
    NTSTATUS status;
    UCHAR trans_dir = USB_DIR_IN; // FIXME: Initialize this properly!

    // Steps for data phase
    // 1. Get data buffer fragment (either SGD list, flat buffer, or none).
    // 2. Schedule data transfer if neccessary.
    // 3. Repeat 1-2 until all data transferred, or endpoint stalls.
    // 4. Move to status phase.

    // Get next data buffer element, if any
    data_buf = umss_get_buffer(pdev_ext, &data_buf_length);

    if (NULL == data_buf)
    {
        //No data to transfer, so move to status phase
        umss_bulkonly_get_status(pdev_ext);
    }
    else
    {
        // Schedule the data transfer.
        // Calls umss_bulkonly_transfer_data_complete when transfer completes.

        if ((pdev_ext->io_packet.flags & IOP_FLAG_STAGE_MASK) == IOP_FLAG_STAGE_NORMAL)
            trans_dir = (UCHAR) ((pdev_ext->cbw.bmCBWFlags & USB_DIR_IN) ? USB_DIR_IN : USB_DIR_OUT);
        else if ((pdev_ext->io_packet.flags & IOP_FLAG_STAGE_MASK) == IOP_FLAG_STAGE_SENSE)
            trans_dir = USB_DIR_IN;

        if ((status = umss_bulk_transfer(pdev_ext,
                                         trans_dir,
                                         data_buf,
                                         data_buf_length,
                                         umss_bulkonly_transfer_data_complete)) != STATUS_PENDING)
        {
            umss_complete_request(pdev_ext, status);
        }
    }
    return;
}

/*++
Routine Description:
    Completion handler for bulk data transfer requests.
--*/
VOID
umss_bulkonly_transfer_data_complete(PURB purb, PVOID reference)
{
    NTSTATUS status;
    PUMSS_DEVICE_EXTENSION pdev_ext;
    pdev_ext = (PUMSS_DEVICE_EXTENSION) reference;

    status = purb->status;

    dev_mgr_remove_irp(pdev_ext->dev_mgr, pdev_ext->io_packet.pirp);

    if (status != STATUS_SUCCESS)
    {
        //
        // clear the data length if this is a scsi pass through request
        //
        umss_clear_pass_through_length(&pdev_ext->io_packet);

        // Device failed data phase
        // Check if we need to clear stalled pipe
        if (usb_halted(status))
        {
            PULONG buf;
            buf = usb_alloc_mem(NonPagedPool, 32);
            buf[0] = (ULONG) pdev_ext;
            buf[1] = (ULONG) purb->endp_handle;

            usb_dbg_print(DBGLVL_MINIMUM, ("umss_transfer_data_complete(): transfer data error!\n"));
            if (!umss_schedule_workitem
                ((PVOID) buf, umss_bulkonly_reset_pipe_and_get_status, pdev_ext->dev_mgr,
                 pdev_ext->dev_handle))
            {

⌨️ 快捷键说明

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