reactos/drivers/usb/nt4compat/usbdriver/dmgrdisp.c
Amine Khaldi c424146e2c Create a branch for cmake bringup.
svn path=/branches/cmake-bringup/; revision=48236
2010-07-24 18:52:44 +00:00

536 lines
21 KiB
C

/**
* dmgrdisp.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
disp_urb_completion(PURB purb, PVOID context)
{
PUSB_DEV_MANAGER dev_mgr;
ULONG ctrl_code;
NTSTATUS status;
PDEVEXT_HEADER dev_hdr;
UNREFERENCED_PARAMETER(context);
if (purb == NULL)
return;
ctrl_code = (ULONG) purb->reference;
dev_mgr = (PUSB_DEV_MANAGER) purb->context;
// at this stage, the irp can not be canceled since the urb
// won't be found in any queue and the irp is not in any queue.
// see line 4685 in hub.c
// Sometimes, it may be very fast to enter this routine before
// the dev_mgr_register_irp to be called in dispatch routine in
// usb2.0 environment as
// we did in usb1.1 driver. We can not simply add a loop to wait
// for the dispatch thread to add the irp to the list, because
// here we are at DPC level higher than the dispatch thread
// running level. And the solution is to register the irp
// before the urb is scheduled instead of registering it after
// urb is scheduled.
if (purb->pirp)
{
PIO_STACK_LOCATION irp_stack;
dev_mgr_remove_irp(dev_mgr, purb->pirp);
status = purb->status;
irp_stack = IoGetCurrentIrpStackLocation(purb->pirp);
if (purb->status != STATUS_SUCCESS)
{
purb->pirp->IoStatus.Information = 0;
}
else
{
// currently only IRP_MJ_DEVICE_CONTROL and IRP_MJ_INTERNAL_DEVICE_CONTROL
// are allowed. And we do not need to set information
// for IRP_MJ_INTERNAL_DEVICE_CONTROL
if (irp_stack->MajorFunction == IRP_MJ_DEVICE_CONTROL)
purb->pirp->IoStatus.Information = purb->data_length;
}
purb->pirp->IoStatus.Status = status;
if (irp_stack)
{
dev_hdr = irp_stack->DeviceObject->DeviceExtension;
if (dev_hdr->start_io)
{
IoStartNextPacket(irp_stack->DeviceObject, TRUE);
}
}
IoCompleteRequest(purb->pirp, IO_NO_INCREMENT);
}
return;
}
VOID
disp_noio_urb_completion(PURB purb, PVOID context)
{
PUSB_CTRL_SETUP_PACKET psetup;
PURB purb2;
PUSB_DEV_MANAGER dev_mgr;
NTSTATUS status = STATUS_SUCCESS;
PIO_STACK_LOCATION irp_stack;
PDEVEXT_HEADER dev_hdr;
if (purb == NULL)
return;
psetup = (PUSB_CTRL_SETUP_PACKET) purb->setup_packet;
if ((psetup->bmRequestType == 0x2) &&
(psetup->bRequest == USB_REQ_CLEAR_FEATURE) &&
(psetup->wIndex == 0)) //reset pipe
{
purb2 = (PURB) context;
}
else
{
purb2 = purb;
}
if (purb2->pirp == NULL)
return;
dev_mgr = (PUSB_DEV_MANAGER) purb2->context;
dev_mgr_remove_irp(dev_mgr, purb2->pirp);
if (purb->status != STATUS_SUCCESS)
status = STATUS_IO_DEVICE_ERROR;
purb2->pirp->IoStatus.Information = 0;
purb2->pirp->IoStatus.Status = status;
irp_stack = IoGetCurrentIrpStackLocation(purb->pirp);
if (irp_stack)
{
dev_hdr = irp_stack->DeviceObject->DeviceExtension;
if (dev_hdr->start_io)
{
IoStartNextPacket(irp_stack->DeviceObject, TRUE);
}
}
IoCompleteRequest(purb2->pirp, IO_NO_INCREMENT);
return;
}
//this function is called by the hcd's
//dispatch when they have done their job.
NTSTATUS
dev_mgr_dispatch(IN PUSB_DEV_MANAGER dev_mgr, IN PIRP irp)
{
PIO_STACK_LOCATION irp_stack;
NTSTATUS status;
ULONG ctrl_code;
USE_NON_PENDING_IRQL;
ASSERT(irp);
if (dev_mgr == NULL)
{
EXIT_DISPATCH(STATUS_INVALID_PARAMETER, irp);
}
status = STATUS_SUCCESS;
irp_stack = IoGetCurrentIrpStackLocation(irp);
ctrl_code = irp_stack->Parameters.DeviceIoControl.IoControlCode;
switch (irp_stack->MajorFunction)
{
case IRP_MJ_CREATE:
{
InterlockedIncrement(&dev_mgr->open_count);
EXIT_DISPATCH(STATUS_SUCCESS, irp);
}
case IRP_MJ_CLOSE:
{
InterlockedDecrement(&dev_mgr->open_count);
EXIT_DISPATCH(STATUS_SUCCESS, irp);
}
case IRP_MJ_INTERNAL_DEVICE_CONTROL:
case IRP_MJ_DEVICE_CONTROL:
{
switch (ctrl_code)
{
case IOCTL_GET_DEV_COUNT:
{
LONG dev_count;
irp->IoStatus.Information = 0;
if (irp_stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(LONG))
{
EXIT_DISPATCH(STATUS_INVALID_PARAMETER, irp);
}
KeAcquireSpinLock(&dev_mgr->dev_list_lock, &old_irql);
dev_count = usb_count_list(&dev_mgr->dev_list);
KeReleaseSpinLock(&dev_mgr->dev_list_lock, old_irql);
*((PLONG) irp->AssociatedIrp.SystemBuffer) = dev_count;
irp->IoStatus.Information = sizeof(LONG);
EXIT_DISPATCH(STATUS_SUCCESS, irp);
}
case IOCTL_ENUM_DEVICES:
{
PLIST_ENTRY pthis, pnext;
LONG dev_count, array_size, i, j = 0;
PUSB_DEV pdev;
PENUM_DEV_ARRAY peda;
irp->IoStatus.Information = 0;
if (irp_stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(LONG))
{
EXIT_DISPATCH(STATUS_INVALID_PARAMETER, irp);
}
if (irp_stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(ENUM_DEV_ARRAY))
{
EXIT_DISPATCH(STATUS_INVALID_PARAMETER, irp);
}
array_size = *((PULONG) irp->AssociatedIrp.SystemBuffer);
KeAcquireSpinLock(&dev_mgr->dev_list_lock, &old_irql);
dev_count = usb_count_list(&dev_mgr->dev_list);
dev_count = dev_count > array_size ? array_size : dev_count;
peda = (PENUM_DEV_ARRAY) irp->AssociatedIrp.SystemBuffer;
RtlZeroMemory(peda, sizeof(ENUM_DEV_ARRAY) + (dev_count - 1) * sizeof(ENUM_DEV_ELEMENT));
if (dev_count)
{
ListFirst(&dev_mgr->dev_list, pthis);
for(i = 0, j = 0; i < dev_count; i++)
{
pdev = struct_ptr(pthis, USB_DEV, dev_link);
ListNext(&dev_mgr->dev_list, pthis, pnext);
pthis = pnext;
lock_dev(pdev, FALSE);
if (dev_state(pdev) == USB_DEV_STATE_ZOMB)
{
unlock_dev(pdev, FALSE);
continue;
}
if (dev_state(pdev) < USB_DEV_STATE_ADDRESSED)
{
unlock_dev(pdev, FALSE);
continue;
}
peda->dev_arr[i].dev_handle = (pdev->dev_id << 16);
//may not get the desc yet
if (pdev->pusb_dev_desc)
{
peda->dev_arr[i].product_id = pdev->pusb_dev_desc->idProduct;
peda->dev_arr[i].vendor_id = pdev->pusb_dev_desc->idVendor;
}
else
{
peda->dev_arr[i].product_id = 0xffff;
peda->dev_arr[i].vendor_id = 0xffff;
}
peda->dev_arr[i].dev_addr = pdev->dev_addr;
unlock_dev(pdev, FALSE);
j++;
}
}
peda->dev_count = dev_count ? j : 0;
KeReleaseSpinLock(&dev_mgr->dev_list_lock, old_irql);
irp->IoStatus.Information =
sizeof(ENUM_DEV_ARRAY) + (dev_count - 1) * sizeof(ENUM_DEV_ELEMENT);
EXIT_DISPATCH(STATUS_SUCCESS, irp);
}
case IOCTL_GET_DEV_DESC:
{
GET_DEV_DESC_REQ gddr;
PUSB_DESC_HEADER pusb_desc_header;
PUSB_DEV pdev;
LONG buf_size;
if (irp_stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(GET_DEV_DESC_REQ))
{
EXIT_DISPATCH(STATUS_INVALID_PARAMETER, irp);
}
if (irp_stack->Parameters.DeviceIoControl.OutputBufferLength < 8)
{
EXIT_DISPATCH(STATUS_INVALID_PARAMETER, irp);
}
status = STATUS_SUCCESS;
buf_size = irp_stack->Parameters.DeviceIoControl.OutputBufferLength;
RtlCopyMemory(&gddr, irp->AssociatedIrp.SystemBuffer, sizeof(GET_DEV_DESC_REQ));
pusb_desc_header = irp->AssociatedIrp.SystemBuffer;
if (gddr.desc_type != USB_DT_CONFIG && gddr.desc_type != USB_DT_DEVICE)
{
EXIT_DISPATCH(STATUS_INVALID_DEVICE_REQUEST, irp);
}
if (usb_query_and_lock_dev(dev_mgr, gddr.dev_handle, &pdev) != STATUS_SUCCESS)
{
EXIT_DISPATCH(STATUS_IO_DEVICE_ERROR, irp);
}
lock_dev(pdev, FALSE);
if (dev_state(pdev) == USB_DEV_STATE_ZOMB)
{
status = STATUS_INVALID_DEVICE_STATE;
goto ERROR_OUT;
}
if (dev_state(pdev) != USB_DEV_STATE_ADDRESSED &&
dev_state(pdev) != USB_DEV_STATE_CONFIGURED)
{
status = STATUS_DEVICE_NOT_READY;
goto ERROR_OUT;
}
if (pdev->pusb_dev_desc == NULL)
{
status = STATUS_DEVICE_NOT_READY;
goto ERROR_OUT;
}
if (gddr.desc_type == USB_DT_DEVICE)
{
RtlCopyMemory(pusb_desc_header,
pdev->pusb_dev_desc,
buf_size > sizeof(USB_DEVICE_DESC)
? sizeof(USB_DEVICE_DESC) : buf_size);
irp->IoStatus.Information =
buf_size >= sizeof(USB_DEVICE_DESC) ? sizeof(USB_DEVICE_DESC) : buf_size;
}
else if (gddr.desc_type == USB_DT_CONFIG)
{
PUSB_CONFIGURATION_DESC pusb_config_desc;
if (pdev->pusb_dev_desc->bNumConfigurations <= gddr.desc_idx)
{
status = STATUS_INVALID_PARAMETER;
goto ERROR_OUT;
}
pusb_config_desc = usb_find_config_desc_by_idx((PUCHAR) & pdev->pusb_dev_desc[1],
gddr.desc_idx,
pdev->pusb_dev_desc->
bNumConfigurations);
if (pusb_config_desc == NULL)
{
status = STATUS_DEVICE_NOT_READY;
goto ERROR_OUT;
}
RtlCopyMemory(pusb_desc_header,
pusb_config_desc,
buf_size >= pusb_config_desc->wTotalLength
? pusb_config_desc->wTotalLength : buf_size);
irp->IoStatus.Information =
buf_size >= pusb_config_desc->wTotalLength
? pusb_config_desc->wTotalLength : buf_size;
}
ERROR_OUT:
unlock_dev(pdev, FALSE);
usb_unlock_dev(pdev);
EXIT_DISPATCH(status, irp);
}
case IOCTL_SUBMIT_URB_RD:
case IOCTL_SUBMIT_URB_WR:
case IOCTL_SUBMIT_URB_NOIO:
{
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;
if (irp_stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(URB))
{
EXIT_DISPATCH(STATUS_INVALID_PARAMETER, irp);
}
purb = (PURB) irp->AssociatedIrp.SystemBuffer;
endp_handle = purb->endp_handle;
if (ctrl_code == IOCTL_SUBMIT_URB_RD || ctrl_code == IOCTL_SUBMIT_URB_WR)
{
if (irp_stack->MajorFunction == IRP_MJ_DEVICE_CONTROL)
{
user_buffer_length = irp_stack->Parameters.DeviceIoControl.OutputBufferLength;
if (user_buffer_length == 0)
EXIT_DISPATCH(STATUS_INVALID_PARAMETER, irp);
user_buffer = MmGetSystemAddressForMdl(irp->MdlAddress);
}
else
{
if (purb->data_buffer == NULL || purb->data_length == 0)
EXIT_DISPATCH(STATUS_INVALID_PARAMETER, irp);
user_buffer_length = purb->data_length;
user_buffer = purb->data_buffer;
}
}
if (usb_query_and_lock_dev(dev_mgr, endp_handle & ~0xffff, &pdev) != STATUS_SUCCESS)
{
EXIT_DISPATCH(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 > 0x100000)
{
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)
{
if (ctrl_code == IOCTL_SUBMIT_URB_RD)
KeFlushIoBuffers(irp->MdlAddress, TRUE, TRUE);
else
KeFlushIoBuffers(irp->MdlAddress, FALSE, TRUE);
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 mark irp before the urb is scheduled to
// avoid race condition
IoMarkIrpPending(irp);
ASSERT(dev_mgr_register_irp(dev_mgr, irp, purb));
status = usb_submit_urb(dev_mgr, purb);
if (status != STATUS_PENDING)
{
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;
IoCompleteRequest(irp, IO_NO_INCREMENT);
}
return status;
ERROR_OUT1:
unlock_dev(pdev, FALSE);
usb_unlock_dev(pdev);
irp->IoStatus.Information = 0;
EXIT_DISPATCH(status, irp);
}
default:
{
irp->IoStatus.Information = 0;
EXIT_DISPATCH(STATUS_NOT_IMPLEMENTED, irp);
}
}
}
default:
{
irp->IoStatus.Information = 0;
break;
}
}
EXIT_DISPATCH(STATUS_INVALID_DEVICE_REQUEST, irp);
}
/*#define IOCTL_GET_DEV_COUNT CTL_CODE( FILE_HCD_DEV_TYPE, 4093, METHOD_BUFFERED, FILE_ANY_ACCESS )
//input_buffer and input_buffer_length is zero, output_buffer is to receive a dword value of the
//dev count, output_buffer_length must be no less than sizeof( long ).
#define IOCTL_ENUM_DEVICES CTL_CODE( FILE_HCD_DEV_TYPE, 4094, METHOD_BUFFERED, FILE_ANY_ACCESS )
//input_buffer is a dword value to indicate the count of elements in the array
//input_buffer_length is sizeof( long ), output_buffer is to receive a
//structure ENUM_DEV_ARRAY where dev_count is the elements hold in this array.
#define IOCTL_GET_DEV_DESC CTL_CODE( FILE_HCD_DEV_TYPE, 4095, METHOD_BUFFERED, FILE_ANY_ACCESS )
//input_buffer is a structure GET_DEV_DESC_REQ, and the input_buffer_length is
//no less than sizeof( input_buffer ), output_buffer is a buffer to receive the
//requested dev's desc, and output_buffer_length specifies the length of the
//buffer
#define IOCTL_SUBMIT_URB_RD CTL_CODE( FILE_HCD_DEV_TYPE, 4096, METHOD_IN_DIRECT, FILE_ANY_ACCESS )
#define IOCTL_SUBMIT_URB_WR CTL_CODE( FILE_HCD_DEV_TYPE, 4097, METHOD_OUT_DIRECT, FILE_ANY_ACCESS )
// input_buffer is a URB, and input_buffer_length is equal to or greater than
// sizeof( URB ); the output_buffer is a buffer to receive data from or send data
// to device. only the following urb fields can be accessed, others must be zeroed.
// DEV_HANDLE endp_handle;
// UCHAR setup_packet[8]; //for control pipe
// the choosing of IOCTL_SUBMIT_URB_RD or IOCTL_SUBMIT_URB_WR should be determined
// by the current URB, for example, a request string from device will use XXX_RD,
// and a write to the bulk endpoint will use XXX_WR
#define IOCTL_SUBMIT_URB_NOIO CTL_CODE( FILE_HCD_DEV_TYPE, 4098, METHOD_BUFFERED, FILE_ANY_ACCESS )
// input_buffer is a URB, and input_buffer_length is equal to or greater than
// sizeof( URB ); the output_buffer is null and no output_buffer_length,
// only the following fields in urb can be accessed, others must be zeroed.
// DEV_HANDLE endp_handle;
// UCHAR setup_packet[8]; //for control pipe
*/