mirror of
https://github.com/reactos/reactos.git
synced 2025-01-11 16:51:06 +00:00
c424146e2c
svn path=/branches/cmake-bringup/; revision=48236
302 lines
9.2 KiB
C
302 lines
9.2 KiB
C
/**
|
|
* cbi.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"
|
|
|
|
VOID umss_cbi_send_adsc_complete(PURB purb, PVOID context);
|
|
VOID umss_cbi_transfer_data(PUMSS_DEVICE_EXTENSION pdev_ext);
|
|
VOID umss_cbi_get_status(PUMSS_DEVICE_EXTENSION pdev_ext);
|
|
VOID umss_cbi_transfer_data_complete(PURB purb, PVOID context);
|
|
VOID umss_cbi_get_status_complete(PURB purb, PVOID context);
|
|
|
|
NTSTATUS
|
|
umss_class_specific_request(IN PUMSS_DEVICE_EXTENSION pdev_ext,
|
|
IN UCHAR request,
|
|
IN UCHAR dir,
|
|
IN PVOID buffer,
|
|
IN ULONG buffer_length,
|
|
IN PURBCOMPLETION completion)
|
|
{
|
|
PURB purb;
|
|
NTSTATUS status;
|
|
|
|
UNREFERENCED_PARAMETER(dir);
|
|
|
|
purb = usb_alloc_mem(NonPagedPool, sizeof(URB));
|
|
if (!purb) return STATUS_NO_MEMORY;
|
|
|
|
// Build URB for the ADSC command
|
|
UsbBuildVendorRequest(purb,
|
|
pdev_ext->dev_handle | 0xffff,
|
|
buffer,
|
|
buffer_length,
|
|
0x21, request, 0, pdev_ext->pif_desc->bInterfaceNumber, completion, pdev_ext, 0);
|
|
|
|
status = usb_submit_urb(pdev_ext->dev_mgr, purb);
|
|
if (status != STATUS_PENDING)
|
|
{
|
|
usb_free_mem(purb);
|
|
purb = NULL;
|
|
return status;
|
|
}
|
|
dev_mgr_register_irp(pdev_ext->dev_mgr, pdev_ext->io_packet.pirp, purb);
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
umss_cbi_startio(IN PUMSS_DEVICE_EXTENSION pdev_ext, IN PIO_PACKET io_packet)
|
|
{
|
|
NTSTATUS status;
|
|
|
|
status = STATUS_NOT_SUPPORTED;
|
|
return status;
|
|
|
|
RtlCopyMemory(&pdev_ext->io_packet, io_packet, sizeof(pdev_ext->io_packet));
|
|
|
|
// Send the ADSC request to the device
|
|
// Calls UMSS_CbiSendADSCComplete when transfer completes
|
|
status = umss_class_specific_request(pdev_ext,
|
|
ACCEPT_DEVICE_SPECIFIC_COMMAND,
|
|
USB_DIR_OUT,
|
|
io_packet->cdb, io_packet->cdb_length, umss_cbi_send_adsc_complete);
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
umss_cbi_send_adsc_complete(PURB purb, PVOID context)
|
|
{
|
|
NTSTATUS status;
|
|
PUMSS_DEVICE_EXTENSION pdev_ext;
|
|
PIO_PACKET io_packet;
|
|
|
|
pdev_ext = (PUMSS_DEVICE_EXTENSION) context;
|
|
io_packet = &pdev_ext->io_packet;
|
|
|
|
status = purb->status;
|
|
|
|
dev_mgr_remove_irp(pdev_ext->dev_mgr, pdev_ext->io_packet.pirp);
|
|
|
|
if (!usb_success(status))
|
|
{
|
|
usb_dbg_print(DBGLVL_MINIMUM, ("umss_cbi_send_adsc_complete(): Command Block Failure!!!\n"));
|
|
|
|
// BUGBUG - Should reset device here?
|
|
// Device failed Command Block, complete with error
|
|
umss_complete_request(pdev_ext, STATUS_IO_DEVICE_ERROR);
|
|
|
|
}
|
|
else if (io_packet->data_length)
|
|
{
|
|
|
|
usb_dbg_print(DBGLVL_HIGH, ("umss_cbi_send_adsc_complete(): Queuing Data Transfer DPC\n"));
|
|
umss_cbi_transfer_data(pdev_ext);
|
|
|
|
}
|
|
else if (pdev_ext->pif_desc->bInterfaceProtocol == PROTOCOL_CBI)
|
|
{
|
|
// Device supports interrupt pipe, so get status
|
|
umss_cbi_get_status(pdev_ext);
|
|
}
|
|
else
|
|
{
|
|
// Device does not report status, so complete request
|
|
umss_complete_request(pdev_ext, STATUS_SUCCESS);
|
|
}
|
|
|
|
usb_free_mem(purb);
|
|
purb = NULL;
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
umss_cbi_reset_pipe(IN PVOID reference)
|
|
{
|
|
PUMSS_DEVICE_EXTENSION pdev_ext;
|
|
pdev_ext = (PUMSS_DEVICE_EXTENSION) reference;
|
|
|
|
// Reset the appropriate pipe, based on data direction
|
|
umss_reset_pipe(pdev_ext,
|
|
(pdev_ext->io_packet.flags & USB_DIR_IN) ?
|
|
usb_make_handle((pdev_ext->dev_handle >> 16), pdev_ext->if_idx, pdev_ext->in_endp_idx) :
|
|
usb_make_handle((pdev_ext->dev_handle >> 16), pdev_ext->if_idx, pdev_ext->out_endp_idx));
|
|
|
|
// Device stalled endpoint, so complete I/O operation with error.
|
|
// BUGBUG is this correct? Check spec...
|
|
umss_complete_request(pdev_ext, USB_STATUS_STALL_PID);
|
|
}
|
|
|
|
VOID
|
|
umss_cbi_transfer_data(PUMSS_DEVICE_EXTENSION pdev_ext)
|
|
{
|
|
PVOID buffer = NULL;
|
|
ULONG buffer_length;
|
|
|
|
// Get next data buffer element, if any.
|
|
buffer = umss_get_buffer(pdev_ext, &buffer_length);
|
|
if (NULL == buffer)
|
|
{
|
|
//Done with data phase, so move to status phase if (supported)
|
|
|
|
if (pdev_ext->pif_desc->bInterfaceProtocol == PROTOCOL_CBI)
|
|
{
|
|
// Device supports interrupt pipe, so get status
|
|
umss_cbi_get_status(pdev_ext);
|
|
}
|
|
else
|
|
{
|
|
// No interrupt pipe, so just complete the request
|
|
umss_complete_request(pdev_ext, STATUS_SUCCESS);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Transfer next element of the data phase
|
|
umss_bulk_transfer(pdev_ext,
|
|
(UCHAR) ((pdev_ext->io_packet.flags & USB_DIR_IN) ? USB_DIR_IN : USB_DIR_OUT),
|
|
buffer, buffer_length, umss_cbi_transfer_data_complete);
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
umss_cbi_transfer_data_complete(PURB purb, PVOID context)
|
|
{
|
|
NTSTATUS status;
|
|
PUMSS_DEVICE_EXTENSION pdev_ext;
|
|
|
|
pdev_ext = (PUMSS_DEVICE_EXTENSION) context;
|
|
status = purb->status;
|
|
|
|
usb_free_mem(purb);
|
|
purb = NULL;
|
|
|
|
if (!usb_success(status))
|
|
{
|
|
// Device failed Data Transfer
|
|
// Check if we need to clear stalled pipe
|
|
if (usb_halted(status))
|
|
{
|
|
// Reset pipe can only be done at passive level, so we need
|
|
// to schedule a work item to do it.
|
|
if (!umss_schedule_workitem
|
|
((PVOID) pdev_ext, umss_cbi_reset_pipe, pdev_ext->dev_mgr, pdev_ext->dev_handle))
|
|
{
|
|
usb_dbg_print(DBGLVL_MINIMUM,
|
|
("umss_cbi_transfer_data_complete(): Failed to allocate work-item to reset pipe!\n"));
|
|
TRAP();
|
|
umss_complete_request(pdev_ext, STATUS_IO_DEVICE_ERROR);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
umss_complete_request(pdev_ext, STATUS_IO_DEVICE_ERROR);
|
|
}
|
|
return;
|
|
}
|
|
// Transfer succeeded
|
|
// umss_cbi_transfer_data( pdev_ext );
|
|
umss_complete_request(pdev_ext, STATUS_SUCCESS);
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
umss_cbi_get_status(PUMSS_DEVICE_EXTENSION pdev_ext)
|
|
{
|
|
PURB purb;
|
|
NTSTATUS status;
|
|
|
|
purb = usb_alloc_mem(NonPagedPool, sizeof(URB));
|
|
if (purb == NULL)
|
|
return;
|
|
|
|
// Build a URB for our interrupt transfer
|
|
UsbBuildInterruptOrBulkTransferRequest(purb,
|
|
usb_make_handle((pdev_ext->dev_handle >> 16), pdev_ext->if_idx,
|
|
pdev_ext->int_endp_idx), (PUCHAR) & pdev_ext->idb,
|
|
sizeof(INTERRUPT_DATA_BLOCK), umss_cbi_get_status_complete,
|
|
pdev_ext, 0);
|
|
|
|
// Call USB driver stack
|
|
status = usb_submit_urb(pdev_ext->dev_mgr, purb);
|
|
if (status != STATUS_PENDING)
|
|
{
|
|
usb_free_mem(purb);
|
|
purb = NULL;
|
|
return;
|
|
}
|
|
dev_mgr_register_irp(pdev_ext->dev_mgr, pdev_ext->io_packet.pirp, purb);
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
umss_cbi_get_status_complete(PURB purb, PVOID context)
|
|
{
|
|
NTSTATUS status;
|
|
PUMSS_DEVICE_EXTENSION pdev_ext;
|
|
PINTERRUPT_DATA_BLOCK idb;
|
|
|
|
pdev_ext = (PUMSS_DEVICE_EXTENSION) context;
|
|
|
|
status = purb->status;
|
|
dev_mgr_remove_irp(pdev_ext->dev_mgr, pdev_ext->io_packet.pirp);
|
|
|
|
usb_free_mem(purb);
|
|
purb = NULL;
|
|
|
|
if (!usb_success(status))
|
|
{
|
|
// Device failed Data Transfer
|
|
// Check if we need to clear stalled pipe
|
|
if (usb_halted(status))
|
|
{
|
|
if (!umss_schedule_workitem
|
|
((PVOID) pdev_ext, umss_cbi_reset_pipe, pdev_ext->dev_mgr, pdev_ext->dev_handle))
|
|
{
|
|
usb_dbg_print(DBGLVL_MINIMUM,
|
|
("umss_cbi_get_status_complete(): Failed to allocate work-item to reset pipe!\n"));
|
|
TRAP();
|
|
umss_complete_request(pdev_ext, STATUS_IO_DEVICE_ERROR);
|
|
return;
|
|
}
|
|
}
|
|
umss_complete_request(pdev_ext, STATUS_IO_DEVICE_ERROR);
|
|
return;
|
|
}
|
|
|
|
// Interrupt transfer succeeded
|
|
idb = &(pdev_ext->idb);
|
|
|
|
// Check for an error in the status block
|
|
if ((0 != idb->bType) || (0 != (idb->bValue & 0x3)))
|
|
{
|
|
umss_complete_request(pdev_ext, STATUS_IO_DEVICE_ERROR);
|
|
}
|
|
else
|
|
{
|
|
umss_complete_request(pdev_ext, STATUS_SUCCESS);
|
|
}
|
|
}
|