mirror of
https://github.com/reactos/reactos.git
synced 2024-11-05 22:26:39 +00:00
c424146e2c
svn path=/branches/cmake-bringup/; revision=48236
797 lines
23 KiB
C
797 lines
23 KiB
C
/**
|
|
* 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);
|
|
}
|
|
|
|
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);
|
|
if (!buf) return;
|
|
|
|
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))
|
|
{
|
|
usb_free_mem(buf), buf = NULL;
|
|
usb_dbg_print(DBGLVL_MINIMUM,
|
|
("umss_transfer_data_complete(): Failed to allocate work-item to reset pipe!\n"));
|
|
TRAP();
|
|
umss_complete_request(pdev_ext, status);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//finish our request
|
|
umss_complete_request(pdev_ext, status);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Start next part of data phase
|
|
//umss_bulkonly_transfer_data( pdev_ext );
|
|
umss_bulkonly_get_status(pdev_ext);
|
|
//umss_complete_request( pdev_ext, status );
|
|
}
|
|
|
|
usb_free_mem(purb);
|
|
purb = NULL;
|
|
|
|
return; // STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
|
|
|
|
VOID
|
|
umss_bulkonly_reset_pipe_and_get_status(IN PVOID reference)
|
|
{
|
|
PUMSS_DEVICE_EXTENSION pdev_ext;
|
|
DEV_HANDLE endp_handle;
|
|
NTSTATUS status;
|
|
|
|
usb_dbg_print(DBGLVL_MINIMUM, ("umss_bulkonly_reset_pipe_and_get_status(): entering...\n"));
|
|
|
|
pdev_ext = (PUMSS_DEVICE_EXTENSION) (((PULONG) reference)[0]);
|
|
endp_handle = (DEV_HANDLE) ((PULONG) reference)[1];
|
|
usb_free_mem(reference);
|
|
reference = NULL;
|
|
|
|
// Reset the endpoint
|
|
if ((status = umss_reset_pipe(pdev_ext, endp_handle)) != STATUS_SUCCESS)
|
|
{
|
|
usb_dbg_print(DBGLVL_MINIMUM, ("umss_bulkonly_reset_pipe_and_get_status(): reset pipe failed\n"));
|
|
umss_complete_request(pdev_ext, status);
|
|
return;
|
|
}
|
|
// Data phase is finished since the endpoint stalled, so go to status phase
|
|
usb_dbg_print(DBGLVL_MINIMUM,
|
|
("umss_bulkonly_reset_pipe_and_get_status(): reset pipe succeeds, continue to get status\n"));
|
|
umss_bulkonly_get_status(pdev_ext);
|
|
}
|
|
|
|
VOID
|
|
umss_bulkonly_get_status(PUMSS_DEVICE_EXTENSION pdev_ext)
|
|
{
|
|
NTSTATUS status;
|
|
// Schedule bulk transfer to get command status wrapper from device
|
|
status = umss_bulk_transfer(pdev_ext,
|
|
USB_DIR_IN,
|
|
&(pdev_ext->csw),
|
|
sizeof(COMMAND_STATUS_WRAPPER), umss_bulkonly_get_status_complete);
|
|
if (status != STATUS_PENDING)
|
|
{
|
|
umss_complete_request(pdev_ext, status);
|
|
}
|
|
}
|
|
|
|
/*++
|
|
Routine Description:
|
|
|
|
Completion handler for bulk data transfer request.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Previous device object.
|
|
Irp - Irp used for sending command.
|
|
Reference - Our FDO.
|
|
|
|
Return Value:
|
|
|
|
Driver-originated IRPs always return STATUS_MORE_PROCESSING_REQUIRED.
|
|
|
|
--*/
|
|
VOID
|
|
umss_bulkonly_get_status_complete(IN PURB purb, IN PVOID context)
|
|
{
|
|
NTSTATUS status;
|
|
PUMSS_DEVICE_EXTENSION pdev_ext;
|
|
PCOMMAND_STATUS_WRAPPER csw;
|
|
|
|
pdev_ext = (PUMSS_DEVICE_EXTENSION) context;
|
|
status = purb->status;
|
|
|
|
dev_mgr_remove_irp(pdev_ext->dev_mgr, pdev_ext->io_packet.pirp);
|
|
|
|
csw = &(pdev_ext->csw);
|
|
if (status == STATUS_SUCCESS &&
|
|
((csw->dCSWSignature == CSW_SIGNATURE) || OLYMPUS_CSW(pdev_ext, csw->dCSWSignature)))
|
|
{
|
|
if (csw->bCSWStatus == CSW_STATUS_PASSED)
|
|
{
|
|
// Received valid CSW with good status
|
|
|
|
if ((pdev_ext->io_packet.flags & IOP_FLAG_STAGE_MASK) == IOP_FLAG_STAGE_NORMAL &&
|
|
(pdev_ext->io_packet.flags & IOP_FLAG_REQ_SENSE) && pdev_ext->io_packet.sense_data != NULL)
|
|
UMSS_FORGE_GOOD_SENSE(pdev_ext->io_packet.sense_data)
|
|
umss_complete_request(pdev_ext, STATUS_SUCCESS);
|
|
}
|
|
else if (csw->bCSWStatus == CSW_STATUS_FAILED)
|
|
{
|
|
// start a request sense if necessary
|
|
if ((pdev_ext->io_packet.flags & IOP_FLAG_REQ_SENSE) &&
|
|
(pdev_ext->io_packet.flags & IOP_FLAG_STAGE_MASK) == IOP_FLAG_STAGE_NORMAL)
|
|
{
|
|
if (umss_bulkonly_send_sense_req(pdev_ext) != STATUS_PENDING)
|
|
{
|
|
// don't know how to handle.
|
|
umss_complete_request(pdev_ext, STATUS_IO_DEVICE_ERROR);
|
|
}
|
|
else
|
|
{
|
|
// fall through to free the urb
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// error occurred, reset device
|
|
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
|
|
{
|
|
// error occurred, reset device
|
|
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 if ((status != STATUS_SUCCESS) && (usb_halted(status)) && (pdev_ext->retry))
|
|
{
|
|
// Device stalled CSW transfer, retry once before failing
|
|
PULONG buf;
|
|
pdev_ext->retry = FALSE;
|
|
|
|
buf = usb_alloc_mem(NonPagedPool, 32);
|
|
if (!buf) return;
|
|
|
|
buf[0] = (ULONG) pdev_ext;
|
|
buf[1] = (ULONG) purb->endp_handle;
|
|
|
|
if (!umss_schedule_workitem
|
|
((PVOID) buf, umss_bulkonly_reset_pipe_and_get_status, pdev_ext->dev_mgr, pdev_ext->dev_handle))
|
|
{
|
|
usb_free_mem(buf), buf = NULL;
|
|
usb_dbg_print(DBGLVL_MINIMUM,
|
|
("umss_bulkonly_get_status_complete(): Failed to allocate work-item to reset pipe!\n"));
|
|
TRAP();
|
|
umss_complete_request(pdev_ext, status);
|
|
}
|
|
}
|
|
else if (status != STATUS_CANCELLED)
|
|
{
|
|
// An error has occured. Reset the device.
|
|
if (!umss_schedule_workitem
|
|
((PVOID) pdev_ext, umss_bulkonly_reset_recovery, pdev_ext->dev_mgr, pdev_ext->dev_handle))
|
|
{
|
|
usb_dbg_print(DBGLVL_MINIMUM,
|
|
("umss_bulkonly_get_status_complete(): Failed to schedule work-item to reset pipe!\n"));
|
|
TRAP();
|
|
umss_complete_request(pdev_ext, status);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// the request is canceled
|
|
usb_dbg_print(DBGLVL_MINIMUM, ("umss_bulkonly_get_status_complete(): the request is canceled\n"));
|
|
umss_complete_request(pdev_ext, STATUS_CANCELLED);
|
|
}
|
|
|
|
usb_free_mem(purb);
|
|
purb = NULL;
|
|
|
|
return;
|
|
}
|
|
|
|
/*++
|
|
Routine Description:
|
|
|
|
Queries Bulk-Only device for maximum LUN number
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - Our device extension.
|
|
|
|
Return Value:
|
|
|
|
Maximum LUN number for device, or 0 if error occurred.
|
|
|
|
--*/
|
|
CHAR
|
|
umss_bulkonly_get_maxlun(IN PUMSS_DEVICE_EXTENSION pdev_ext)
|
|
{
|
|
PURB purb = NULL;
|
|
UCHAR max_lun;
|
|
NTSTATUS status;
|
|
|
|
purb = usb_alloc_mem(NonPagedPool, sizeof(URB));
|
|
|
|
if (!purb)
|
|
{
|
|
usb_dbg_print(DBGLVL_MINIMUM,
|
|
("umss_bulkonly_get_maxlun(): Failed to allocate URB, setting max LUN to 0\n"));
|
|
max_lun = 0;
|
|
}
|
|
else
|
|
{
|
|
// Build the get max lun command
|
|
UsbBuildVendorRequest(purb, (pdev_ext->dev_handle | 0xffff), &max_lun, sizeof(max_lun), 0xb1, //class, interface, in
|
|
BULK_ONLY_GET_MAX_LUN, 0, pdev_ext->pif_desc->bInterfaceNumber, NULL, NULL, 0);
|
|
|
|
// Send get max lun command to device
|
|
status = umss_sync_submit_urb(pdev_ext, purb);
|
|
|
|
if (status != STATUS_PENDING)
|
|
{
|
|
usb_dbg_print(DBGLVL_MINIMUM,
|
|
("umss_bulkonly_get_maxlun(): Get Max LUN command failed, setting max LUN to 0!\n"));
|
|
max_lun = 0;
|
|
}
|
|
}
|
|
|
|
if (purb)
|
|
usb_free_mem(purb);
|
|
|
|
usb_dbg_print(DBGLVL_MINIMUM, ("umss_bulkonly_get_maxlun(): Max LUN = %x\n", max_lun));
|
|
|
|
return max_lun;
|
|
}
|
|
|
|
PVOID
|
|
umss_get_buffer(PUMSS_DEVICE_EXTENSION pdev_ext, ULONG * buf_length)
|
|
{
|
|
PVOID buffer;
|
|
|
|
if ((pdev_ext->io_packet.flags & IOP_FLAG_STAGE_MASK) == IOP_FLAG_STAGE_NORMAL)
|
|
{
|
|
buffer = (PVOID) pdev_ext->io_packet.data_buffer;
|
|
*buf_length = pdev_ext->io_packet.data_length;
|
|
}
|
|
else if ((pdev_ext->io_packet.flags & IOP_FLAG_STAGE_MASK) == IOP_FLAG_STAGE_SENSE)
|
|
{
|
|
buffer = (PVOID) pdev_ext->io_packet.sense_data;
|
|
*buf_length = pdev_ext->io_packet.sense_data_length;
|
|
}
|
|
else
|
|
{
|
|
buffer = NULL;
|
|
*buf_length = 0;
|
|
}
|
|
|
|
return buffer;
|
|
}
|
|
|
|
BOOLEAN
|
|
umss_bulkonly_build_sense_cdb(PUMSS_DEVICE_EXTENSION pdev_ext, PCOMMAND_BLOCK_WRAPPER cbw)
|
|
{
|
|
UCHAR sub_class;
|
|
PUCHAR cdb;
|
|
|
|
if (pdev_ext == NULL || cbw == NULL)
|
|
return FALSE;
|
|
|
|
cdb = cbw->CBWCB;
|
|
RtlZeroMemory(cdb, MAX_CDB_LENGTH);
|
|
sub_class = pdev_ext->pif_desc->bInterfaceSubClass;
|
|
|
|
cdb[0] = SFF_REQUEST_SENSEE;
|
|
cdb[1] = pdev_ext->io_packet.lun << 5;
|
|
cdb[4] = 18;
|
|
|
|
switch (sub_class)
|
|
{
|
|
case UMSS_SUBCLASS_SFF8070I:
|
|
case UMSS_SUBCLASS_UFI:
|
|
{
|
|
cbw->bCBWLength = 12;
|
|
break;
|
|
}
|
|
case UMSS_SUBCLASS_RBC:
|
|
case UMSS_SUBCLASS_SCSI_TCS:
|
|
{
|
|
cbw->bCBWLength = 6;
|
|
break;
|
|
}
|
|
default:
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
NTSTATUS
|
|
umss_bulkonly_send_sense_req(PUMSS_DEVICE_EXTENSION pdev_ext)
|
|
{
|
|
PCOMMAND_BLOCK_WRAPPER cbw;
|
|
NTSTATUS status;
|
|
|
|
if (pdev_ext == NULL || pdev_ext->io_packet.sense_data == NULL
|
|
|| pdev_ext->io_packet.sense_data_length < 18)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
pdev_ext->retry = TRUE;
|
|
|
|
cbw = usb_alloc_mem(NonPagedPool, sizeof(COMMAND_BLOCK_WRAPPER));
|
|
if (!cbw) return STATUS_NO_MEMORY;
|
|
|
|
RtlZeroMemory(cbw, sizeof(COMMAND_BLOCK_WRAPPER));
|
|
pdev_ext->io_packet.flags &= ~IOP_FLAG_STAGE_MASK;
|
|
pdev_ext->io_packet.flags |= IOP_FLAG_STAGE_SENSE;
|
|
|
|
cbw->dCBWSignature = CBW_SIGNATURE;
|
|
cbw->dCBWTag = 0;
|
|
cbw->dCBWDataTransferLength = pdev_ext->io_packet.sense_data_length;
|
|
cbw->bmCBWFlags = USB_DIR_IN;
|
|
cbw->bCBWLun = 0;
|
|
|
|
if (umss_bulkonly_build_sense_cdb(pdev_ext, cbw) == FALSE)
|
|
{
|
|
usb_free_mem(cbw);
|
|
cbw = NULL;
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
status = umss_bulk_transfer(pdev_ext,
|
|
USB_DIR_OUT,
|
|
cbw, sizeof(COMMAND_BLOCK_WRAPPER), umss_bulkonly_send_cbw_completion);
|
|
|
|
if (status != STATUS_PENDING)
|
|
{
|
|
usb_free_mem(cbw);
|
|
cbw = NULL;
|
|
}
|
|
return status;
|
|
}
|
|
|
|
BOOLEAN
|
|
umss_clear_pass_through_length(PIO_PACKET io_packet)
|
|
{
|
|
//
|
|
// clear the respective data length to meet request of scsi pass through requirement.
|
|
//
|
|
|
|
BOOLEAN sense_stage;
|
|
ULONG ctrl_code;
|
|
PIO_STACK_LOCATION cur_stack;
|
|
PSCSI_PASS_THROUGH pass_through;
|
|
PSCSI_PASS_THROUGH_DIRECT pass_through_direct;
|
|
|
|
if (io_packet == NULL)
|
|
return FALSE;
|
|
|
|
if ((io_packet->flags & IOP_FLAG_SCSI_CTRL_TRANSFER) == 0)
|
|
return FALSE;
|
|
|
|
sense_stage = FALSE;
|
|
if (io_packet->flags & IOP_FLAG_STAGE_SENSE)
|
|
sense_stage = TRUE;
|
|
|
|
cur_stack = IoGetCurrentIrpStackLocation(io_packet->pirp);
|
|
ctrl_code = cur_stack->Parameters.DeviceIoControl.IoControlCode;
|
|
if (ctrl_code == IOCTL_SCSI_PASS_THROUGH_DIRECT)
|
|
{
|
|
pass_through_direct = io_packet->pirp->AssociatedIrp.SystemBuffer;
|
|
if (sense_stage)
|
|
pass_through_direct->SenseInfoLength = 0;
|
|
else
|
|
pass_through_direct->DataTransferLength = 0;
|
|
}
|
|
else if (ctrl_code == IOCTL_SCSI_PASS_THROUGH)
|
|
{
|
|
pass_through = io_packet->pirp->AssociatedIrp.SystemBuffer;
|
|
if (sense_stage)
|
|
pass_through->SenseInfoLength = 0;
|
|
else
|
|
pass_through->DataTransferLength = 0;
|
|
}
|
|
else
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|