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 + -
显示快捷键?