diff --git a/reactos/drivers/usb/directory.rbuild b/reactos/drivers/usb/directory.rbuild index 46b5fa5cf49..084d6236cec 100644 --- a/reactos/drivers/usb/directory.rbuild +++ b/reactos/drivers/usb/directory.rbuild @@ -1,6 +1,9 @@ + + + diff --git a/reactos/drivers/usb/nt4compat/directory.rbuild b/reactos/drivers/usb/nt4compat/directory.rbuild new file mode 100644 index 00000000000..fda5da60b5a --- /dev/null +++ b/reactos/drivers/usb/nt4compat/directory.rbuild @@ -0,0 +1,3 @@ + diff --git a/reactos/drivers/usb/nt4compat/usbdriver/bulkonly.c b/reactos/drivers/usb/nt4compat/usbdriver/bulkonly.c new file mode 100644 index 00000000000..d8f786449ca --- /dev/null +++ b/reactos/drivers/usb/nt4compat/usbdriver/bulkonly.c @@ -0,0 +1,909 @@ +/** + * 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 +#include "debug.h" +#include "usb.h" +#include "umss.h" +#include + +#define OLYMPUS_CSW( pdev_EXT, staTUS ) \ +( ( ( pdev_EXT )->flags & UMSS_DEV_FLAG_OLYMPUS_DEV ) ? ( ( staTUS ) == CSW_OLYMPUS_SIGNATURE ) : FALSE ) + +BOOL +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 +); + +NTSTATUS +umss_bulkonly_startio( +IN PUMSS_DEVICE_EXTENSION pdev_ext, +IN PIO_PACKET io_packet +) +/*++ +Routine Description: + + Handler for all I/O requests using bulk-only protocol. + + Arguments: + + DeviceExtension - Device extension for our FDO. + +Return Value: + + NONE + +--*/ + +{ + 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; +} + +NTSTATUS +umss_sync_submit_urb( +PUMSS_DEVICE_EXTENSION pdev_ext, +PURB purb +) +//can only be called at passive level +{ + 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; +} + +VOID +umss_bulkonly_reset_recovery( +IN PVOID reference +) +/*++ +Routine Description: + + Worker function used to execute a reset recovery after a stall. + + Arguments: + + Reference - Our device extension. + +Return Value: + + NONE + +--*/ + +{ + 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 ); +} + + +VOID +umss_bulkonly_transfer_data( +PUMSS_DEVICE_EXTENSION pdev_ext +) +/*++ +Routine Description: + + Schedules a bulk data transfer to/from the device. + + Arguments: + + DeviceExtension - Our FDO's device extension. + +Return Value: + + NONE + +--*/ + +{ + PVOID data_buf; + ULONG data_buf_length; + NTSTATUS status; + UCHAR trans_dir; + + // 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; +} + + +VOID +umss_bulkonly_transfer_data_complete( +PURB purb, +PVOID reference +) +/*++ +Routine Description: + Completion handler for bulk data transfer requests. +--*/ + +{ + 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 ) ) + { + 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 ); + } +} + + +VOID +umss_bulkonly_get_status_complete( +IN PURB purb, +IN PVOID context +) +/*++ +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. + +--*/ + +{ + 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 ); + 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; +} + + +CHAR +umss_bulkonly_get_maxlun( +IN PUMSS_DEVICE_EXTENSION pdev_ext +) +/*++ +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. + +--*/ + +{ + PURB purb=NULL; + CHAR 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; + } + + return buffer; +} + +BOOL +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 ) ); + 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; +} + +BOOL +umss_clear_pass_through_length( +PIO_PACKET io_packet +) +{ + // + // clear the respective data length to meet request of scsi pass through requirement. + // + + BOOL 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; +} diff --git a/reactos/drivers/usb/nt4compat/usbdriver/cbi.c b/reactos/drivers/usb/nt4compat/usbdriver/cbi.c new file mode 100644 index 00000000000..0911f85ba38 --- /dev/null +++ b/reactos/drivers/usb/nt4compat/usbdriver/cbi.c @@ -0,0 +1,363 @@ +/** + * 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 +#include "debug.h" +#include "umss.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; + + purb = usb_alloc_mem( NonPagedPool, sizeof( URB ) ); + // 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; + PIO_STACK_LOCATION irpStack; + PUMSS_DEVICE_EXTENSION pdev_ext; + PIO_PACKET io_packet; + ULONG bytes_to_transfer; + PUCHAR buf; + + 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; + 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 ); + } +} diff --git a/reactos/drivers/usb/nt4compat/usbdriver/compdrv.c b/reactos/drivers/usb/nt4compat/usbdriver/compdrv.c new file mode 100644 index 00000000000..732a77c2ac4 --- /dev/null +++ b/reactos/drivers/usb/nt4compat/usbdriver/compdrv.c @@ -0,0 +1,566 @@ +/** + * compdrv.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 + */ + +//this driver is part of the dev manager responsible to manage if device + +#include "td.h" +#include "ntddk.h" +#include "umss.h" +#include "usb.h" +#include "hub.h" +#include "debug.h" + +VOID +compdev_set_cfg_completion( +PURB purb, +PVOID context +); + +VOID +compdev_select_driver( +PUSB_DEV_MANAGER dev_mgr, +DEV_HANDLE dev_handle +); + +BOOL +compdev_connect( +PCONNECT_DATA param, +DEV_HANDLE dev_handle +); + +BOOL +compdev_stop( +PUSB_DEV_MANAGER dev_mgr, +DEV_HANDLE dev_handle +); + +BOOL +compdev_disconnect( +PUSB_DEV_MANAGER dev_mgr, +DEV_HANDLE dev_handle +); + +BOOL +compdev_driver_init( +PUSB_DEV_MANAGER dev_mgr, +PUSB_DRIVER pdriver +) +{ + if( dev_mgr == NULL || pdriver == NULL ) + return FALSE; + + pdriver->driver_desc.flags = USB_DRIVER_FLAG_DEV_CAPABLE; + pdriver->driver_desc.vendor_id = 0xffff; // USB Vendor ID + pdriver->driver_desc.product_id = 0xffff; // USB Product ID. + pdriver->driver_desc.release_num = 0x100; // Release Number of Device + + pdriver->driver_desc.config_val = 0; // Configuration Value + pdriver->driver_desc.if_num = 0; // Interface Number + pdriver->driver_desc.if_class = 0; // Interface Class + pdriver->driver_desc.if_sub_class = 0; // Interface SubClass + pdriver->driver_desc.if_protocol = 0; // Interface Protocol + + pdriver->driver_desc.driver_name = "USB composit dev driver"; // Driver name for Name Registry + pdriver->driver_desc.dev_class = USB_CLASS_PER_INTERFACE; + pdriver->driver_desc.dev_sub_class = 0; // Device Subclass + pdriver->driver_desc.dev_protocol = 0; // Protocol Info. + + //we have no extra data sturcture currently + pdriver->driver_ext = NULL; + pdriver->driver_ext_size = 0; + + pdriver->disp_tbl.version = 1; + pdriver->disp_tbl.dev_connect = compdev_connect; + pdriver->disp_tbl.dev_disconnect = compdev_disconnect; + pdriver->disp_tbl.dev_stop = compdev_stop; + pdriver->disp_tbl.dev_reserved = NULL; + + return TRUE; +} + +BOOL +compdev_driver_destroy( +PUSB_DEV_MANAGER dev_mgr, +PUSB_DRIVER pdriver +) +{ + return TRUE; +} + +BOOL +compdev_connect( +PCONNECT_DATA param, +DEV_HANDLE dev_handle +) +{ + PURB purb; + PUSB_CTRL_SETUP_PACKET psetup; + NTSTATUS status; + PUCHAR buf; + LONG credit, i, j, match; + PUSB_CONFIGURATION_DESC pconfig_desc; + PUSB_INTERFACE_DESC pif_desc; + PUSB_DEV_MANAGER dev_mgr; + + if( param == NULL || dev_handle == 0 ) + return FALSE; + + dev_mgr = param->dev_mgr; + + // let's set the configuration + purb = usb_alloc_mem( NonPagedPool, sizeof( URB ) ); + if( purb == NULL ) + return FALSE; + + buf = usb_alloc_mem( NonPagedPool, 512 ); + if( buf == NULL ) + { + usb_dbg_print( DBGLVL_MAXIMUM, ( "compdev_connect(): can not alloc buf\n" ) ); + usb_free_mem( purb ); + return FALSE; + } + + // before we set the configuration, let's search to find if there + // exist interfaces we supported + psetup = ( PUSB_CTRL_SETUP_PACKET )( purb )->setup_packet; + urb_init( ( purb ) ); + purb->endp_handle = dev_handle | 0xffff; + purb->data_buffer = buf; + purb->data_length = 512; + purb->completion = NULL; // this is an immediate request, no completion required + purb->context = NULL; + purb->reference = 0; + psetup->bmRequestType = 0x80; + psetup->bRequest = USB_REQ_GET_DESCRIPTOR; + psetup->wValue = USB_DT_CONFIG << 8; + psetup->wIndex = 0; + psetup->wLength = 512; + + status = usb_submit_urb( dev_mgr, purb ); + if( status == STATUS_PENDING ) + { + TRAP(); + usb_free_mem( buf ); + usb_free_mem( purb ); + return FALSE; + } + + // let's scan the interfacs for those we recognize + pconfig_desc = ( PUSB_CONFIGURATION_DESC )buf; + if( pconfig_desc->wTotalLength > 512 ) + { + usb_free_mem( buf ); + usb_free_mem( purb ); + usb_dbg_print( DBGLVL_MAXIMUM, ( "compdev_connect(): error, bad configuration desc\n" ) ); + return FALSE; + } + + pif_desc = ( PUSB_INTERFACE_DESC )&pconfig_desc[ 1 ]; + for( i = 0, credit = 0; i < ( LONG )pconfig_desc->bNumInterfaces; i++ ) + { + for( j = 0; j < DEVMGR_MAX_DRIVERS; j++ ) + { + credit = dev_mgr_score_driver_for_if( dev_mgr, &dev_mgr->driver_list[ j ], pif_desc ); + if( credit ) + break; + } + if( credit ) + break; + + if( usb_skip_if_and_altif( ( PUCHAR* )&pif_desc ) ) + break; + } + + i = pconfig_desc->bConfigurationValue; + usb_free_mem( buf ); + buf = NULL; + if( credit == 0 ) + { + usb_free_mem( purb ); + usb_dbg_print( DBGLVL_MAXIMUM, ( "compdev_connect(): oops..., no supported interface found\n" ) ); + return FALSE; + } + + //set the configuration + urb_init( purb ); + purb->endp_handle = dev_handle | 0xffff; + purb->data_buffer = NULL; + purb->data_length = 0; + purb->completion = compdev_set_cfg_completion; + purb->context = dev_mgr; + purb->reference = ( ULONG )param->pdriver; + psetup->bmRequestType = 0; + psetup->bRequest = USB_REQ_SET_CONFIGURATION; + psetup->wValue = ( USHORT ) i; + psetup->wIndex = 0; + psetup->wLength = 0; + + usb_dbg_print( DBGLVL_MAXIMUM, ( "compdev_connect(): start config the device, cfgval=%d\n", i ) ); + status = usb_submit_urb( dev_mgr, purb ); + + if( status != STATUS_PENDING ) + { + usb_free_mem( purb ); + + if( status == STATUS_SUCCESS ) + return TRUE; + + return FALSE; + } + + return TRUE; +} + +VOID +compdev_event_select_if_driver( +PUSB_DEV pdev, +ULONG event, +ULONG context, +ULONG param +) +{ + PUSB_DEV_MANAGER dev_mgr; + DEV_HANDLE dev_handle; + PUMSS_CREATE_DATA cd; + + if( pdev == NULL ) + return; + + // + // RtlZeroMemory( &cd, sizeof( cd ) ); + // + dev_mgr = dev_mgr_from_dev( pdev ); + dev_handle = usb_make_handle( pdev->dev_id, 0, 0 ); + compdev_select_driver( dev_mgr, dev_handle ); + return; +} + +BOOL +compdev_post_event_select_driver( +PUSB_DEV_MANAGER dev_mgr, +DEV_HANDLE dev_handle +) +{ + PUSB_EVENT pevent; + BOOL bret; + PUSB_DEV pdev; + + USE_IRQL; + + if( dev_mgr == NULL || dev_handle == 0 ) + return FALSE; + + if( usb_query_and_lock_dev( dev_mgr, dev_handle, &pdev ) != STATUS_SUCCESS ) + return FALSE; + + KeAcquireSpinLockAtDpcLevel( &dev_mgr->event_list_lock ); + lock_dev( pdev, TRUE ); + + if( dev_state( pdev ) == USB_DEV_STATE_ZOMB ) + { + bret = FALSE; + goto LBL_OUT; + } + + pevent = alloc_event( &dev_mgr->event_pool, 1 ); + if( pevent == NULL ) + { + bret = FALSE; + goto LBL_OUT; + } + pevent->flags = USB_EVENT_FLAG_ACTIVE; + pevent->event = USB_EVENT_DEFAULT; + pevent->pdev = pdev; + pevent->context = 0; + pevent->param = 0; + pevent->pnext = 0; //vertical queue for serialized operation + pevent->process_event = compdev_event_select_if_driver; + pevent->process_queue = event_list_default_process_queue; + + InsertTailList( &dev_mgr->event_list, &pevent->event_link ); + KeSetEvent( &dev_mgr->wake_up_event, 0, FALSE ); // wake up the dev_mgr_thread + bret = TRUE; + + LBL_OUT: + + unlock_dev( pdev, TRUE ); + KeReleaseSpinLockFromDpcLevel( &dev_mgr->event_list_lock ); + usb_unlock_dev( pdev ); + return bret; +} + +VOID +compdev_set_cfg_completion( +PURB purb, +PVOID context +) +{ + DEV_HANDLE dev_handle; + PUSB_DEV_MANAGER dev_mgr; + PWORK_QUEUE_ITEM pwork_item; + PUSB_DRIVER pdriver; + NTSTATUS status; + PUSB_DEV pdev; + + USE_IRQL; + + if( purb == NULL || context == NULL ) + return; + + dev_handle = purb->endp_handle & ~0xffff; + dev_mgr = ( PUSB_DEV_MANAGER ) context; + pdriver = ( PUSB_DRIVER )purb->reference; + + if( purb->status != STATUS_SUCCESS ) + { + usb_free_mem( purb ); + return; + } + + usb_free_mem( purb ); + purb = NULL; + + // set the dev state + status = usb_query_and_lock_dev( dev_mgr, dev_handle, &pdev ); + if( status != STATUS_SUCCESS ) + { + usb_unlock_dev( pdev ); + return; + } + // safe to release the pdev ref since we are in urb completion + usb_unlock_dev( pdev ); + + lock_dev( pdev, TRUE ); + if( dev_state( pdev ) >= USB_DEV_STATE_BEFORE_ZOMB ) + { + unlock_dev( pdev, TRUE ); + return; + } + + if( dev_mgr_set_driver( dev_mgr, dev_handle, pdriver, pdev ) == FALSE ) + return; + + //transit the state to configured + pdev->flags &= ~USB_DEV_STATE_MASK; + pdev->flags |= USB_DEV_STATE_CONFIGURED; + unlock_dev( pdev, TRUE ); + + // + // we change to use our thread for driver choosing. it will reduce + // the race condition when different pnp event comes simultaneously + // + usb_dbg_print( DBGLVL_MAXIMUM, ( "compdev_set_cfg_completion(): start select driver for the dev\n" ) ); + compdev_post_event_select_driver( dev_mgr, dev_handle ); + + return; +} + +VOID +compdev_select_driver( +PUSB_DEV_MANAGER dev_mgr, +DEV_HANDLE dev_handle +) +{ + URB urb; + LONG i, j, k, credit; + ULONG dev_id; + PUCHAR buf; + NTSTATUS status; + PUSB_DRIVER pcand, ptemp_drv; + + PUSB_CTRL_SETUP_PACKET psetup; + PUSB_INTERFACE_DESC pif_desc; + PUSB_CONFIGURATION_DESC pconfig_desc; + PUSB_DEV pdev; + + USE_IRQL; + + usb_dbg_print( DBGLVL_MAXIMUM, ( "compdev_select_driver(): entering...\n" ) ); + + dev_id = dev_handle >> 16; + + buf = usb_alloc_mem( NonPagedPool, 512 ); + + if( buf == NULL ) + return; + + // now let's get the descs, one configuration + urb_init( &urb ); + psetup = ( PUSB_CTRL_SETUP_PACKET )urb.setup_packet; + urb.endp_handle = dev_handle | 0xffff; + urb.data_buffer = buf; + urb.data_length = 512; + urb.completion = NULL; // this is an immediate request, no completion required + urb.context = NULL; + urb.reference = 0; + psetup->bmRequestType = 0x80; + psetup->bRequest = USB_REQ_GET_DESCRIPTOR; + psetup->wValue = USB_DT_CONFIG << 8; + psetup->wIndex = 0; + psetup->wLength = 512; + + status = usb_submit_urb( dev_mgr, &urb ); + if( status == STATUS_PENDING ) + { + TRAP(); + } + + // let's scan the interfacs for those we recognize + pconfig_desc = ( PUSB_CONFIGURATION_DESC )buf; + if( pconfig_desc->wTotalLength > 512 ) + { + usb_free_mem( buf ); + usb_dbg_print( DBGLVL_MAXIMUM, ( "compdev_select_driver(): error, bad configuration desc\n" ) ); + return; + } + pif_desc = ( PUSB_INTERFACE_DESC )&pconfig_desc[ 1 ]; + + if( usb_query_and_lock_dev( dev_mgr, dev_handle, &pdev ) != STATUS_SUCCESS ) + { + usb_free_mem( buf ); + usb_dbg_print( DBGLVL_MAXIMUM, ( "compdev_select_driver(): error, dev does not exist\n" ) ); + return; + } + + for( i = 0; i < ( LONG )pconfig_desc->bNumInterfaces; i++ ) + { + for( j = 0, credit = 0, pcand = NULL; j < DEVMGR_MAX_DRIVERS; j++ ) + { + ptemp_drv = &dev_mgr->driver_list[ j ]; + k = dev_mgr_score_driver_for_if( dev_mgr, ptemp_drv, pif_desc ); + if( k > credit ) + credit = k, pcand = ptemp_drv; + } + + if( credit ) + { + // ok, we find one + CONNECT_DATA param; + + if( pcand->disp_tbl.dev_connect ) + { + param.dev_mgr = dev_mgr; + param.pdriver = pcand; + param.dev_handle = 0; + pcand->disp_tbl.dev_connect( ¶m, usb_make_handle( dev_id, i, 0 ) ); + } + } + if( usb_skip_if_and_altif( ( PUCHAR* )&pif_desc ) == FALSE ) + { + break; + } + } + usb_unlock_dev( pdev ); + + if( buf ) + { + usb_free_mem( buf ); + buf = NULL; + } + return; +} + +BOOL +compdev_stop( +PUSB_DEV_MANAGER dev_mgr, +DEV_HANDLE dev_handle +) +{ + PUSB_DEV pdev; + LONG i; + ULONG dev_id; + PUSB_DRIVER pdrv; + NTSTATUS status; + + if( dev_mgr == NULL || dev_handle == 0 ) + return FALSE; + + pdev = NULL; + dev_id = dev_handle >> 16; + status = usb_query_and_lock_dev( dev_mgr, dev_handle, &pdev ); + if( pdev ) + { + if( pdev->usb_config ) + { + for( i = 0; i < pdev->usb_config->if_count; i++ ) + { + if( pdrv = pdev->usb_config->interf[ i ].pif_drv ) + { + pdrv->disp_tbl.dev_stop( dev_mgr, usb_make_handle( dev_id, i, 0 ) ); + } + } + } + } + if( status == STATUS_SUCCESS ) + { + usb_unlock_dev( pdev ); + } + return TRUE; +} + +BOOL +compdev_disconnect( +PUSB_DEV_MANAGER dev_mgr, +DEV_HANDLE dev_handle +) +{ + PUSB_DEV pdev; + LONG i; + ULONG dev_id; + PUSB_DRIVER pdrv; + NTSTATUS status; + + if( dev_mgr == NULL || dev_handle == 0 ) + return FALSE; + + pdev = NULL; + dev_id = dev_handle >> 16; + status = usb_query_and_lock_dev( dev_mgr, dev_handle, &pdev ); + if( pdev ) + { + if( pdev->usb_config ) + { + for( i = 0; i < pdev->usb_config->if_count; i++ ) + { + if( pdrv = pdev->usb_config->interf[ i ].pif_drv ) + { + pdrv->disp_tbl.dev_disconnect( dev_mgr, usb_make_handle( dev_id, i, 0 ) ); + } + } + } + } + if( status == STATUS_SUCCESS ) + { + usb_unlock_dev( pdev ); + } + return TRUE; +} + +// note: +// dev_mgr_set_driver seems to be dangeous since compdev, gendrv and hub and +// umss use it to set the driver while there may exist race condition when the +// dev_mgr_disconnect_dev is called. If the driver is set and the +// disconnect_dev cut in immediately, the stop or disconnect may not function +// well. Now hub and compdev's set dev_mgr_set_driver are ok. +// +// another danger comes from umss's dev irp processing. This may confuse the device +// when the disk is being read or written, and at the same time dmgrdisp's dispatch +// route irp request to the device a control request. diff --git a/reactos/drivers/usb/nt4compat/usbdriver/debug.h b/reactos/drivers/usb/nt4compat/usbdriver/debug.h new file mode 100644 index 00000000000..9bb965ca77b --- /dev/null +++ b/reactos/drivers/usb/nt4compat/usbdriver/debug.h @@ -0,0 +1,70 @@ +#ifndef _UHCIDBG_H_ +#define _UHCIDBG_H_ + +#define DBGLVL_OFF 0 // if gDebugLevel set to this, there is NO debug output +#define DBGLVL_MINIMUM 1 // minimum verbosity +#define DBGLVL_DEFAULT 2 // default verbosity level if no registry override +#define DBGLVL_MEDIUM 3 // medium verbosity +#define DBGLVL_HIGH 4 // highest 'safe' level (without severely affecting timing ) +#define DBGLVL_MAXIMUM 5 // maximum level, may be dangerous + +#ifndef DBGSTR_PREFIX +#define DBGSTR_PREFIX "wood_uhci: " +#endif + +#define DEBUG_UHCI TRUE +#define DEBUG_HUB TRUE +#define DEBUG_DEV_MGR TRUE + +#define DPRINT DbgPrint + + +#define UHCI_DBGOUTSIZE 512 + +#define hcd_dbg_print_cond( ilev, cond, _x_) \ + if( debug_level && ( ilev <= debug_level ) && ( cond )) { \ + DPRINT( DBGSTR_PREFIX ); \ + DPRINT _x_ ; \ + } + +#define hcd_dbg_print( ilev, _x_) hcd_dbg_print_cond( ilev, TRUE, _x_ ) + +extern ULONG debug_level; + +#if DBG + +#define uhci_dbg_print_cond( ilev, cond, _x_ ) hcd_dbg_print_cond( ilev, cond, _x_ ) +#define uhci_dbg_print( ilev, _x_) hcd_dbg_print_cond( ilev, TRUE, _x_ ) + +#define uhci_trap_cond( ilev, cond ) if ( debug_level && ( ilev <= debug_level ) && (cond) ) TRAP() +#define uhci_trap( ilev ) uhci_trap_cond( ilev, TRUE ) + + +#define uhci_assert( cond ) ASSERT( cond ) +#define dbg_count_list( _x_ ) usb_count_list( _x_ ) + +#define TRAP() DbgBreakPoint() + +#else // if not DBG + +// dummy definitions that go away in the retail build + +#define uhci_dbg_print_cond( ilev, cond, _x_ ) +#define uhci_dbg_print( ilev, _x_) +#define uhci_trap_cont( ilev, cond ) +#define uhci_trap( ilev ) +#define uhci_assert( cond ) +#define TRAP() +#define dbg_count_list( _x_ ) 0 + +#endif //DBG + +#define usb_dbg_print( ilev, _x_ ) uhci_dbg_print( ilev, _x_ ) +#define ehci_dbg_print( ilev, _x_ ) uhci_dbg_print( ilev, _x_ ) +#define ohci_dbg_print( ilev, _x_ ) uhci_dbg_print( ilev, _x_ ) +#define ehci_dbg_print_cond( ilev, cond, _x_ ) uhci_dbg_print_cond( ilev, cond, _x_ ) + +#define DO_NOTHING + +LONG usb_count_list( struct _LIST_ENTRY* list_head ); +#endif // included diff --git a/reactos/drivers/usb/nt4compat/usbdriver/dmgrdisp.c b/reactos/drivers/usb/nt4compat/usbdriver/dmgrdisp.c new file mode 100644 index 00000000000..e1395235046 --- /dev/null +++ b/reactos/drivers/usb/nt4compat/usbdriver/dmgrdisp.c @@ -0,0 +1,551 @@ +/** + * 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 +#include "usb.h" +#include "debug.h" +#include "td.h" +#include "hub.h" + +VOID +disp_urb_completion( +PURB purb, +PVOID context +) +{ + PUSB_CTRL_SETUP_PACKET psetup; + PUSB_DEV_MANAGER dev_mgr; + ULONG ctrl_code; + NTSTATUS status; + PDEVEXT_HEADER dev_hdr; + + 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; + 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; +} + +NTSTATUS +dev_mgr_dispatch( +IN PUSB_DEV_MANAGER dev_mgr, +IN PIRP irp +) +//this function is called by the hcd's +//dispatch when they have done their job. +{ + PIO_STACK_LOCATION irp_stack; + NTSTATUS status; + ULONG ctrl_code; + USE_IRQL; + + if( dev_mgr == NULL || irp == 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; + 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; + KIRQL old_irql; + 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: + { + LONG buf_size; + PURB purb; + KIRQL old_irql; + ULONG endp_idx, if_idx, user_buffer_length; + PUCHAR user_buffer; + PUSB_DEV pdev; + DEV_HANDLE endp_handle; + PUSB_ENDPOINT pendp; + + PUSB_CTRL_SETUP_PACKET psetup; + + 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 +*/ diff --git a/reactos/drivers/usb/nt4compat/usbdriver/ehci.c b/reactos/drivers/usb/nt4compat/usbdriver/ehci.c new file mode 100644 index 00000000000..e49c9944b83 --- /dev/null +++ b/reactos/drivers/usb/nt4compat/usbdriver/ehci.c @@ -0,0 +1,6733 @@ +/** + * ehci.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 "td.h" +#include "hub.h" +#include "debug.h" +#include "stdio.h" +#include "usb.h" +#include +#include "ehci.h" +//---------------------------------------------------------- +// ehci routines +//#define DEMO + +#define DEFAULT_ENDP( enDP ) \ +( enDP->flags & USB_ENDP_FLAG_DEFAULT_ENDP ) + +#define dev_from_endp( enDP ) \ +( DEFAULT_ENDP( enDP )\ + ? ( ( PUSB_DEV )( enDP )->pusb_if )\ + : ( ( enDP )->pusb_if->pusb_config->pusb_dev ) ) + +#define endp_state( enDP ) ( ( enDP )->flags & USB_ENDP_FLAG_STAT_MASK ) + +#define endp_num( enDP ) \ +( DEFAULT_ENDP( enDP )\ + ? 0 \ + : ( ( enDP )->pusb_endp_desc->bEndpointAddress & 0x0f ) ) + +#define endp_dir( enDP ) \ +( DEFAULT_ENDP( enDP )\ + ? 0L\ + : ( ( enDP )->pusb_endp_desc->bEndpointAddress & USB_DIR_IN ) ) + +#define dev_set_state( pdEV, staTE ) \ +( pdEV->flags = ( ( pdEV )->flags & ( ~USB_DEV_STATE_MASK ) ) | ( staTE ) ) + +#define endp_max_packet_size( enDP ) \ +( DEFAULT_ENDP( enDP )\ + ? ( ( ( PUSB_DEV )enDP->pusb_if )->pusb_dev_desc ? \ + ( ( PUSB_DEV )enDP->pusb_if )->pusb_dev_desc->bMaxPacketSize0\ + : 8 )\ + : ( enDP->pusb_endp_desc->wMaxPacketSize & 0x7ff ) ) + +#define endp_mult_count( endp ) ( ( ( endp->pusb_endp_desc->wMaxPacketSize & 0x1800 ) >> 11 ) + 1 ) + +#define release_adapter( padapTER ) \ +{\ + ( ( padapTER ) ); \ +} + +#define get_int_idx( _urb, _idx ) \ +{\ + UCHAR interVAL;\ + interVAL = ( UCHAR )( ( _urb )->pipe >> 24 );\ + for( _idx = 1; _idx < 9; _idx++ )\ + {\ + interVAL >>= 1;\ + if( !interVAL )\ + break;\ + }\ + _idx --;\ +} + +#define ehci_insert_urb_to_schedule( eHCI, pURB, rET ) \ +{\ + SYNC_PARAM sync_param;\ + sync_param.ehci = eHCI;\ + sync_param.context = ( pURB );\ + sync_param.ret = FALSE;\ +\ + rET = KeSynchronizeExecution( eHCI->pdev_ext->ehci_int, ehci_sync_insert_urb_schedule, &sync_param );\ +} + +#define EHCI_ERROR_INT ( STS_FATAL | STS_ERR ) +#define EHCI_QH_ERROR( qh_contENT ) ( ( qh_contENT )->cur_qtd.status & ( QTD_STS_HALT | QTD_STS_DBE | QTD_STS_BABBLE | QTD_STS_XACT | QTD_STS_MMF ) ) +#define EHCI_QTD_ERROR( qtd_contENT ) ( ( qtd_contENT )->status & ( QTD_STS_HALT | QTD_STS_DBE | QTD_STS_BABBLE | QTD_STS_XACT | QTD_STS_MMF ) ) + +#define EHCI_READ_PORT_ULONG( pul ) ( *pul ) +#define EHCI_WRITE_PORT_ULONG( pul, src ) \ +{\ + ULONG cmd_reg;\ + *pul = ( ULONG )src;\ + cmd_reg = EHCI_READ_PORT_ULONG( ehci->port_base + EHCI_USBCMD );\ + if( cmd_reg == 0 )\ + cmd_reg++;\ +} + +#define EHCI_READ_PORT_UCHAR( pch ) ( *pch ) +#define EHCI_WRITE_PORT_UCHAR( pch, src ) ( *pch = ( UCHAR )src ) + +#define EHCI_READ_PORT_USHORT( psh ) ( *psh ) +#define EHCI_WRITE_PORT_USHORT( psh, src ) ( *psh = ( USHORT )src ) + +#define press_doorbell( eHCI ) \ +{\ + ULONG tmp;\ + tmp = EHCI_READ_PORT_ULONG( ( PULONG )( ( eHCI )->port_base + EHCI_USBCMD ) );\ + tmp |= CMD_IAAD;\ + EHCI_WRITE_PORT_ULONG( ( PULONG )( ( eHCI )->port_base + EHCI_USBCMD ), tmp );\ +} +#define ehci_from_hcd( hCD ) ( struct_ptr( ( hCD ), EHCI_DEV, hcd_interf ) ) + +#define qh_from_list_entry( pentry ) ( ( PEHCI_QH )( ( ( ULONG )struct_ptr( pentry, EHCI_ELEM_LINKS, elem_link )->phys_part ) & PHYS_PART_ADDR_MASK ) ) +#define qtd_from_list_entry( pentry ) ( ( PEHCI_QTD )( ( ( ULONG )struct_ptr( pentry, EHCI_ELEM_LINKS, elem_link )->phys_part ) & PHYS_PART_ADDR_MASK ) ) +#define itd_from_list_entry( pentry ) ( ( PEHCI_ITD )( ( ( ULONG )struct_ptr( pentry, EHCI_ELEM_LINKS, elem_link )->phys_part ) & PHYS_PART_ADDR_MASK ) ) +#define sitd_from_list_entry( pentry ) ( ( PEHCI_SITD )( ( ( ULONG )struct_ptr( pentry, EHCI_ELEM_LINKS, elem_link )->phys_part ) & PHYS_PART_ADDR_MASK ) ) +#define fstn_from_list_entry( pentry ) ( ( PEHCI_FSTN )( ( ( ULONG )struct_ptr( pentry, EHCI_ELEM_LINKS, elem_link )->phys_part ) & PHYS_PART_ADDR_MASK ) ) + +#define qh_from_schedule( pentry ) ( ( PEHCI_QH )( ( ( ULONG )struct_ptr( pentry, EHCI_ELEM_LINKS, sched_link )->phys_part ) & PHYS_PART_ADDR_MASK ) ) +#define itd_from_schedule( pentry ) ( ( PEHCI_ITD )( ( ( ULONG )struct_ptr( pentry, EHCI_ELEM_LINKS, sched_link )->phys_part ) & PHYS_PART_ADDR_MASK ) ) +#define sitd_from_schedule( pentry ) ( ( PEHCI_SITD )( ( ( ULONG )struct_ptr( pentry, EHCI_ELEM_LINKS, sched_link )->phys_part ) & PHYS_PART_ADDR_MASK ) ) +#define fstn_from_schedule( pentry ) ( ( PEHCI_FSTN )( ( ( ULONG )struct_ptr( pentry, EHCI_ELEM_LINKS, sched_link )->phys_part ) & PHYS_PART_ADDR_MASK ) ) + +#define elem_type( ptr, from_list ) ( from_list ? ( ( ( ( ULONG )struct_ptr( ptr, EHCI_ELEM_LINKS, elem_link)->phys_part ) & PHYS_PART_TYPE_MASK ) >> 1 ) \ + : ( ( ( ( ULONG )struct_ptr( ptr, EHCI_ELEM_LINKS, sched_link)->phys_part ) & PHYS_PART_TYPE_MASK ) >> 1 ) ) + +// #define elem_type_list_entry( pentry ) ( ( qh_from_schedule( pentry )->hw_next & 0x06 ) >> 1 ) +#define elem_type_list_entry( pentry ) ( elem_type( pentry, TRUE ) ) + +#define get_parent_hs_hub( pDEV, parent_HUB, port_IDX ) \ +{\ + parent_HUB = pDEV->parent_dev;\ + port_IDX = pdev->port_idx;\ + while( parent_HUB )\ + {\ + if( ( parent_HUB->flags & USB_DEV_CLASS_MASK ) != USB_DEV_CLASS_HUB )\ + {\ + parent_HUB = NULL;\ + break;\ + }\ + if( ( parent_HUB->flags & USB_DEV_FLAG_HIGH_SPEED ) == 0 )\ + {\ + port_IDX = parent_HUB->port_idx;\ + parent_HUB = parent_HUB->parent_dev;\ + continue;\ + }\ + break;\ + }\ +} + +#define init_elem_phys_part( pelnk ) RtlZeroMemory( ( PVOID )( ( ( ULONG )( pelnk )->phys_part ) & PHYS_PART_ADDR_MASK ), get_elem_phys_part_size( ( ( ( ULONG )( pelnk )->phys_part ) & 0x06 ) >> 1 ) ) +#define REAL_INTERVAL ( 1 << pipe_content->interval ) + +#define elem_safe_free( ptHIS, single ) \ +{\ + UCHAR em_type; \ + em_type = ( UCHAR )elem_type( ptHIS, TRUE ); \ + if( ptHIS )\ + {\ + if( em_type == INIT_LIST_FLAG_QTD )\ + {\ + elem_pool_lock( qtd_pool, TRUE );\ + if( single )\ + elem_pool_free_elem( qtd_from_list_entry( ptHIS )->elem_head_link );\ + else \ + elem_pool_free_elems( qtd_from_list_entry( ptHIS )->elem_head_link );\ + elem_pool_unlock( qtd_pool, TRUE );\ + }\ + else if( em_type == INIT_LIST_FLAG_ITD )\ + {\ + elem_pool_lock( itd_pool, TRUE );\ + if( single )\ + elem_pool_free_elem( itd_from_list_entry( ptHIS )->elem_head_link );\ + else \ + elem_pool_free_elems( itd_from_list_entry( ptHIS )->elem_head_link );\ + elem_pool_unlock( itd_pool, TRUE );\ + }\ + else if( em_type == INIT_LIST_FLAG_SITD )\ + {\ + elem_pool_lock( sitd_pool, TRUE );\ + if( single )\ + elem_pool_free_elem( sitd_from_list_entry( ptHIS )->elem_head_link );\ + else \ + elem_pool_free_elems( sitd_from_list_entry( ptHIS )->elem_head_link );\ + elem_pool_unlock( sitd_pool, TRUE );\ + }\ + else if( em_type == INIT_LIST_FLAG_FSTN )\ + {\ + elem_pool_lock( fstn_pool, TRUE );\ + if( single )\ + elem_pool_free_elem( fstn_from_list_entry( ptHIS )->elem_head_link );\ + else \ + elem_pool_free_elems( fstn_from_list_entry( ptHIS )->elem_head_link );\ + elem_pool_unlock( fstn_pool, TRUE );\ + }\ + else if( em_type == INIT_LIST_FLAG_QH )\ + {\ + elem_pool_lock( qh_pool, TRUE );\ + if( single )\ + elem_pool_free_elem( qh_from_list_entry( ptHIS )->elem_head_link );\ + else \ + elem_pool_free_elems( qh_from_list_entry( ptHIS )->elem_head_link );\ + elem_pool_unlock( qh_pool, TRUE );\ + }\ + }\ +} + +#define min( a, b ) ( ( a ) > ( b ) ? ( b ) : ( a ) ) +#define max( a, b ) ( ( a ) > ( b ) ? ( a ) : ( b ) ) + +#define CLR_RH2_PORTSTAT( port_idx, x ) \ +{\ + PULONG addr; \ + addr = ( PULONG )( ehci->port_base + port_idx ); \ + status = EHCI_READ_PORT_ULONG( addr ); \ + status = ( status & 0xfffffd5 ) & ~( x ); \ + EHCI_WRITE_PORT_ULONG( addr, ( ULONG )status ); \ +} + +#define SET_RH2_PORTSTAT( port_idx, x ) \ +{\ + PULONG addr; \ + addr = ( PULONG )( ehci->port_base + port_idx ); \ + status = EHCI_READ_PORT_ULONG( addr ); \ + if( x & PORT_PR ) \ + status = ( status & 0xffffffd1 ) | ( x ); \ + else \ + status = ( status & 0xffffffd5 ) | ( x ); \ + EHCI_WRITE_PORT_ULONG( addr, ( ULONG )status ); \ +} + +#define ehci_from_hcd( hCD ) ( struct_ptr( ( hCD ), EHCI_DEV, hcd_interf ) ) +#define ehci_from_dev( dEV ) ( ehci_from_hcd( dEV->hcd ) ) + +#define ehci_copy_overlay( pQHC, pTDC ) \ +{\ + LONG td_size;\ + PEHCI_QH pqh1;\ + PEHCI_QTD ptd1;\ + pqh1 = ( PEHCI_QH )( pQHC );\ + ptd1 = ( PEHCI_QTD )( pTDC );\ + td_size = get_elem_phys_part_size( INIT_LIST_FLAG_QTD );\ + ( pQHC )->cur_qtd_ptr = ptd1->phys_addr;\ + RtlZeroMemory( &( pQHC )->cur_qtd, td_size );\ + ( pQHC )->cur_qtd.data_toggle = ( pTDC )->data_toggle;\ + pqh1->hw_qtd_next = ptd1->phys_addr;\ + pqh1->hw_alt_next = EHCI_PTR_TERM;\ +} + +//declarations +typedef struct +{ + union + { + PUHCI_DEV uhci; + PEHCI_DEV ehci; + }; + PVOID context; + ULONG ret; + +} SYNC_PARAM, *PSYNC_PARAM; + +PDEVICE_OBJECT +ehci_alloc( +PDRIVER_OBJECT drvr_obj, +PUNICODE_STRING reg_path, +ULONG bus_addr, +PUSB_DEV_MANAGER dev_mgr +); + +BOOL +ehci_init_schedule( +PEHCI_DEV ehci, +PADAPTER_OBJECT padapter +); + +BOOL +ehci_release( +PDEVICE_OBJECT pdev +); + +static VOID +ehci_stop( +PEHCI_DEV ehci +); + +BOOL +ehci_destroy_schedule( +PEHCI_DEV ehci +); + +BOOLEAN +ehci_sync_insert_urb_schedule( +PVOID context +); + +VOID +ehci_init_hcd_interface( +PEHCI_DEV ehci +); + +NTSTATUS +ehci_rh_submit_urb( +PUSB_DEV rh, +PURB purb +); + +NTSTATUS +ehci_dispatch_irp( +IN PDEVICE_OBJECT DeviceObject, +IN PIRP irp +); + +VOID +ehci_generic_urb_completion( +PURB purb, +PVOID context +); + +static NTSTATUS +ehci_internal_submit_bulk( +PEHCI_DEV ehci, +PURB purb +); + +static NTSTATUS +ehci_internal_submit_int( +PEHCI_DEV ehci, +PURB purb +); + +static NTSTATUS +ehci_internal_submit_ctrl( +PEHCI_DEV ehci, +PURB purb +); + +static NTSTATUS +ehci_internal_submit_iso( +PEHCI_DEV ehci, +PURB purb +); + +static ULONG +ehci_scan_iso_error( +PEHCI_DEV ehci, +PURB purb +); + +BOOL +ehci_claim_bandwidth( +PEHCI_DEV ehci, +PURB purb, +BOOL claim_bw +);//true to claim band-width, false to free band-width + +static VOID +ehci_insert_bulk_schedule( +PEHCI_DEV ehci, +PURB purb +); + +#define ehci_insert_control_schedule ehci_insert_bulk_schedule + +static VOID +ehci_insert_int_schedule( +PEHCI_DEV ehci, +PURB purb +); + +static VOID +ehci_insert_iso_schedule( +PEHCI_DEV ehci, +PURB purb +); + +#define ehci_remove_control_from_schedule ehci_remove_bulk_from_schedule + +PDEVICE_OBJECT +ehci_probe( +PDRIVER_OBJECT drvr_obj, +PUNICODE_STRING reg_path, +PUSB_DEV_MANAGER dev_mgr +); + +PDEVICE_OBJECT +ehci_create_device( +PDRIVER_OBJECT drvr_obj, +PUSB_DEV_MANAGER dev_mgr +); + +BOOL +ehci_delete_device( +PDEVICE_OBJECT pdev +); + +VOID +ehci_get_capabilities( +PEHCI_DEV ehci, +PBYTE base +); + +BOOLEAN +ehci_isr( +PKINTERRUPT interrupt, +PVOID context +); + +BOOL +ehci_start( +PHCD hcd +); + +extern VOID +rh_timer_svc_reset_port_completion( +PUSB_DEV dev, +PVOID context +); + +extern VOID +rh_timer_svc_int_completion( +PUSB_DEV dev, +PVOID context +); + +extern USB_DEV_MANAGER g_dev_mgr; + +#ifndef INCLUDE_EHCI +ULONG debug_level = DBGLVL_MAXIMUM; +PDRIVER_OBJECT usb_driver_obj = NULL; + +//pending endpoint pool funcs +VOID +ehci_wait_ms( +PEHCI_DEV ehci, +LONG ms +) +{ + LARGE_INTEGER lms; + if( ms <= 0 ) + return; + + lms.QuadPart = -10 * ms; + KeSetTimer( &ehci->reset_timer, lms, NULL ); + + KeWaitForSingleObject( + &ehci->reset_timer, + Executive, + KernelMode, + FALSE, + NULL ); + + return; +} + +BOOL +init_pending_endp_pool( +PUHCI_PENDING_ENDP_POOL pool +) +{ + int i; + if( pool == NULL ) + return FALSE; + + pool->pending_endp_array = usb_alloc_mem( NonPagedPool, sizeof( UHCI_PENDING_ENDP ) * UHCI_MAX_PENDING_ENDPS ); + InitializeListHead( &pool->free_que ); + pool->free_count = 0; + pool->total_count = UHCI_MAX_PENDING_ENDPS; + KeInitializeSpinLock( &pool->pool_lock ); + + for( i = 0; i < MAX_TIMER_SVCS; i++ ) + { + free_pending_endp( pool, &pool->pending_endp_array[i] ); + } + + return TRUE; + +} + +BOOL +free_pending_endp( +PUHCI_PENDING_ENDP_POOL pool, +PUHCI_PENDING_ENDP pending_endp +) +{ + if( pool == NULL || pending_endp == NULL ) + { + return FALSE; + } + + RtlZeroMemory( pending_endp, sizeof( UHCI_PENDING_ENDP ) ); + InsertTailList( &pool->free_que, (PLIST_ENTRY) &pending_endp->endp_link ); + pool->free_count++; + + return TRUE; +} + +PUHCI_PENDING_ENDP +alloc_pending_endp( +PUHCI_PENDING_ENDP_POOL pool, +LONG count) +{ + PUHCI_PENDING_ENDP new; + if( pool == NULL || count != 1 ) + return NULL; + + if( pool->free_count <= 0 ) + return NULL; + + new = ( PUHCI_PENDING_ENDP )RemoveHeadList( &pool->free_que ); + pool->free_count --; + return new; +} + +BOOL +destroy_pending_endp_pool( +PUHCI_PENDING_ENDP_POOL pool +) +{ + if( pool == NULL ) + return FALSE; + + InitializeListHead( &pool->free_que ); + pool->free_count = pool->total_count = 0; + usb_free_mem( pool->pending_endp_array ); + pool->pending_endp_array = NULL; + + return TRUE; + +} +#else +#define ehci_wait_ms uhci_wait_ms +extern VOID +uhci_wait_ms( +PEHCI_DEV ehci, +LONG ms +); + +extern BOOL +init_pending_endp_pool( +PUHCI_PENDING_ENDP_POOL pool +); + +extern BOOL +free_pending_endp( +PUHCI_PENDING_ENDP_POOL pool, +PUHCI_PENDING_ENDP pending_endp +); + +extern PUHCI_PENDING_ENDP +alloc_pending_endp( +PUHCI_PENDING_ENDP_POOL pool, +LONG count); + +extern BOOL +destroy_pending_endp_pool( +PUHCI_PENDING_ENDP_POOL pool +); + +#endif + +//end of pending endpoint pool funcs + +static VOID +ehci_cancel_pending_endp_urb( +IN PVOID Parameter +) +{ + PLIST_ENTRY abort_list; + PUSB_DEV pdev; + PURB purb; + USE_IRQL; + + abort_list = ( PLIST_ENTRY )Parameter; + + if( abort_list == NULL ) + return; + + while( IsListEmpty( abort_list ) == FALSE ) + { + //these devs are protected by purb's ref-count + purb = ( PURB )RemoveHeadList( abort_list ); + pdev = purb->pdev; + // purb->status is set when they are added to abort_list + + ehci_generic_urb_completion( purb, purb->context ); + + lock_dev( pdev, FALSE ); + pdev->ref_count--; + unlock_dev( pdev, FALSE ); + } + usb_free_mem( abort_list ); + return; +} + +static BOOL +ehci_process_pending_endp( +PEHCI_DEV ehci +) +{ + PUSB_DEV pdev; + LIST_ENTRY temp_list, abort_list; + PLIST_ENTRY pthis; + PURB purb; + PUSB_ENDPOINT pendp; + BOOL can_submit; + PWORK_QUEUE_ITEM pwork_item; + PLIST_ENTRY cancel_list; + PUSB_DEV pparent; + UCHAR port_idx; + BOOL tt_needed; + UCHAR hub_addr; + PURB_HS_CONTEXT_CONTENT phcc; + USE_IRQL; + + if( ehci == NULL ) + return FALSE; + + InitializeListHead( &temp_list ); + InitializeListHead( &abort_list ); + + purb = NULL; + ehci_dbg_print( DBGLVL_MEDIUM, ("ehci_process_pending_endp(): entering..., ehci=0x%x\n", ehci ) ); + + lock_pending_endp_list( &ehci->pending_endp_list_lock ); + while( IsListEmpty( &ehci->pending_endp_list ) == FALSE ) + { + + ehci_dbg_print( DBGLVL_MAXIMUM, ( "ehci_process_pending_endp(): pending_endp_list=0x%x\n", \ + &ehci->pending_endp_list ) ); + + tt_needed = FALSE; + pthis = RemoveHeadList( &ehci->pending_endp_list ); + pendp = ( ( PUHCI_PENDING_ENDP )pthis )->pendp; + pdev = dev_from_endp( pendp ); + lock_dev( pdev, TRUE ); + + if( dev_state( pdev ) == USB_DEV_STATE_ZOMB ) + { + unlock_dev( pdev, TRUE ); + free_pending_endp( &ehci->pending_endp_pool, struct_ptr( pthis, UHCI_PENDING_ENDP, endp_link ) ); + //delegate to ehci_remove_device for remiving the purb queue on the endpoint + continue; + } + if( ( pdev->flags & USB_DEV_FLAG_HIGH_SPEED ) == 0 ) + { + // prepare split transaction + unlock_dev( pdev, TRUE ); + + // pparent won't be removed when pending_endp_list_lock is acquired. + get_parent_hs_hub( pdev, pparent, port_idx ); + + if( pparent == NULL ) + { + TRAP(); + ehci_dbg_print( DBGLVL_MEDIUM, ("ehci_process_pending_endp(): full/low speed device with no parent!!!\n" ) ); + free_pending_endp( &ehci->pending_endp_pool, struct_ptr( pthis, UHCI_PENDING_ENDP, endp_link ) ); + continue; + } + + if( hub_lock_tt( pparent, port_idx, ( UCHAR )endp_type( pendp ) ) == FALSE ) + { + lock_dev( pdev, TRUE ); + if( dev_state( pdev ) != USB_DEV_STATE_ZOMB ) + { + // reinsert the pending-endp to the list + InsertTailList( &temp_list, pthis ); + unlock_dev( pdev, TRUE ); + } + else + { + // delegate to ehci_remove_device for purb removal + unlock_dev( pdev, TRUE ); + free_pending_endp( &ehci->pending_endp_pool, struct_ptr( pthis, UHCI_PENDING_ENDP, endp_link ) ); + } + continue; + } + + // backup the hub address for future use + hub_addr = pparent->dev_addr; + + lock_dev( pdev, TRUE ); + if( dev_state( pdev ) == USB_DEV_STATE_ZOMB ) + { + unlock_dev( pdev, TRUE ); + free_pending_endp( &ehci->pending_endp_pool, struct_ptr( pthis, UHCI_PENDING_ENDP, endp_link ) ); + hub_unlock_tt( pparent, port_idx, ( UCHAR )endp_type( pendp ) ); + continue; + } + tt_needed = TRUE; + // go on processing + } + + if( endp_state( pendp ) == USB_ENDP_FLAG_STALL ) + { + while( IsListEmpty( &pendp->urb_list ) == FALSE ) + { + purb = ( PURB )RemoveHeadList( &pendp->urb_list ); + purb->status = USB_STATUS_ENDPOINT_HALTED; + InsertTailList( &abort_list, ( LIST_ENTRY* )purb ); + } + InitializeListHead( &pendp->urb_list ); + unlock_dev( pdev, TRUE ); + free_pending_endp( &ehci->pending_endp_pool, struct_ptr( pthis, UHCI_PENDING_ENDP, endp_link ) ); + if( tt_needed ) + hub_unlock_tt( pparent, port_idx, ( UCHAR )endp_type( pendp ) ); + continue; + } + + if( IsListEmpty( &pendp->urb_list ) == FALSE ) + { + purb = ( PURB )RemoveHeadList( &pendp->urb_list ); + ASSERT( purb ); + } + else + { + InitializeListHead( &pendp->urb_list ); + unlock_dev( pdev, TRUE ); + free_pending_endp( &ehci->pending_endp_pool, struct_ptr( pthis, UHCI_PENDING_ENDP, endp_link ) ); + if( tt_needed ) + hub_unlock_tt( pparent, port_idx, ( UCHAR )endp_type( pendp ) ); + continue; + } + + if( tt_needed ) + { + ( ( PURB_HS_CONTEXT_CONTENT )&purb->hs_context )->hub_addr = hub_addr; + ( ( PURB_HS_CONTEXT_CONTENT )&purb->hs_context )->port_idx = port_idx; + } + + // if can_submit is STATUS_SUCCESS, the purb is inserted into the schedule + switch( endp_type( pendp ) ) + { + case USB_ENDPOINT_XFER_BULK: + { + can_submit = ehci_internal_submit_bulk( ehci, purb ); + break; + } + case USB_ENDPOINT_XFER_CONTROL: + { + can_submit = ehci_internal_submit_ctrl( ehci, purb ); + break; + } + case USB_ENDPOINT_XFER_INT: + { + can_submit = ehci_internal_submit_int( ehci, purb ); + break; + } + case USB_ENDPOINT_XFER_ISOC: + { + can_submit = ehci_internal_submit_iso( ehci, purb ); + break; + } + } + + if( can_submit == STATUS_NO_MORE_ENTRIES ) + { + //no enough bandwidth or tds + InsertHeadList( &pendp->urb_list, ( PLIST_ENTRY )purb ); + InsertTailList( &temp_list, pthis ); + } + else + { + // otherwise error or success + free_pending_endp( + &ehci->pending_endp_pool, + struct_ptr( pthis, UHCI_PENDING_ENDP, endp_link ) ); + + if( can_submit != STATUS_SUCCESS ) + { + //abort these URBs + InsertTailList( &abort_list, ( LIST_ENTRY* )purb ); + purb->status = can_submit; + } + } + unlock_dev( pdev, TRUE ); + if( can_submit != STATUS_SUCCESS && tt_needed ) + { + hub_unlock_tt( pparent, port_idx, ( UCHAR )endp_type( pendp ) ); + } + } + + if( IsListEmpty( &temp_list ) == FALSE ) + { + //re-append them to the pending_endp_list + ListFirst( &temp_list, pthis ); + RemoveEntryList( &temp_list ); + MergeList( &ehci->pending_endp_list, pthis ); + } + unlock_pending_endp_list( &ehci->pending_endp_list_lock ); + + if( IsListEmpty( &abort_list ) == FALSE ) + { + PLIST_ENTRY pthis; + cancel_list = ( PLIST_ENTRY )usb_alloc_mem( NonPagedPool, sizeof( WORK_QUEUE_ITEM ) + sizeof( LIST_ENTRY ) ); + ASSERT( cancel_list ); + + ListFirst( &abort_list, pthis ); + RemoveEntryList( &abort_list ); + InsertTailList( pthis, cancel_list ); + + pwork_item = ( PWORK_QUEUE_ITEM )&cancel_list[ 1 ]; + + // we do not need to worry the ehci_cancel_pending_endp_urb running when the + // driver is unloading since purb-reference count will prevent the dev_mgr to + // quit till all the reference count to the dev drop to zero. + ExInitializeWorkItem( pwork_item, ehci_cancel_pending_endp_urb, ( PVOID )cancel_list ); + ExQueueWorkItem( pwork_item, DelayedWorkQueue ); + } + return TRUE; +} + +BOOL +ehci_submit_urb( +PEHCI_DEV ehci, +PUSB_DEV pdev, +PUSB_ENDPOINT pendp, +PURB purb +) +{ + int i; + PLIST_ENTRY pthis, pnext; + PUHCI_PENDING_ENDP pending_endp; + NTSTATUS status; + USE_IRQL; + + if( ehci == NULL ) + return STATUS_INVALID_PARAMETER; + + if( pdev == NULL || pendp == NULL || purb == NULL ) + { + // give a chance to those pending urb, especially for clearing hub tt + ehci_process_pending_endp( ehci ); + return STATUS_INVALID_PARAMETER; + } + + lock_pending_endp_list( &ehci->pending_endp_list_lock ); + lock_dev( pdev, TRUE ); + + if( dev_state( pdev ) == USB_DEV_STATE_ZOMB ) + { + status = purb->status = STATUS_DEVICE_DOES_NOT_EXIST; + goto LBL_OUT; + } + + if( dev_class( pdev ) == USB_DEV_CLASS_ROOT_HUB ) + { + unlock_dev( pdev, TRUE ); + unlock_pending_endp_list( &ehci->pending_endp_list_lock ); + status = ehci_rh_submit_urb( pdev, purb ); + return status; + } + + if( pendp ) + purb->pendp = pendp; + else + purb->pendp = &pdev->default_endp; + + if( dev_from_endp( purb->pendp ) != pdev ) + { + status = purb->status = STATUS_INVALID_PARAMETER; + goto LBL_OUT; + } + + if( endp_state( purb->pendp ) == USB_ENDP_FLAG_STALL ) + { + status = purb->status = USB_STATUS_ENDPOINT_HALTED; + goto LBL_OUT; + } + + if( ( pdev->flags & USB_DEV_FLAG_HIGH_SPEED ) == 0 ) + { + // wait one ms + usb_wait_ms_dpc( 1 ); + } + + purb->pdev = pdev; + purb->rest_bytes = purb->data_length; + + if( endp_type( purb->pendp ) == USB_ENDPOINT_XFER_BULK ) + purb->bytes_to_transfer = + ( purb->data_length > EHCI_MAX_SIZE_TRANSFER ? + EHCI_MAX_SIZE_TRANSFER : purb->data_length ); //multiple transfer for large data block + else + purb->bytes_to_transfer = purb->data_length; + + ehci_dbg_print( DBGLVL_MEDIUM, ( "ehci_submit_urb(): bytes_to_transfer=0x%x\n", purb->bytes_to_transfer ) ); + + purb->bytes_transfered = 0; + InitializeListHead( &purb->trasac_list ); + purb->last_finished_td = &purb->trasac_list; + purb->flags &= ~( URB_FLAG_STATE_MASK | URB_FLAG_IN_SCHEDULE | URB_FLAG_FORCE_CANCEL ); + purb->flags |= URB_FLAG_STATE_PENDING; + + + i = IsListEmpty( &pendp->urb_list ); + InsertTailList( &pendp->urb_list, &purb->urb_link ); + + pdev->ref_count ++; //for purb reference + + if( i == FALSE ) + { + //there is purb pending, simply queue it and return + status = purb->status = STATUS_PENDING; + goto LBL_OUT; + } + else if( usb_endp_busy_count( purb->pendp ) + && endp_type( purb->pendp) != USB_ENDPOINT_XFER_ISOC ) + { + // + //No purb waiting but purb overlap not allowed, + //so leave it in queue and return, will be scheduled + //later + // + status = purb->status = STATUS_PENDING; + goto LBL_OUT; + } + + pending_endp = alloc_pending_endp( &ehci->pending_endp_pool, 1 ); + if( pending_endp == NULL ) + { + //panic + status = purb->status = STATUS_UNSUCCESSFUL; + goto LBL_OUT2; + } + + pending_endp->pendp = purb->pendp; + InsertTailList( &ehci->pending_endp_list, ( PLIST_ENTRY )pending_endp ); + + unlock_dev( pdev, TRUE ); + unlock_pending_endp_list( &ehci->pending_endp_list_lock ); + + ehci_process_pending_endp( ehci ); + return STATUS_PENDING; + +LBL_OUT2: + pdev->ref_count --; + RemoveEntryList( ( PLIST_ENTRY )purb ); + +LBL_OUT: + unlock_dev( pdev, TRUE ); + unlock_pending_endp_list( &ehci->pending_endp_list_lock ); + ehci_process_pending_endp( ehci ); + return status; +} + +static NTSTATUS +ehci_set_error_code( +PURB purb, +ULONG raw_status +) +{ + PURB_HS_PIPE_CONTENT pipe_content; + + pipe_content = ( PURB_HS_PIPE_CONTENT )&purb->pipe; + + //test if the purb is canceled + if( purb->flags & URB_FLAG_FORCE_CANCEL ) + { + purb->status = STATUS_CANCELLED; + } + else if( raw_status == 0 ) + purb->status = STATUS_SUCCESS; + + else if( pipe_content->trans_type == USB_ENDPOINT_XFER_INT || + pipe_content->trans_type == USB_ENDPOINT_XFER_BULK || + pipe_content->trans_type == USB_ENDPOINT_XFER_CONTROL ) + { + + if( raw_status & QTD_STS_BABBLE ) + purb->status = USB_STATUS_DATA_OVERRUN; + + else if( raw_status & QTD_STS_HALT ) + purb->status = USB_STATUS_ENDPOINT_HALTED; + + else if( raw_status & QTD_STS_DBE ) + purb->status = USB_STATUS_BUFFER_OVERRUN; + + else if( raw_status & QTD_STS_XACT ) + purb->status = USB_STATUS_CRC; // crc is included in xact err. + + else if( raw_status & QTD_STS_MMF ) + purb->status = USB_STATUS_BTSTUFF; + + else + purb->status = STATUS_UNSUCCESSFUL; + } + else if( pipe_content->trans_type == USB_ENDPOINT_XFER_ISOC ) + { + if( pipe_content->speed_high ) + { + if( raw_status & ITD_STS_BUFERR ) + purb->status = USB_STATUS_BUFFER_OVERRUN; + + else if( raw_status & ITD_STS_BABBLE ) + purb->status = USB_STATUS_BABBLE_DETECTED; + + else if( raw_status & ITD_STS_XACTERR ) // Xact Err + purb->status = USB_STATUS_CRC; + + else + purb->status = STATUS_UNSUCCESSFUL; + + } + else + { + if( raw_status & SITD_STS_ERR ) // ERR is received from hub's tt + purb->status = USB_STATUS_ERROR; + + else if( raw_status & SITD_STS_DBE ) + purb->status = USB_STATUS_BUFFER_OVERRUN; + + else if( raw_status & SITD_STS_BABBLE ) + purb->status = USB_STATUS_BABBLE_DETECTED; + + else if( raw_status & SITD_STS_XACTERR ) // Xact Error + purb->status = USB_STATUS_CRC; + + else if( raw_status & SITD_STS_MISSFRM ) // missing microframe + purb->status = USB_STATUS_DATA_TOGGLE_MISMATCH; + + else + purb->status = STATUS_UNSUCCESSFUL; + } + } + if( purb->status != STATUS_SUCCESS ) + { + hcd_dbg_print( DBGLVL_MEDIUM, ( "ehci_set_error_code(): error status 0x%x\n", raw_status ) ); + } + return purb->status; +} + +BOOLEAN +ehci_sync_remove_urb_finished( +PVOID context +) +{ + PEHCI_DEV ehci; + PLIST_ENTRY pthis, pnext, ptemp; + PURB purb; + PSYNC_PARAM pparam; + + pparam = ( PSYNC_PARAM )context; + ehci = pparam->ehci; + ptemp = ( PLIST_ENTRY )pparam->context; + + if( ehci == NULL ) + { + return ( UCHAR )pparam->ret = FALSE; + } + + ListFirst( &ehci->urb_list, pthis ); + while( pthis ) + { + //remove urbs not in the schedule + ListNext( &ehci->urb_list, pthis, pnext ); + purb = ( PURB )pthis; + + if( ( purb->flags & URB_FLAG_IN_SCHEDULE ) == 0 ) + { + //finished or canceled( not applied for split bulk ). + RemoveEntryList( pthis ); + InsertTailList( ptemp, pthis ); + } + pthis = pnext; + } + pparam->ret = TRUE; + return ( UCHAR )TRUE; +} + +VOID +ehci_dpc_callback( +PKDPC dpc, +PVOID context, +PVOID sysarg1, +PVOID sysarg2 +) +{ + PEHCI_DEV ehci; + + LIST_HEAD temp_list; + PLIST_ENTRY pthis, pnext; + PURB purb; + PEHCI_QH pqh; + PEHCI_QTD ptd; + PUHCI_PENDING_ENDP pending_endp; + PEHCI_FSTN pfstn; + PUSB_DEV pdev; + PUSB_ENDPOINT pendp; + + BOOL finished; + LONG i; + ULONG ehci_status, urb_status, toggle = 0; + + SYNC_PARAM sync_param; + UCHAR ep_type; + USE_IRQL; + + ehci = ( PEHCI_DEV ) context; + if( ehci == NULL ) + return; + + ehci_status = ( ULONG )sysarg1; + + InitializeListHead( &temp_list ); + + sync_param.ehci = ehci; + sync_param.context = ( PVOID )&temp_list; + + ehci_dbg_print( DBGLVL_MAXIMUM, ( "ehci_dpc_callback(): entering..., ehci=0x%x\n", ehci ) ); + //remove finished purb from ehci's purb-list + KeSynchronizeExecution( ehci->pdev_ext->ehci_int, ehci_sync_remove_urb_finished, &sync_param ); + + //release resources( itds, sitds, fstns, tds, and qhs ) allocated for the purb + while( IsListEmpty( &temp_list ) == FALSE ) + { + //not in any public queue, if do not access into dev, no race + //condition will occur + purb = ( PURB ) RemoveHeadList( &temp_list ); + urb_status = purb->status; + ep_type = endp_type( purb->pendp ); + + if( ep_type == USB_ENDPOINT_XFER_ISOC ) + { + // collect error for iso transfer + urb_status = ehci_scan_iso_error( ehci, purb ); + } + + //the only place we do not use this lock on non-pending-endp-list data + KeAcquireSpinLockAtDpcLevel( &ehci->pending_endp_list_lock ); + while( IsListEmpty( &purb->trasac_list ) == FALSE ) + { + UCHAR em_type; + pthis = RemoveHeadList( &purb->trasac_list ); + em_type = ( UCHAR )elem_type( pthis, TRUE ); + + if( em_type == INIT_LIST_FLAG_QH ) + { + pqh = qh_from_list_entry( pthis ); + elem_safe_free( pthis, TRUE ); + } + else + { + //must be an itd, sitd chain + InsertHeadList( &purb->trasac_list, pthis ); + for( i = 0, purb->bytes_transfered = 0; i < purb->td_count; i++ ) + { + PEHCI_QTD_CONTENT ptdc; + PEHCI_ITD_CONTENT pitdc; + PEHCI_SITD_CONTENT psitdc; + + em_type = ( UCHAR )elem_type( pthis, TRUE ); + + // accumulate data transfered in tds + if( em_type == INIT_LIST_FLAG_QTD ) + { + ptd = qtd_from_list_entry( pthis ); + ptdc = ( PEHCI_QTD_CONTENT )ptd; + if( ( ptdc->status & QTD_STS_ACTIVE ) == 0 && ( ( ptdc->status & QTD_ANY_ERROR ) == 0 ) ) + purb->bytes_transfered += ptd->bytes_to_transfer; + } + else if( em_type == INIT_LIST_FLAG_ITD ) + { + int j; + pitdc = ( PEHCI_ITD_CONTENT )itd_from_list_entry( pthis ); + for( j = 0; j < 8; j++ ) + { + if( ( pitdc->status_slot[ j ].status & ITD_STS_ACTIVE ) == 0 && ( pitdc->status_slot[ j ].status & ITD_ANY_ERROR ) == 0 ) + purb->bytes_transfered += ptdc->bytes_to_transfer; + } + } + else if( em_type == INIT_LIST_FLAG_SITD ) + { + psitdc = ( PEHCI_SITD_CONTENT )sitd_from_list_entry( pthis ); + if( ( psitdc->status & SITD_STS_ACTIVE ) == 0 && ( psitdc->status & SITD_ANY_ERROR ) == 0 ) + purb->bytes_transfered += ptdc->bytes_to_transfer; + } + ListNext( &purb->trasac_list, pthis, pnext ); + pthis = pnext; + } + + // check to see if an fstn is there + ListFirstPrev( &purb->trasac_list, pthis ); + if( elem_type( pthis, TRUE ) == INIT_LIST_FLAG_FSTN ) + { + RemoveEntryList( pthis ); + elem_safe_free( pthis, TRUE ); + } + + ListFirst( &purb->trasac_list, pthis ); + RemoveEntryList( &purb->trasac_list ); + + // free the tds + elem_safe_free( pthis, FALSE ); + + //termination condition + InitializeListHead( &purb->trasac_list ); + purb->last_finished_td = NULL; + } + } + + if( ep_type == USB_ENDPOINT_XFER_ISOC + || ep_type == USB_ENDPOINT_XFER_INT ) + ehci_claim_bandwidth( ehci, purb, FALSE); //release band-width + + KeReleaseSpinLockFromDpcLevel( &ehci->pending_endp_list_lock ); + + ehci_set_error_code( purb, urb_status ); + + pdev = dev_from_endp( purb->pendp ); + pendp = purb->pendp; + + // perform clear tt buffer if error on full/low bulk/control pipe + if( ep_type == USB_ENDPOINT_XFER_BULK || ep_type == USB_ENDPOINT_XFER_CONTROL ) + { + PURB_HS_PIPE_CONTENT pipe_content; + PUSB_DEV phub; + UCHAR port_idx; + + get_parent_hs_hub( pdev, phub, port_idx ); + pipe_content = ( PURB_HS_PIPE_CONTENT )&purb->pipe; + + if( pipe_content->speed_high == 0 && purb->status != STATUS_SUCCESS ) + { + // lets schedule an event to clear the tt buffer + hub_post_clear_tt_event( phub, port_idx, purb->pipe ); + } + else if( pipe_content->speed_high == 0 ) + { + if( phub == NULL ) + TRAP(); + else + { + // release tt if no error + hub_unlock_tt( phub, ( UCHAR )port_idx, ( UCHAR )pipe_content->trans_type ); + } + } + } + + finished = TRUE; + + //since the ref_count for the purb is not released, we can safely have one + //pointer to dev + + if( purb->status == USB_STATUS_BABBLE_DETECTED ) + { + usb_dbg_print( DBGLVL_MEDIUM, ( "ehci_dpc_callback(): alert!!!, babble detected, severe error, reset the whole bus\n" ) ); + // ehci_start( ehci ); + } + + if ( ehci_status & STS_HALT ) //&& !ehci->is_suspended + { + ehci_start( &ehci->hcd_interf ); + } + + //this will let the new request in ehci_generic_urb_completion to this endp + //be processed rather than queued in the pending_endp_list + lock_dev( pdev, TRUE ); + usb_endp_busy_count_dec( pendp ); + unlock_dev( pdev, TRUE ); + + if( usb_success( purb->status ) == FALSE ) + { + // set error code and complete the purb and purb is invalid from this point + ehci_generic_urb_completion( purb, purb->context ); + } + else + { + if( ep_type == USB_ENDPOINT_XFER_BULK ) + { + purb->rest_bytes -= purb->bytes_transfered; + if( purb->rest_bytes ) + { + finished = FALSE; + } + else + { + ehci_generic_urb_completion( purb, purb->context ); + } + } + else + { + ehci_generic_urb_completion( purb, purb->context ); + // DbgBreakPoint(); + //purb is now invalid + } + } + + KeAcquireSpinLockAtDpcLevel( &ehci->pending_endp_list_lock ); + lock_dev( pdev, TRUE ); + + if( finished ) + pdev->ref_count--; + + if( urb_status && + ( ( ep_type == USB_ENDPOINT_XFER_BULK ) || + ( ep_type == USB_ENDPOINT_XFER_INT ) ) ) + { + // error on int or bulk pipe, cleared in usb_reset_pipe_completion + pendp->flags &= ~USB_ENDP_FLAG_STAT_MASK; + pendp->flags |= USB_ENDP_FLAG_STALL; + } + + if( dev_state( pdev )== USB_DEV_STATE_ZOMB ) + { + unlock_dev( pdev, TRUE); + KeReleaseSpinLockFromDpcLevel( &ehci->pending_endp_list_lock ); + if( finished == FALSE ) + { + + purb->status = STATUS_DEVICE_DOES_NOT_EXIST; + ehci_generic_urb_completion( purb, purb->context ); + + lock_dev( pdev, TRUE ); + pdev->ref_count--; + unlock_dev( pdev, TRUE); + } + continue; + } + + if( finished && IsListEmpty( &pendp->urb_list ) == TRUE ) + { + unlock_dev( pdev, TRUE); + KeReleaseSpinLockFromDpcLevel( &ehci->pending_endp_list_lock ); + continue; + } + else if( finished == TRUE ) + { + //has purb in the endp's purb-list + if( usb_endp_busy_count( pendp ) > 0 ) + { + //the urbs still have chance to be sheduled but not this time + unlock_dev( pdev, TRUE); + KeReleaseSpinLockFromDpcLevel( &ehci->pending_endp_list_lock ); + continue; + } + } + + if( finished == FALSE ) + { + //a split bulk transfer, ( not the high speed split transfer ) + purb->bytes_transfered = 0; + purb->bytes_to_transfer = + EHCI_MAX_SIZE_TRANSFER > purb->rest_bytes + ? purb->rest_bytes + : EHCI_MAX_SIZE_TRANSFER; + + //the purb is not finished + purb->flags &= ~URB_FLAG_STATE_MASK; + purb->flags |= URB_FLAG_STATE_PENDING; + + InsertHeadList( &pendp->urb_list, ( PLIST_ENTRY )purb ); + } + + pending_endp = alloc_pending_endp( &ehci->pending_endp_pool, 1 ); + pending_endp->pendp = pendp; + InsertTailList( &ehci->pending_endp_list, &pending_endp->endp_link ); + + unlock_dev( pdev, TRUE); + KeReleaseSpinLockFromDpcLevel( &ehci->pending_endp_list_lock ); + } + + //ah...exhausted, let's find some in the pending_endp_list to rock + ehci_process_pending_endp( ehci ); + return; +} + +BOOLEAN +ehci_sync_cancel_urbs_dev( +PVOID context +) +{ + //cancel all the urbs on one dev + PEHCI_DEV ehci; + PUSB_DEV pdev, dest_dev; + PSYNC_PARAM sync_param; + PLIST_ENTRY pthis, pnext; + LONG count; + + sync_param = ( PSYNC_PARAM )context; + dest_dev = ( PUSB_DEV )sync_param->context; + ehci = sync_param->ehci; + + if( ehci == NULL || dest_dev == NULL ) + { + return ( UCHAR )sync_param->ret = FALSE; + } + count = 0; + ListFirst( &ehci->urb_list, pthis ); + while( pthis ) + { + pdev = dev_from_endp( ( ( PURB ) pthis )->pendp ); + if( pdev == dest_dev ) + { + ( ( PURB ) pthis )->flags |= URB_FLAG_FORCE_CANCEL; + } + ListNext( &ehci->urb_list, pthis, pnext ); + pthis = pnext; + count ++; + } + + if( count ) + { + // signal an int for further process + press_doorbell( ehci ); + } + return ( UCHAR )sync_param->ret = TRUE; +} + +BOOL +ehci_remove_device( +PEHCI_DEV ehci, +PUSB_DEV dev +) +{ + PUHCI_PENDING_ENDP ppending_endp; + PLIST_ENTRY pthis, pnext; + PURB purb; + LIST_HEAD temp_list; + int i, j, k; + SYNC_PARAM sync_param; + + USE_IRQL; + + if( ehci == NULL || dev == NULL ) + return FALSE; + + InitializeListHead( &temp_list ); + + //free pending endp that has purb queued from pending endp list + lock_pending_endp_list( &ehci->pending_endp_list_lock ); + + ListFirst( &ehci->pending_endp_list, pthis ); + + while( pthis ) + { + ppending_endp = ( PUHCI_PENDING_ENDP ) pthis; + ListNext( &ehci->pending_endp_list, pthis, pnext ); + if( dev_from_endp( ppending_endp->pendp ) == dev ) + { + RemoveEntryList( pthis ); + free_pending_endp( &ehci->pending_endp_pool, struct_ptr( pthis, UHCI_PENDING_ENDP, endp_link ) ); + } + pthis = pnext; + } + unlock_pending_endp_list( &ehci->pending_endp_list_lock ); + + //cancel all the urbs in the purb-list + sync_param.ehci = ehci; + sync_param.context = ( PVOID )dev; + + KeSynchronizeExecution( ehci->pdev_ext->ehci_int, ehci_sync_cancel_urbs_dev, &sync_param ); + + //cancel all the purb in the endp's purb-list + k = 0; + lock_dev( dev, FALSE ); + if( dev->usb_config ) + { + //only for configed dev + for( i = 0; i < dev->usb_config->if_count; i++ ) + { + for( j = 0; j < dev->usb_config->interf[ i ].endp_count; j++ ) + { + ListFirst( &dev->usb_config->interf[ i ].endp[ j ].urb_list, pthis ); + while( pthis ) + { + ListNext( &dev->usb_config->interf[ i ].endp[ j ].urb_list, + pthis, + pnext ); + + RemoveEntryList( pthis ); + InsertHeadList( &temp_list, pthis ); + pthis = pnext; + k++; + } + + } + } + } + ListFirst( &dev->default_endp.urb_list, pthis ); + + while( pthis ) + { + ListNext( &dev->default_endp.urb_list, + pthis, + pnext ); + + RemoveEntryList( pthis ); + InsertHeadList( &temp_list, pthis ); + pthis = pnext; + k++; + } + unlock_dev( dev, FALSE ); + + if( IsListEmpty( &temp_list ) == FALSE ) + { + for( i = 0; i < k ; i++ ) + { + //complete those urbs with error + pthis = RemoveHeadList( &temp_list ); + purb = ( PURB ) pthis; + purb->status = STATUS_DEVICE_DOES_NOT_EXIST; + { + ehci_generic_urb_completion( purb, purb->context ); + } + } + } + + lock_dev( dev, FALSE ) + dev->ref_count -= k; + unlock_dev( dev, FALSE ); + + return TRUE; +} + +static BOOL +ehci_insert_urb_schedule( +PEHCI_DEV ehci, +PURB purb +) +// must have dev_lock( ehci_process_pending_endp ) and frame_list_lock acquired +{ + PURB_HS_PIPE_CONTENT pipe_content; + + if( ehci == NULL || purb == NULL ) + return FALSE; + + pipe_content = ( PURB_HS_PIPE_CONTENT )&purb->pipe; + switch( pipe_content->trans_type ) + { + case USB_ENDPOINT_XFER_CONTROL: + ehci_insert_control_schedule( ehci, purb ); + break; + case USB_ENDPOINT_XFER_BULK: + ehci_insert_bulk_schedule( ehci, purb ); + break; + case USB_ENDPOINT_XFER_INT: + ehci_insert_int_schedule( ehci, purb ); + break; + case USB_ENDPOINT_XFER_ISOC: + ehci_insert_iso_schedule( ehci, purb ); + break; + default: + return FALSE; + } + + purb->flags &= ~URB_FLAG_STATE_MASK; + purb->flags |= URB_FLAG_STATE_IN_PROCESS | URB_FLAG_IN_SCHEDULE; + InsertTailList( &ehci->urb_list, ( PLIST_ENTRY )purb ); + + return TRUE; +} + +static BOOL +ehci_insert_tds_qh( +PEHCI_DEV ehci, +PEHCI_QH pqh, +PEHCI_QTD td_chain +) +{ + if( pqh == NULL || td_chain == NULL ) + return FALSE; + + ehci_copy_overlay( ( PEHCI_QH_CONTENT )pqh, ( PEHCI_QTD_CONTENT )td_chain ); + InsertTailList( &td_chain->elem_head_link->elem_link, &pqh->elem_head_link->elem_link ); + return TRUE; + ehci; +} + +static BOOL +ehci_insert_qh_urb( +PURB purb, +PEHCI_QH pqh +) +{ + PLIST_ENTRY pthis, pnext; + if( pqh == NULL || purb == NULL ) + return FALSE; + + InsertTailList( &pqh->elem_head_link->elem_link, &purb->trasac_list ); + ListFirst( &purb->trasac_list, pthis ) + while( pthis ) + { + // note: fstn may in this chain + struct_ptr( pthis, EHCI_ELEM_LINKS, elem_link )->purb = purb; + ListNext( &purb->trasac_list, pthis, pnext ); + pthis = pnext; + } + return TRUE; +} + +#define calc_td_count( pURB, start_aDDR, td_coUNT ) \ +{\ + LONG i, j, k;\ + td_coUNT = 0;\ + k = ( ( pURB )->bytes_to_transfer + max_packet_size - 1 ) / max_packet_size;\ + if( k != 0 )\ + {\ + LONG packets_per_td, packets_per_page;\ + packets_per_td = EHCI_QTD_MAX_TRANS_SIZE / max_packet_size;\ + packets_per_page = PAGE_SIZE / max_packet_size;\ + i = ( ( LONG )&( pURB )->data_buffer[ ( start_aDDR ) ] ) & ( PAGE_SIZE - 1 );\ + if( i )\ + {\ + i = PAGE_SIZE - i;\ + j = i & ( max_packet_size - 1 );\ + k -= ( EHCI_QTD_MAX_TRANS_SIZE - PAGE_SIZE + i - j ) / max_packet_size;\ + if( k < 0 )\ + td_coUNT = 1;\ + else\ + {\ + if( j )\ + i = packets_per_td - packets_per_page;\ + else\ + i = packets_per_td;\ + td_coUNT = 1 + ( k + i - 1 ) / i; \ + }\ + }\ + else\ + {\ + td_coUNT = ( k + packets_per_td - 1 ) / packets_per_td;\ + }\ + }\ +} + +static BOOL +ehci_fill_td_buf_ptr( +PURB purb, +LONG start_addr, // start idx into purb->data_buffer +PLIST_ENTRY td_list, +LONG td_count, +ULONG toggle +) +// fill the tds' bytes_to_transfer and hw_buf, return next toggle value: true 1, false 0 +{ + LONG i, j, k, data_load; + LONG packets_per_td, packets_per_page, bytes_to_transfer, max_packet_size; + PLIST_ENTRY pthis, pnext; + PEHCI_QTD_CONTENT ptdc; + PEHCI_QTD ptd; + PVOID ptr; + + if( purb == NULL || td_list == NULL || td_count == 0 ) + return toggle; + + max_packet_size = 1 << ( ( PURB_HS_PIPE_CONTENT )&purb->pipe )->max_packet_size; + packets_per_td = EHCI_QTD_MAX_TRANS_SIZE / max_packet_size; + packets_per_page = PAGE_SIZE / max_packet_size; + + pthis = td_list; + bytes_to_transfer = purb->bytes_to_transfer; + + i = ( ( LONG )&( purb )->data_buffer[ ( start_addr ) ] ) & ( PAGE_SIZE - 1 ); + if( i ) + { + i = PAGE_SIZE - i; + j = i & ( max_packet_size - 1 ); + } + else + { + i = j = 0; + } + + while( bytes_to_transfer ) + { + ptd = qtd_from_list_entry( pthis ); + ptd->hw_buf[ 0 ] = MmGetPhysicalAddress( &purb->data_buffer[ start_addr ] ).LowPart; + ptdc = ( PEHCI_QTD_CONTENT )ptd; + + if( i != 0 ) + { + data_load = ( LONG )( EHCI_QTD_MAX_TRANS_SIZE - PAGE_SIZE + i - j ) < bytes_to_transfer + ? ( LONG )( EHCI_QTD_MAX_TRANS_SIZE - PAGE_SIZE + i - j ) : bytes_to_transfer; + + ptdc->bytes_to_transfer = ( USHORT )data_load; + ptd->bytes_to_transfer = ( USHORT )data_load; + + // subtract the header part + data_load -= ( i < data_load ? i : data_load ); + + for( k = 1; data_load > 0; k++ ) + { + ptr = &purb->data_buffer[ start_addr + i + ( k - 1 ) * PAGE_SIZE ]; + ptr = ( PVOID )( ( ( ULONG )ptr ) & ~( PAGE_SIZE - 1 ) ); + ptd->hw_buf[ k ] = MmGetPhysicalAddress( ptr ).LowPart; + data_load -= PAGE_SIZE < data_load ? PAGE_SIZE : data_load; + } + } + else + { + // aligned on page boundary + data_load = EHCI_QTD_MAX_TRANS_SIZE < bytes_to_transfer + ? EHCI_QTD_MAX_TRANS_SIZE : bytes_to_transfer; + + ptdc->bytes_to_transfer = ( USHORT )data_load; + ptd->bytes_to_transfer = ( USHORT )data_load; + + data_load -= ( PAGE_SIZE < data_load ? PAGE_SIZE : data_load ); + + for( k = 1; data_load > 0; k++ ) + { + ptr = &purb->data_buffer[ start_addr + k * PAGE_SIZE ]; + ptr = ( PVOID )( ( ( ULONG )ptr ) & ~( PAGE_SIZE - 1 ) ); + ptd->hw_buf[ k ] = MmGetPhysicalAddress( ptr ).LowPart; + data_load -= PAGE_SIZE < data_load ? PAGE_SIZE : data_load; + } + } + ptdc->data_toggle = toggle; + if( ( ( ptdc->bytes_to_transfer + max_packet_size - 1 ) / max_packet_size ) & 1 ) + { + //only odd num of transactions has effect + toggle ^= 1; + } + start_addr += ptdc->bytes_to_transfer; + bytes_to_transfer -= ptdc->bytes_to_transfer; + ListNext( td_list, pthis, pnext ); + pthis = pnext; + i = j; + } + return toggle; +} + +static NTSTATUS +ehci_internal_submit_bulk( +PEHCI_DEV ehci, +PURB purb +) +// +// assume that the purb has its rest_bytes and bytes_to_transfer set +// and bytes_transfered is zeroed. +// dev_lock must be acquired outside +// purb comes from dev's endpoint purb-list. it is already removed from +// the endpoint purb-list. +// +{ + + LONG max_packet_size, td_count, offset, bytes_to_transfer, data_load; + PBYTE start_addr; + PEHCI_QTD ptd; + PEHCI_QH pqh; + LIST_ENTRY td_list, *pthis, *pnext; + BOOL old_toggle, toggle, ret; + UCHAR pid; + LONG i, j, k; + PURB_HS_PIPE_CONTENT pipe_content; + PEHCI_QTD_CONTENT ptdc; + PEHCI_QH_CONTENT pqhc; + PEHCI_ELEM_LINKS pelnk; + + if( ehci == NULL || purb == NULL ) + return STATUS_INVALID_PARAMETER; + + max_packet_size = endp_max_packet_size( purb->pendp ); + if( purb->bytes_to_transfer == 0 ) + { + return STATUS_INVALID_PARAMETER; + } + + start_addr = &purb->data_buffer[ purb->data_length - purb->rest_bytes ]; + calc_td_count( purb, purb->data_length - purb->rest_bytes, td_count ); + + elem_pool_lock( qtd_pool , TRUE ); + pelnk = elem_pool_alloc_elems( qtd_pool, td_count ); + elem_pool_unlock( qtd_pool, TRUE ); + + if( pelnk == NULL ) + { + return STATUS_UNSUCCESSFUL; + } + ptd = ( PEHCI_QTD )( ( ULONG )pelnk->phys_part & PHYS_PART_ADDR_MASK ); + + InitializeListHead( &td_list ); + InsertTailList( &ptd->elem_head_link->elem_link, &td_list ); + + ListFirst( &td_list, pthis ); + ListNext( &td_list, pthis, pnext ); + + offset = 0; + + old_toggle = toggle = ( purb->pendp->flags & USB_ENDP_FLAG_DATATOGGLE ) ? TRUE : FALSE; + bytes_to_transfer = purb->bytes_to_transfer; + ehci_dbg_print( DBGLVL_MAXIMUM, ("ehci_internal_submit_bulk():dev toggle=%d\n", toggle ) ); + + for( i = 1; i < 16; i++ ) + { + if( ( max_packet_size >> i ) == 0 ) + break; + } + i--; + i &= 0xf; + + purb->pipe = 0; + pipe_content = ( PURB_HS_PIPE_CONTENT )&purb->pipe; + pipe_content->max_packet_size = i; + pipe_content->endp_addr = endp_num( purb->pendp ); + pipe_content->dev_addr = dev_from_endp( purb->pendp )->dev_addr; + pipe_content->trans_dir = endp_dir( purb->pendp ); + pipe_content->trans_type = USB_ENDPOINT_XFER_BULK; + pipe_content->data_toggle = toggle; + pipe_content->speed_high = ( dev_from_endp( purb->pendp )->flags & USB_DEV_FLAG_HIGH_SPEED ) ? 1 : 0; + pipe_content->speed_low = ( dev_from_endp( purb->pendp )->flags & USB_DEV_FLAG_LOW_SPEED ) ? 1 : 0; + + pid = ( ( ( ULONG )purb->pendp->pusb_endp_desc->bEndpointAddress & USB_DIR_IN ) ? QTD_PID_IN : QTD_PID_OUT ); + + i = ( ( ULONG )start_addr ) & ( PAGE_SIZE - 1 ) ; // header part within first page + if( i ) + { + i = PAGE_SIZE - i; + if( i < purb->bytes_to_transfer ) + j = i & ( max_packet_size - 1 ); + else + j = 0; + } + else + j = 0; + + // fill the page pointer and toggle + + toggle = ehci_fill_td_buf_ptr( purb, purb->data_length - purb->rest_bytes, pthis, td_count, toggle ); + while( pthis ) + { + ptd = qtd_from_list_entry( pthis ); + ptdc = ( PEHCI_QTD_CONTENT ) ptd; + + // ptdc->alt_terminal = 1; + // ptdc->alt_qtd = 0; + ptd->hw_alt_next = EHCI_PTR_TERM; + ptdc->pid = pid; + + // ptd->elem_head_link->purb = purb; will be filled later + ptdc->err_count = 3; + ptdc->status = 0x80; // active, and do_start_split for split transfer + ptdc->cur_page = 0; + // ptdc->data_toggle = toggle; + + if( pnext ) + { + ptd->hw_next = qtd_from_list_entry( pnext )->phys_addr; + } + else + { + //Last one, enable ioc and short packet detect if necessary + ptd->hw_next = EHCI_PTR_TERM; + ptdc->ioc = TRUE; + if( bytes_to_transfer < max_packet_size && ( pid == QTD_PID_IN ) ) + { + //ptd->status |= TD_CTRL_SPD; + } + } + + pthis = pnext; + + if( pthis ) + ListNext( &td_list, pthis, pnext ); + } + + ListFirst( &td_list, pthis ); + RemoveEntryList( &td_list ); + + elem_pool_lock( qh_pool, TRUE ); + pqh = ( PEHCI_QH )( ( ULONG )elem_pool_alloc_elem( qh_pool )->phys_part & PHYS_PART_ADDR_MASK ); + elem_pool_unlock( qh_pool, TRUE); + + if( pqh == NULL ) + { + // free the qtds + elem_safe_free( pthis, TRUE ); + return STATUS_NO_MORE_ENTRIES; + + } + + purb->td_count = td_count; + pqhc = ( PEHCI_QH_CONTENT )pqh; + pqh->hw_next = EHCI_PTR_TERM; // filled later + pqhc->dev_addr = pipe_content->dev_addr; + pqhc->inactive = 0; + pqhc->endp_addr = pipe_content->endp_addr; + pqhc->data_toggle = 0; //pipe_content->data_toggle; + pqhc->is_async_head = 0; + pqhc->max_packet_size = ( 1 << pipe_content->max_packet_size ); + pqhc->is_ctrl_endp = 0; + pqhc->reload_counter = EHCI_NAK_RL_COUNT; + + if( pipe_content->speed_high ) + pqhc->endp_spd = USB_SPEED_HIGH; + else if( pipe_content->speed_low ) + pqhc->endp_spd = USB_SPEED_LOW; + else + pqhc->endp_spd = USB_SPEED_FULL; + + pqh->hw_info2 = 0; + pqhc->mult = 1; + pqh->hw_current = 0; + pqh->hw_qtd_next = 0; // filled later + pqh->hw_alt_next = EHCI_PTR_TERM; + pqh->hw_token = 0; //indicate to advance queue before execution + + if( !pipe_content->speed_high ) + { + pqhc->hub_addr = ( ( PURB_HS_CONTEXT_CONTENT )&purb->hs_context )->hub_addr; + pqhc->port_idx = ( ( PURB_HS_CONTEXT_CONTENT )&purb->hs_context )->port_idx; + } + + ptd = qtd_from_list_entry( pthis ); + ehci_insert_tds_qh( ehci, pqh, ptd ); + ehci_insert_qh_urb( purb, pqh ); + purb->pendp->flags = ( purb->pendp->flags & ~USB_ENDP_FLAG_DATATOGGLE ) | ( toggle ? USB_ENDP_FLAG_DATATOGGLE : 0 ) ; + usb_endp_busy_count_inc( purb->pendp ); + ehci_insert_urb_to_schedule( ehci, purb, ret ); + + if( ret == FALSE ) + { + // undo all we have done + ListFirst( &pqh->elem_head_link->elem_link, pthis ); + + RemoveEntryList( &purb->trasac_list ); + RemoveEntryList( &pqh->elem_head_link->elem_link ); //remove qh from td_chain + + elem_safe_free( pthis, FALSE ); + elem_safe_free( &pqh->elem_head_link->elem_link, TRUE ); + + InitializeListHead( &purb->trasac_list ); + // usb_endp_busy_count_dec( purb->pendp ); // the decrement is done in the dpc callback + purb->pendp->flags = ( purb->pendp->flags & ~USB_ENDP_FLAG_DATATOGGLE ) | ( old_toggle ? USB_ENDP_FLAG_DATATOGGLE : 0 ) ; + return STATUS_UNSUCCESSFUL; + } + return STATUS_SUCCESS; +} + +static NTSTATUS +ehci_internal_submit_ctrl( +PEHCI_DEV ehci, +PURB purb +) +{ + + LIST_ENTRY td_list, *pthis, *pnext; + LONG i, j, td_count; + LONG toggle; + LONG max_packet_size, bytes_to_transfer, bytes_rest, start_idx; + + PEHCI_QTD ptd; + PEHCI_QH pqh; + PEHCI_QH_CONTENT pqhc; + UCHAR dev_addr; + BOOL ret; + PURB_HS_PIPE_CONTENT pipe_content; + PEHCI_QTD_CONTENT ptdc; + PEHCI_ELEM_LINKS pelnk; + PUSB_DEV pdev; + + if( ehci == NULL || purb == NULL ) + return STATUS_INVALID_PARAMETER; + + bytes_rest = purb->rest_bytes; + bytes_to_transfer = purb->bytes_to_transfer; + max_packet_size = endp_max_packet_size( purb->pendp ); + start_idx = purb->data_length - purb->rest_bytes; + + calc_td_count( purb, start_idx, td_count ); + td_count += 2; // add setup td and handshake td + + elem_pool_lock( qtd_pool, TRUE ); + pelnk = elem_pool_alloc_elems( qtd_pool, td_count ); + elem_pool_unlock( qtd_pool, TRUE ); + + if( pelnk == NULL ) + { + return STATUS_NO_MORE_ENTRIES; + } + + InsertTailList( &pelnk->elem_link, &td_list ); + ListFirst( &td_list, pthis ); + ListNext( &td_list, pthis, pnext ); + + ptd = qtd_from_list_entry( pthis ); + + pdev = dev_from_endp( purb->pendp ); + dev_addr = pdev->dev_addr; + + if( dev_state( pdev ) <= USB_DEV_STATE_RESET ) //only valid for control transfer + dev_addr = 0; + + usb_dbg_print( DBGLVL_MAXIMUM, ( "ehci_internal_submit_ctrl(): dev_addr =0x%x\n", \ + dev_addr ) ); + + // fill the setup packet + ptdc = ( PEHCI_QTD_CONTENT )ptd; + ptd->hw_next = qtd_from_list_entry( pnext )->phys_addr; + ptd->hw_alt_next = EHCI_PTR_TERM; + ptdc->status = 0x80; // active + ptdc->pid = QTD_PID_SETUP; + ptdc->err_count = 3; + ptdc->cur_page = 0; + ptdc->ioc = 0; + ptdc->bytes_to_transfer = sizeof( USB_CTRL_SETUP_PACKET ); + ptdc->data_toggle = 0; + ptd->hw_buf[ 0 ] = MmGetPhysicalAddress( purb->setup_packet ).LowPart; + + for( i = 1; i < 16; i++ ) + { + if( ( max_packet_size >> i ) == 0 ) + break; + } + i--; + i &= 0xf; + + purb->pipe = 0; + pipe_content = ( PURB_HS_PIPE_CONTENT )&purb->pipe; + pipe_content->max_packet_size = i; + pipe_content->endp_addr = endp_num( purb->pendp ); + pipe_content->dev_addr = dev_addr; + pipe_content->speed_low = ( pdev->flags & USB_DEV_FLAG_LOW_SPEED ) ? 1 : 0; + pipe_content->speed_high = ( pdev->flags & USB_DEV_FLAG_HIGH_SPEED ) ? 1 : 0; + pipe_content->trans_type = USB_ENDPOINT_XFER_CONTROL; + + pthis = pnext; + ListNext( &td_list, pthis, pnext ); + + // all the tds's toggle and data_buffer pointer is filled here + toggle = 1; + ehci_fill_td_buf_ptr( purb, start_idx, pthis, td_count - 2, toggle ); + + for( i = 0; ( ( i < td_count - 2 ) && pthis ); i++ ) + { + //construct tds for DATA packets of data stage. + ptd = qtd_from_list_entry( pthis ); + ptdc = ( PEHCI_QTD_CONTENT )ptd; + ptd->hw_alt_next = EHCI_PTR_TERM; + ptdc->status = 0x80; // active and startXSplit + ptdc->pid = ( ( purb->setup_packet[ 0 ] & USB_DIR_IN ) ? QTD_PID_IN : QTD_PID_OUT ); + ptdc->err_count = 3; + ptdc->cur_page = 0; + ptdc->ioc = 0; + + if( pnext ) + ptd->hw_next = qtd_from_list_entry( pnext )->phys_addr; + else + ptd->hw_next = EHCI_PTR_TERM; + + pthis = pnext; + if( pthis ) + ListNext( &td_list, pthis, pnext ); + } + + if( pthis ) + ptd->hw_next = qtd_from_list_entry( pthis )->phys_addr; + else + TRAP(); + + // ListFirstPrev( &td_list, pthis ); + ptd = qtd_from_list_entry( pthis ); + + //the last is an IN transaction + ptdc = ( PEHCI_QTD_CONTENT )ptd; + ptd->hw_alt_next = EHCI_PTR_TERM; + ptdc->status = 0x80; + ptdc->pid = ( ( td_count > 2 ) + ? ( ( purb->setup_packet[ 0 ] & USB_DIR_IN ) + ? QTD_PID_OUT : QTD_PID_IN ) + : QTD_PID_IN ); + + ptdc->err_count = 3; + ptdc->cur_page = 0; + ptdc->ioc = 1; + ptdc->bytes_to_transfer = 0; + ptdc->data_toggle = 1; + ptd->hw_next = EHCI_PTR_TERM; + + ListFirst( &td_list, pthis ); + RemoveEntryList( &td_list ); + + ptd = qtd_from_list_entry( pthis ); + elem_pool_lock( qh_pool, TRUE ); + pelnk = elem_pool_alloc_elem( qh_pool ); + elem_pool_unlock( qh_pool, TRUE); + + if( pelnk == NULL ) + { + elem_safe_free( pthis, FALSE ); + return STATUS_NO_MORE_ENTRIES; + + } + pqh = ( PEHCI_QH )( ( ULONG )pelnk->phys_part & PHYS_PART_ADDR_MASK ); + pqhc = ( PEHCI_QH_CONTENT )pqh; + + pqh->hw_alt_next = pqh->hw_next = EHCI_PTR_TERM; + + pqhc->dev_addr = dev_addr; + pqhc->inactive = 0; + pqhc->endp_addr = endp_num( purb->pendp ); + + if( pipe_content->speed_high ) + pqhc->endp_spd = USB_SPEED_HIGH; + else if( pipe_content->speed_low ) + pqhc->endp_spd = USB_SPEED_LOW; + else + pqhc->endp_spd = USB_SPEED_FULL; + + pqhc->data_toggle = 1; // use dt from qtd + pqhc->is_async_head = 0; + pqhc->max_packet_size = endp_max_packet_size( purb->pendp ); + + if( pipe_content->speed_high == 0 ) + pqhc->is_ctrl_endp = 1; + else + pqhc->is_ctrl_endp = 0; + + pqhc->reload_counter = EHCI_NAK_RL_COUNT; + + // DWORD 2 + pqh->hw_info2 = 0; + pqhc->mult = 1; + + if( !pipe_content->speed_high ) + { + pqhc->hub_addr = ( ( PURB_HS_CONTEXT_CONTENT )&purb->hs_context )->hub_addr; + pqhc->port_idx = ( ( PURB_HS_CONTEXT_CONTENT )&purb->hs_context )->port_idx; + } + + purb->td_count = td_count; + + ehci_insert_tds_qh( ehci, pqh, ptd ); + ehci_insert_qh_urb( purb, pqh ); + + usb_endp_busy_count_inc( purb->pendp ); + ehci_insert_urb_to_schedule( ehci, purb, ret ); + + if( ret == FALSE ) + { + RemoveEntryList( &purb->trasac_list ); + RemoveEntryList( &pqh->elem_head_link->elem_link ); + + elem_safe_free( &pqh->elem_head_link->elem_link, TRUE ); + elem_safe_free( pthis, FALSE ); + + InitializeListHead( &purb->trasac_list ); + // usb_endp_busy_count_dec( purb->pendp ); + return STATUS_UNSUCCESSFUL; + } + return STATUS_SUCCESS; +} + +static NTSTATUS +ehci_internal_submit_int( +PEHCI_DEV ehci, +PURB purb +) +{ + LIST_ENTRY td_list, *pthis, *pnext; + LONG i, max_packet_size; + PEHCI_QTD ptd; + BOOL ret; + PUSB_DEV pdev; + PURB_HS_PIPE_CONTENT pipe_content; + UCHAR mult_trans, toggle, old_toggle; + PEHCI_ELEM_LINKS pelnk; + PEHCI_QTD_CONTENT ptdc; + PEHCI_QH pqh; + PEHCI_QH_CONTENT pqhc; + PEHCI_FSTN pfstn; + + if( ehci == NULL || purb == NULL ) + return STATUS_INVALID_PARAMETER; + + old_toggle = toggle = ( purb->pendp->flags & USB_ENDP_FLAG_DATATOGGLE ) ? TRUE : FALSE; + max_packet_size = endp_max_packet_size( purb->pendp ); + pdev = dev_from_endp( purb->pendp ); + + if( max_packet_size == 0 || max_packet_size > 64 ) + return STATUS_INVALID_PARAMETER; + + if( ( pdev->flags & USB_DEV_FLAG_HIGH_SPEED ) == 0 ) + { + if( max_packet_size < purb->data_length ) + return STATUS_INVALID_PARAMETER; + + for( i = 1; i < 16; i ++ ) + { + if ( ( ( ( ULONG )purb->pendp->pusb_endp_desc->bInterval ) >> i ) == 0 ) + break; + } + i--; + mult_trans = 1; + } + else + { + mult_trans = endp_mult_count( purb->pendp ); + if( max_packet_size * endp_mult_count( purb->pendp ) < purb->data_length ) + return STATUS_INVALID_PARAMETER; + i = purb->pendp->pusb_endp_desc->bInterval - 1; + } + + purb->pipe = 0; + pipe_content = ( PURB_HS_PIPE_CONTENT )&purb->pipe; + pipe_content->interval = i; + pipe_content->trans_type = USB_ENDPOINT_XFER_INT;// bit 0-1 + pipe_content->speed_high = ( pdev->flags & USB_DEV_FLAG_HIGH_SPEED ) ? 1 : 0; // bit 5 + pipe_content->speed_low = ( pdev->flags & USB_DEV_FLAG_LOW_SPEED ) ? 1 : 0; // bit 6 + pipe_content->trans_dir = endp_dir( purb->pendp ) == USB_DIR_IN ? 1 : 0; // bit 7 + pipe_content->dev_addr = pdev->dev_addr; // bit 8-14 + pipe_content->endp_addr = endp_num( purb->pendp ); // bit 15-18 + pipe_content->data_toggle = 1; // bit 19 + pipe_content->mult_count = mult_trans; + + // pipe_content->start_uframe : 3; // bit 28-30 will be filled later + + for( i = 1; i <= 16; i++ ) + { + if( ( ( ULONG )max_packet_size ) >> i ) + continue; + else + break; + } + i --; + i &= 0xf; + + pipe_content->max_packet_size = i; // bit 20-23 log2( max_packet_size ) + + if( ehci_claim_bandwidth( ehci, purb, TRUE ) == FALSE ) + { + // can not allocate bandwidth for it + return STATUS_UNSUCCESSFUL; + } + + // one qtd is enough + elem_pool_lock( qtd_pool, TRUE ); + pelnk = elem_pool_alloc_elem( qtd_pool ); + elem_pool_unlock( qtd_pool, TRUE ); + + if( pelnk == NULL ) + { + ehci_claim_bandwidth( ehci, purb, FALSE ); + return STATUS_NO_MORE_ENTRIES; + } + + ptd = ( PEHCI_QTD )( ( ULONG )pelnk->phys_part & PHYS_PART_ADDR_MASK ); + ptdc = ( PEHCI_QTD_CONTENT )ptd; + ptd->hw_next = EHCI_PTR_TERM; + // DWORD 1 + ptd->hw_alt_next = EHCI_PTR_TERM; + // DWORD 2 + ptdc->status = 0x80; + ptdc->pid = pipe_content->trans_dir ? QTD_PID_IN : QTD_PID_OUT; + ptdc->err_count = 3; + ptdc->cur_page = 0; + ptdc->ioc = 1; + ptdc->bytes_to_transfer = purb->data_length; + toggle = ( UCHAR )ehci_fill_td_buf_ptr( purb, 0, &pelnk->elem_link, 1, toggle ); + + elem_pool_lock( qh_pool, TRUE ); + pelnk = elem_pool_alloc_elem( qh_pool ); + elem_pool_unlock( qh_pool, TRUE ); + if( pelnk == NULL ) + { + elem_safe_free( &ptd->elem_head_link->elem_link, TRUE ); + InitializeListHead( &purb->trasac_list ); + ehci_claim_bandwidth( ehci, purb, FALSE ); + return STATUS_NO_MORE_ENTRIES; + } + pqh = ( PEHCI_QH )( ( ULONG )pelnk->phys_part & PHYS_PART_ADDR_MASK ); + pqhc = ( PEHCI_QH_CONTENT )pqh; + + pqh->hw_next = EHCI_PTR_TERM; + pqhc->dev_addr = pdev->dev_addr; + pqhc->inactive = 0; + pqhc->endp_addr = endp_num( purb->pendp ); + + if( pipe_content->speed_high ) + pqhc->endp_spd = USB_SPEED_HIGH; + else if( pipe_content->speed_low ) + pqhc->endp_spd = USB_SPEED_LOW; + else + pqhc->endp_spd = USB_SPEED_FULL; + + pqhc->data_toggle = 0; + pqhc->is_async_head = 0; + pqhc->max_packet_size = endp_max_packet_size( purb->pendp ); + pqhc->is_ctrl_endp = 0; + pqhc->reload_counter = 0; + + // DWORD 2 + pqh->hw_info2 = 0; + pqhc->mult = mult_trans; + + if( pipe_content->speed_high ) + { + if( pipe_content->interval == 0 ) // one poll per uframe + pqhc->s_mask = 0xff; + else if( pipe_content->interval == 1 ) // one poll every 2 uframe + pqhc->s_mask = pipe_content->start_uframe == 0 ? 0x55 : 0xbb; + else if( pipe_content->interval == 2 ) + { + pqhc->s_mask = 0x11; + pqhc->s_mask <<= pipe_content->start_uframe; + } + else + { + pqhc->s_mask = 1 << ( pipe_content->start_uframe ); + } + pqhc->c_mask = 0; + } + else // full/low speed + { + pqhc->s_mask = 1 << pipe_content->start_uframe; + if( pipe_content->start_uframe < 4 ) + { + pqhc->c_mask = 0x07 << ( pipe_content->start_uframe + 2 ); + } + else if( pipe_content->start_uframe == 4 ) + { + pqhc->c_mask = 0xc1; + } + else if( pipe_content->start_uframe >= 5 ) + { + // we need fstn + pqhc->c_mask = 0x03; + if( pipe_content->start_uframe == 5 ) + { + pqhc->c_mask |= 0x80; + } + } + if( pipe_content->start_uframe >= 4 ) + { + // chain an fstn + elem_pool_lock( fstn_pool, TRUE ); + pelnk = elem_pool_alloc_elem( fstn_pool ); + elem_pool_unlock( fstn_pool, TRUE ); + if( pelnk == NULL ) + { + elem_safe_free( &pqh->elem_head_link->elem_link, TRUE ); + elem_safe_free( &ptd->elem_head_link->elem_link, TRUE ); + InitializeListHead( &purb->trasac_list ); + ehci_claim_bandwidth( ehci, purb, FALSE ); + return STATUS_NO_MORE_ENTRIES; + } + pfstn = ( PEHCI_FSTN )( ( ULONG )pelnk->phys_part & PHYS_PART_ADDR_MASK ); + pfstn->hw_prev = ptd->phys_addr; + pfstn->elem_head_link->purb = purb; + InsertTailList( &ptd->elem_head_link->elem_link, &pfstn->elem_head_link->elem_link ); + } + pqhc->hub_addr = ( ( PURB_HS_CONTEXT_CONTENT )&purb->hs_context )->hub_addr; + pqhc->port_idx = ( ( PURB_HS_CONTEXT_CONTENT )&purb->hs_context )->port_idx; + } + + // DWORD 3 + purb->td_count = 1; + + InitializeListHead( &purb->trasac_list ); + ehci_insert_tds_qh( ehci, pqh, ptd ); + ehci_insert_qh_urb( purb, pqh ); + + purb->pendp->flags = ( purb->pendp->flags & ~USB_ENDP_FLAG_DATATOGGLE ) | ( toggle << 31 ); + usb_endp_busy_count_inc( purb->pendp ); + + ehci_insert_urb_to_schedule( ehci, purb, ret ); + + if( ret == FALSE ) + { + RemoveEntryList( &purb->trasac_list ); + RemoveEntryList( &pqh->elem_head_link->elem_link ); + + elem_safe_free( &pqh->elem_head_link->elem_link, TRUE ); + // an fstn may follow the td + elem_safe_free( &ptd->elem_head_link->elem_link, FALSE ); + + InitializeListHead( &purb->trasac_list ); + ehci_claim_bandwidth( ehci, purb, FALSE ); + + purb->pendp->flags = ( purb->pendp->flags & ~USB_ENDP_FLAG_DATATOGGLE ) | ( ( toggle ^ 1 ) << 31 ); + // usb_endp_busy_count_dec( purb->pendp ); + + return STATUS_UNSUCCESSFUL; + } + + return STATUS_SUCCESS; +} + + +static NTSTATUS +ehci_internal_submit_iso( +PEHCI_DEV ehci, +PURB purb +) +{ + LONG i, j, td_count, temp; + PEHCI_ITD pitd; + PEHCI_SITD psitd; + PEHCI_SITD_CONTENT psitdc; + PEHCI_ITD_CONTENT pitdc; + LIST_ENTRY td_list, *pthis, *pnext, *pprev; + BOOL toggle, ret; + KIRQL old_irql; + PURB_HS_PIPE_CONTENT pipe_content; + PUSB_DEV pdev; + PEHCI_ELEM_LINKS pelnk; + + if( ehci == NULL || purb == NULL ) + return STATUS_INVALID_PARAMETER; + + if( purb->iso_frame_count == 0 ) + return STATUS_INVALID_PARAMETER; + + pdev = dev_from_endp( purb->pendp ); + purb->pipe = 0; + pipe_content = ( PURB_HS_PIPE_CONTENT )&purb->pipe; + pipe_content->trans_type = USB_ENDPOINT_XFER_ISOC; // bit 0-1 + pipe_content->speed_high = ( pdev->flags & USB_DEV_FLAG_HIGH_SPEED ) ? 1 : 0; // bit 5 + pipe_content->speed_low = 0; // bit 6 + pipe_content->trans_dir = endp_dir( purb->pendp ) == USB_DIR_IN ? 1 : 0; // bit 7 + pipe_content->dev_addr = pdev->dev_addr; // bit 8-14 + pipe_content->endp_addr = endp_num( purb->pendp ); // bit 15-18 + pipe_content->data_toggle = 0; // bit 19 + + ret = FALSE; + purb->params[ 0 ] = j = endp_max_packet_size( purb->pendp ); + + if( pipe_content->speed_high == 0 ) + { + // check to see if the frame data is too long to transfer + if( purb->iso_frame_count >= ( LONG )ehci->frame_count ) + return STATUS_INVALID_PARAMETER; + + for( i = 0; i < ( LONG )purb->iso_frame_count; i++ ) + { + if( purb->iso_packet_desc[ i ].length > j ) + return STATUS_INVALID_PARAMETER; + } + } + else + { + // excess the frame count limit + if( purb->iso_frame_count >= ( LONG )( ehci->frame_count << 3 ) ) + return STATUS_INVALID_PARAMETER; + + for( i = 0; i < ( LONG )purb->iso_frame_count; i++ ) + { + if( purb->iso_packet_desc[ i ].length > j * endp_mult_count( purb->pendp ) ) // 3 is max mult-transaction count + return STATUS_INVALID_PARAMETER; + } + + pipe_content->mult_count = endp_mult_count( purb->pendp ); + } + + pipe_content->max_packet_size = 0; // bit 20-23 log( max_packet_size ), not correct, should not be used + + if( pipe_content->speed_high == 0 ) + { + for( i = 1; i < 16; i ++ ) + { + if ( ( ( ( ULONG )purb->pendp->pusb_endp_desc->bInterval ) >> i ) == 0 ) + break; + } + i--; + } + else + { + i = purb->pendp->pusb_endp_desc->bInterval - 1; + } + + pipe_content->interval = i; // bit 24-27 the same definition as in USB2.0 spec, for high or full/low speed + + if( ehci_claim_bandwidth( ehci, purb, TRUE ) == FALSE ) + return STATUS_UNSUCCESSFUL; + + if( pipe_content->speed_high == 0 ) + { + td_count = purb->iso_frame_count; + + // test to see if the last td needs one more sitd for pure complete-split + if( pipe_content->trans_dir == 0 ) + { + j = ( purb->iso_packet_desc[ purb->iso_frame_count - 1 ].length + 187 ) / 188; + if( purb->iso_packet_desc[ purb->iso_frame_count - 1 ].params.start_uframe + 1 + j >= 8 ) + { + td_count++; + ret = TRUE; + } + } + elem_pool_lock( itd_pool, TRUE ); + pelnk = elem_pool_alloc_elems( itd_pool, td_count ); + elem_pool_unlock( itd_pool, TRUE ); + + } + else + { + i = REAL_INTERVAL; + if( pipe_content->interval >= 3 ) + { + td_count = purb->iso_frame_count; + j = 0; + } + else + { + j = purb->iso_start_frame & 0x07; + if( j == 0 ) + { + td_count = ( purb->iso_frame_count + 8 / i - 1 ) * i / 8; + } + else + { + j = 1 + ( 7 - j ) / i; // the leading packets from the 8-trans boundary + td_count = ( j >= ( LONG )purb->iso_frame_count ? + 1 : 1 + ( purb->iso_frame_count - j + 8 / i - 1 ) * i / 8 ); + } + } + + elem_pool_lock( sitd_pool, TRUE ); + pelnk = elem_pool_alloc_elems( sitd_pool, td_count ); + elem_pool_unlock( sitd_pool, TRUE ); + } + + if( pelnk == NULL ) + { + ehci_claim_bandwidth( ehci, purb, FALSE ); + return STATUS_NO_MORE_ENTRIES; + } + + InsertTailList( &pelnk->elem_link, &td_list ); + ListFirst( &td_list, pthis ); + pprev = pthis; + purb->td_count = td_count; + + //set up offset for high speed and interval == 1 + if( pipe_content->speed_high && pipe_content->interval == 0 ) + { + for( i = 0; i < ( LONG )purb->iso_frame_count; i++ ) + { + if( i == 0 ) + purb->iso_packet_desc[ i ].offset = 0; + else + purb->iso_packet_desc[ i ].offset = purb->iso_packet_desc[ i - 1 ].offset + + purb->iso_packet_desc[ i ].length; + } + } + + i = 0, temp = 0; + + while( pthis ) + { + init_elem_phys_part( struct_ptr( pthis, EHCI_ELEM_LINKS, elem_link ) ); + if( pipe_content->speed_high ) + { + LONG start_uframe, k; + LONG l, pk_idx, offset, start_uf, td_length; + PULONG pbuf; + ULONG phys_addr[ 8 ]; + + pitd = itd_from_list_entry( pthis ); + pitdc = ( PEHCI_ITD_CONTENT )pitd; + start_uframe = purb->iso_start_frame & 0x07; + + // will be filled later + pitd->hw_next = EHCI_PTR_TERM; + + // DWORD 9; + pitdc->dev_addr = pdev->dev_addr; + pitdc->endp_num = endp_num( purb->pendp ); + + pitdc->max_packet_size = endp_max_packet_size( purb->pendp ); + pitdc->io_dir = pipe_content->trans_dir; + pitdc->mult = endp_mult_count( purb->pendp ); + + pbuf = pitd->hw_bufp; + RtlZeroMemory( phys_addr, sizeof( phys_addr ) ); + + if( pipe_content->interval < 3 ) + { + // this indicates one itd schedules more than one uframes + // for multiple transactions described by iso_packet_desc + if( i == 0 ) + k = td_count == 1 ? purb->iso_frame_count : j; // the first itd + else + k = ( LONG )( purb->iso_frame_count - i ) <= 8 / REAL_INTERVAL + ? ( purb->iso_frame_count - i ) : 8 / REAL_INTERVAL; + + // j is the header transactions out of the interval + // aligned transactions per td + if( j > 0 && i == 0 ) // handle the first itd + start_uf = start_uframe; + else + start_uf = start_uframe % REAL_INTERVAL; + } + else + { + k = 1, start_uf = start_uframe & 0x07; + } + + + // calculate the data to transfer with this td + td_length = 0; + for( l = start_uf, pk_idx = i; pk_idx < i + k; pk_idx++, l += REAL_INTERVAL ) + { + td_length += purb->iso_packet_desc[ pk_idx ].length; + phys_addr[ l ] = MmGetPhysicalAddress( &purb->data_buffer[ purb->iso_packet_desc[ pk_idx ].offset ] ).LowPart; + } + + // fill the page pointer, and offset + if( pipe_content->interval != 0 ) + { + for( l = start_uf, pk_idx = i; pk_idx < i + k; pk_idx++, l+= REAL_INTERVAL ) + { + pitdc->status_slot[ l ].offset = phys_addr[ l ] & ( PAGE_SIZE - 1 ); + pbuf[ l >> pipe_content->interval ] |= phys_addr[ l ] & ( ~( PAGE_SIZE - 1 ) ); + pitdc->status_slot[ l ].page_sel = l >> pipe_content->interval; + pitdc->status_slot[ l ].status = 0x08; + pitdc->status_slot[ l ].trans_length = purb->iso_packet_desc[ pk_idx ].length; + if( PAGE_SIZE - pitdc->status_slot[ l ].offset < ( ULONG )purb->iso_packet_desc[ pk_idx ].length ) + { + // fill the next page buf, we can not simply add + // PAGE_SIZE to the phys_addr[ l ]. + pbuf[ ( l >> pipe_content->interval ) + 1 ] |= + MmGetPhysicalAddress( + ( PBYTE )( ( ( ULONG )&purb->data_buffer[ purb->iso_packet_desc[ pk_idx ].offset ] ) & ( ~( PAGE_SIZE - 1 ) ) ) + + PAGE_SIZE ).LowPart; + } + } + } + else // interval == 0 + { + LONG m, n, n2; + // fill the page buffer first + // calculate the page buffer needed + offset = phys_addr[ 0 ] & ( PAGE_SIZE - 1 ); + if( offset != 0 ) + { + offset = PAGE_SIZE - offset; + l = 1 + ( td_length - offset + PAGE_SIZE - 1 ) / PAGE_SIZE; + } + else + { + l = ( td_length + PAGE_SIZE - 1 ) / PAGE_SIZE; + } + + if( l > 7 ) + TRAP(); + + // fill the hw_bufp array and PG field, pk_idx is index into hw_bufp + for( pk_idx = 0; pk_idx < l; pk_idx++ ) + { + if( pk_idx == 0 ) + { + offset = phys_addr[ start_uf ] & ( ~( PAGE_SIZE - 1 ) ); + pbuf[ pk_idx ] |= offset; + n = pk_idx; + pitdc->status_slot[ 0 ].page_sel = n; + n2 = start_uf; + } + else + { + // scan to find if the buf pointer already filled in the td + // since interval = 1, we do not need k * REAL_INTERVAL + // k is transaction count for current td, + // n is hw_bufp( pbuf ) index + // n2 is the last phys_addr index we stopped + for( m = n2; m < start_uf + k ; m++ ) + { + // we can not determine the phys_addr[ x ] is piror + // to offset if it is less than offset. + // because phys_addr is discrete. + // if( ( phys_addr[ m ] & ( ~( PAGE_SIZE - 1 ) ) ) < offset ) + // continue; + + if( ( phys_addr[ m ] & ( ~( PAGE_SIZE - 1 ) ) ) == ( ULONG )offset ) + { + pitdc->status_slot[ m ].page_sel = n; + continue; + } + break; + } + + if( m == start_uf + k ) + TRAP(); + + offset = phys_addr[ m ] & ( ~( PAGE_SIZE - 1 ) ); + pbuf[ pk_idx ] |= offset; + n = pk_idx; + n2 = m; + pitdc->status_slot[ m ].page_sel = n; + } + } + // fill offset and others + for( l = start_uf, pk_idx = i; l < start_uf + k; l++, pk_idx++ ) + { + pitdc->status_slot[ l ].offset = ( phys_addr[ l ] & ( PAGE_SIZE - 1 ) ); + pitdc->status_slot[ l ].status = 0x08; + pitdc->status_slot[ l ].trans_length = purb->iso_packet_desc[ pk_idx ].length; + } + // exhausted + } + i += k; + } + else // full/low speed + { + psitd = sitd_from_list_entry( pthis ); + psitdc = ( PEHCI_SITD_CONTENT )psitd; + psitd->hw_next = EHCI_PTR_TERM; + + // DWORD 1; + psitdc->dev_addr = pdev->dev_addr; + psitdc->endp_num = endp_num( purb->pendp ); + psitdc->hub_addr = ( ( PURB_HS_CONTEXT_CONTENT )&purb->hs_context )->hub_addr; + psitdc->port_idx = ( ( PURB_HS_CONTEXT_CONTENT )&purb->hs_context )->port_idx; + psitdc->io_dir = endp_dir( purb->pendp ); + + psitdc->status &= 0x80; // in DWORD 3 + + // DWORD 2; + j = ( purb->iso_packet_desc[ i ].length + 187 ) / 188; + + if( psitdc->io_dir == 0 ) + { + for( ; j > 0; j-- ) + { + psitdc->s_mask |= ( 1 << ( j - 1 ) ); + } + psitdc->s_mask <<= purb->iso_packet_desc[ i ].params.start_uframe & 0x07; + psitdc->c_mask = 0; + } + else + { + LONG k; + + psitdc->s_mask = 1 << purb->iso_packet_desc[ i ].params.start_uframe & 0x07; + // iso split case 2b: ehci spec 1.0 + if( j == 6 ) + j = 5; + + j = j - 1 + 2; // actual complete-split count + + psitdc->c_mask |= temp >> 8; // the previous sitd's complete split + if( temp >> 8 ) // link back for sitd split completion + { + psitd->hw_backpointer = sitd_from_list_entry( pprev )->phys_addr; + psitdc->status &= 0x82; + } + else + { + psitd->hw_backpointer = EHCI_PTR_TERM; + } + + for( k = temp = 0; k < j; k++ ) + { + temp |= 1 << k; + } + + temp <<= ( ( purb->iso_packet_desc[ i ].params.start_uframe & 0x07 ) + 2 ); + + // only uframe zero and one have complete split for prev sitd + if( ( temp >> 8 ) > 3 ) + TRAP(); + + psitdc->c_mask |= temp & 0xff; + } + + // DWORD 3: + psitdc->c_prog_mask = 0; + psitdc->bytes_to_transfer = purb->iso_packet_desc[ i ].length; + psitdc->page_sel = 0; + psitdc->ioc = 0; + + // DWORD 4; + j = ( ULONG )( ( PBYTE )purb->data_buffer + purb->iso_packet_desc[ i ].offset ); + psitd->hw_tx_results2 = MmGetPhysicalAddress( ( PVOID )j ).LowPart; + + // DWORD 5; + if( PAGE_SIZE - ( j & ( PAGE_SIZE - 1 ) ) < ( ULONG )purb->iso_packet_desc[ i ].length ) + { + // need to fill another slot + psitdc->page1 = MmGetPhysicalAddress ( ( PVOID ) ( ( j & ~( PAGE_SIZE - 1 ) ) + PAGE_SIZE ) ).LowPart >> 12; + } + + if( purb->iso_packet_desc[ i ].length > 188 ) + psitdc->trans_pos = 0x00; + else if( purb->iso_packet_desc[ i ].length <= 188 ) + psitdc->trans_pos = 0x01; + + if( psitdc->io_dir == 0 ) + psitdc->trans_count = ( purb->iso_packet_desc[ i ].length + 187 ) / 188; + + } + ListNext( &td_list, pthis, pnext ); + pprev = pthis; + pthis = pnext; + + } + + if( pipe_content->speed_high == 0 ) + { + // has an extra sitd to fill at the tail + if( ret ) + { + ListFirstPrev( &td_list, pthis ); + init_elem_phys_part( struct_ptr( pthis, EHCI_ELEM_LINKS, elem_link ) ); + + psitd = sitd_from_list_entry( pthis ); + psitdc = ( PEHCI_SITD_CONTENT )psitd; + psitd->hw_next = EHCI_PTR_TERM; + + // DWORD 1; + psitdc->dev_addr = pdev->dev_addr; + psitdc->endp_num = endp_num( purb->pendp ); + psitdc->hub_addr = ( ( PURB_HS_CONTEXT_CONTENT )&purb->hs_context )->hub_addr; + psitdc->port_idx = ( ( PURB_HS_CONTEXT_CONTENT )&purb->hs_context )->port_idx; + psitdc->io_dir = endp_dir( purb->pendp ); + + psitdc->status &= 0x80; // in DWORD 3 + + // DWORD 2; + psitdc->s_mask = 0x04; // uframe 2, random selection + + psitdc->c_mask = 0x70; // complete split at uframe 4, 5, 6 + ListFirstPrev( pthis, pprev ); + psitd->hw_backpointer = sitd_from_list_entry( pprev )->phys_addr; + psitdc->status &= 0x82; + + // DWORD 3: + psitdc->c_prog_mask = 0; + psitdc->bytes_to_transfer = 1; // purb->iso_packet_desc[ purb->iso_frame_count - 1 ].length; + psitdc->page_sel = 0; + + j = ( ULONG )( ( PBYTE )purb->data_buffer + purb->iso_packet_desc[ purb->iso_frame_count - 1 ].offset ); + // the last byte is overridden. + j += purb->iso_packet_desc[ purb->iso_frame_count - 1 ].length - 1; + psitd->hw_tx_results2 = MmGetPhysicalAddress( ( PVOID )j ).LowPart; + } + + // set the interrupt + ListFirstPrev( &td_list, pthis ); + psitdc = ( PEHCI_SITD_CONTENT )sitd_from_list_entry( pthis ); + psitdc->ioc = 1; + } + else + { + // set the ioc + ListFirstPrev( &td_list, pthis ); + pitdc = ( PEHCI_ITD_CONTENT )itd_from_list_entry( pthis ); + for( i = 7; i >= 0; i-- ) + { + if( pitdc->status_slot[ i ].status = 0x08 ) + { + pitdc->status_slot[ i ].ioc = 1; + break; + } + } + if( i < 0 ) + TRAP(); + } + + ListFirst( &td_list, pthis ); + // ListFirst( &purb->trasac_list, pthis ) + RemoveEntryList( &td_list ); + InsertTailList( pthis, &purb->trasac_list ); + + while( pthis ) + { + // fill the purb ptr + struct_ptr( pthis, EHCI_ELEM_LINKS, elem_link )->purb = purb; + ListNext( &purb->trasac_list, pthis, pnext ); + pthis = pnext; + } + + //indirectly guarded by pending_endp_list_lock + usb_endp_busy_count_inc( purb->pendp ); + ehci_insert_urb_to_schedule( ehci, purb, ret ); + + if( ret == FALSE ) + { + // usb_endp_busy_count_dec( purb->pendp ); + + ListFirst( &purb->trasac_list, pthis ); + RemoveEntryList( &purb->trasac_list ); + + elem_safe_free( pthis, FALSE ); + ehci_claim_bandwidth( ehci, purb, FALSE ); + return STATUS_UNSUCCESSFUL; + } + return STATUS_SUCCESS; +} + +BOOLEAN +ehci_sync_insert_urb_schedule( +PVOID context +) +//this function used as the KeSynchronizeExecution param to delegate control to ehci_insert_urb_schedule +{ + PSYNC_PARAM sync_param; + PEHCI_DEV ehci; + PURB purb; + + sync_param = ( PSYNC_PARAM ) context; + if( sync_param == NULL ) + return FALSE; + + ehci = sync_param->ehci; + purb = ( PURB )sync_param->context; + + if( ehci == NULL || purb == NULL ) + return ( UCHAR )sync_param->ret = FALSE; + + return ( UCHAR )( sync_param->ret = ehci_insert_urb_schedule( ehci, purb ) ); +} + +BOOLEAN +ehci_sync_cancel_urb( +PVOID context +) +{ + //cancel a single purb + PEHCI_DEV ehci; + PSYNC_PARAM sync_param; + PURB purb2, dest_urb; + PLIST_ENTRY pthis, pnext; + BOOL found = FALSE; + + if( context == NULL ) + return FALSE; + + sync_param = ( PSYNC_PARAM )context; + ehci = sync_param->ehci; + dest_urb = ( PURB )sync_param->context; + + if( ehci == NULL || dest_urb == NULL ) + return ( UCHAR )sync_param->ret = FALSE; + + ListFirst( &ehci->urb_list, pthis ); + while( pthis ) + { + purb2 = ( PURB ) pthis; + if( purb2 == dest_urb ) + { + found = TRUE; + purb2->flags |= URB_FLAG_FORCE_CANCEL; + break; + } + ListNext( &ehci->urb_list, pthis, pnext ); + pthis = pnext; + } + + if( found ) + { + press_doorbell( ehci ); + } + return ( UCHAR )( sync_param->ret = found ); +} + +NTSTATUS +ehci_cancel_urb( +PEHCI_DEV ehci, +PUSB_DEV pdev, +PUSB_ENDPOINT pendp, +PURB purb +) +//note any fields of the purb can not be referenced unless it is found in some queue +{ + + NTSTATUS status; + PLIST_ENTRY pthis, pnext; + BOOL found; + PURB purb2; + + SYNC_PARAM sync_param; + + USE_IRQL; + + if( ehci == NULL || purb == NULL || pdev == NULL || pendp == NULL ) + return STATUS_INVALID_PARAMETER; + + lock_dev( pdev, FALSE ); + + if( dev_state( pdev ) == USB_DEV_STATE_ZOMB ) + { + unlock_dev( pdev, FALSE ); + //delegate to remove device for this job + return STATUS_DEVICE_DOES_NOT_EXIST; + } + + if( dev_from_endp( pendp ) != pdev ) + { + unlock_dev( pdev, FALSE ); + return STATUS_INVALID_PARAMETER; + } + + if( endp_state( pendp ) == USB_ENDP_FLAG_STALL ) + { + //it will be canceled in ehci_process_pending_endp + unlock_dev( pdev, FALSE ); + return USB_STATUS_ENDPOINT_HALTED; + } + + found = FALSE; + ListFirst( &pendp->urb_list, pthis ); + while( pthis ) + { + purb2 = ( PURB ) pthis; + if( purb2 == purb ) + { + found = TRUE; + RemoveEntryList( pthis ); + InitializeListHead( pthis ); + break; + } + ListNext( &pendp->urb_list, pthis, pnext ); + pthis = pnext; + } + unlock_dev( pdev, FALSE ); + + if( found ) + { + purb->status = STATUS_CANCELLED; + + ehci_generic_urb_completion( purb, purb->context ); + + lock_dev( pdev, FALSE ); + pdev->ref_count --; + unlock_dev( pdev, FALSE ); + return STATUS_SUCCESS; + } + + // search the purb in the purb-list and try to cancel + sync_param.ehci = ehci; + sync_param.context = purb; + + KeSynchronizeExecution( ehci->pdev_ext->ehci_int, ehci_sync_cancel_urb, &sync_param ); + + found = sync_param.ret; + + if( found ) + return USB_STATUS_CANCELING; + + return STATUS_INVALID_PARAMETER; +} + +VOID +ehci_generic_urb_completion( +PURB purb, +PVOID context +) +{ + PUSB_DEV pdev; + KIRQL cur_irql; + BOOL is_ctrl; + USE_IRQL; + + old_irql = KeGetCurrentIrql(); + if( old_irql > DISPATCH_LEVEL ) + TRAP(); + + if( old_irql < DISPATCH_LEVEL ) + KeRaiseIrql(DISPATCH_LEVEL, &old_irql); + + pdev = purb->pdev; + if( purb == NULL ) + goto LBL_LOWER_IRQL; + + if( pdev == NULL ) + goto LBL_LOWER_IRQL; + + lock_dev( pdev, TRUE ); + + if( dev_state( pdev ) == USB_DEV_STATE_ZOMB ) + { + // no need to do following statistics + unlock_dev( pdev, TRUE ); + goto LBL_CLIENT_PROCESS; + } + if( usb_error( purb->status ) ) + { + pdev->error_count++; + } + + if( purb->pendp == &pdev->default_endp ) + { + if( usb_halted( purb->status ) ) + { + pdev->time_out_count++; + if( pdev->time_out_count > 3 ) + { + dev_set_state( pdev, USB_DEV_STATE_ZOMB ); + ehci_dbg_print( DBGLVL_MAXIMUM, ("ehci_generic_urb_completion(): contiguous error 3 times, dev 0x%x is deactivated\n", pdev ) ); + } + } + else + pdev->time_out_count = 0; + + } + + is_ctrl = FALSE; + if( endp_type( purb->pendp ) == USB_ENDPOINT_XFER_CONTROL ) + is_ctrl = TRUE; + + unlock_dev( pdev, TRUE ); + +LBL_CLIENT_PROCESS: + if( !is_ctrl ) + { + if( purb->completion ) + purb->completion( purb, context ); + } + else + { + PCTRL_REQ_STACK pstack; + if( purb->ctrl_req_context.ctrl_stack_count == 0 ) + { + if( purb->completion ) + purb->completion( purb, context ); + } + else + { + // pstack = &purb->ctrl_req_stack[ purb->ctrl_req_context.ctrl_cur_stack ]; + // if( pstack->urb_completion ) + // pstack->urb_completion( purb, pstack->context ); + usb_call_ctrl_completion( purb ); + } + } + +LBL_LOWER_IRQL: + if( old_irql < DISPATCH_LEVEL ) + KeLowerIrql( old_irql ); + + return; +} + +NTSTATUS +ehci_rh_submit_urb( +PUSB_DEV pdev, +PURB purb +) +{ + PUSB_DEV_MANAGER dev_mgr; + PTIMER_SVC ptimer; + PUSB_CTRL_SETUP_PACKET psetup; + PEHCI_DEV ehci; + NTSTATUS status; + PHUB2_EXTENSION hub_ext; + PUSB_PORT_STATUS ps, psret; + LONG i; + UCHAR port_count; + + USE_IRQL; + if( pdev == NULL || purb == NULL ) + return STATUS_INVALID_PARAMETER; + + dev_mgr = dev_mgr_from_dev( pdev ); + + KeAcquireSpinLock( &dev_mgr->timer_svc_list_lock, &old_irql ); + lock_dev( pdev, FALSE ); + if( dev_state( pdev ) == USB_DEV_STATE_ZOMB ) + { + unlock_dev( pdev, FALSE ); + KeReleaseSpinLock( &dev_mgr->timer_svc_list_lock, old_irql ); + return STATUS_DEVICE_DOES_NOT_EXIST; + } + + ehci = ehci_from_hcd( pdev->hcd ); + psetup = ( PUSB_CTRL_SETUP_PACKET ) purb->setup_packet; + + hub_ext = ( ( PHUB2_EXTENSION )pdev->dev_ext ); + port_count = ( UCHAR )( ( PEHCI_HCS_CONTENT )&ehci->ehci_caps.hcs_params )->port_count; + + switch( endp_type( purb->pendp ) ) + { + case USB_ENDPOINT_XFER_CONTROL: + { + if( psetup->bmRequestType == 0xa3 \ + && psetup->bRequest == USB_REQ_GET_STATUS ) + { + //get-port-status + if( psetup->wIndex == 0 \ + || psetup->wIndex > port_count\ + || psetup->wLength < 4 ) + { + purb->status = STATUS_INVALID_PARAMETER; + break; + } + + i = EHCI_PORTSC + 4 * ( psetup->wIndex - 1 ); // USBPORTSC1; + status = EHCI_READ_PORT_ULONG( ( PULONG )( ehci->port_base + i ) ); + ps = &hub_ext->rh_port_status[ psetup->wIndex ]; + + psret = ( PUSB_PORT_STATUS )purb->data_buffer; + ps->wPortStatus = 0; + + if( status & PORT_CCS ) + { + ps->wPortStatus |= USB_PORT_STAT_CONNECTION; + } + if( status & PORT_PE ) + { + ps->wPortStatus |= USB_PORT_STAT_ENABLE; + ps->wPortStatus |= USB_PORT_STAT_HIGH_SPEED; // ehci spec + } + if( status & PORT_PR ) + { + ps->wPortStatus |= USB_PORT_STAT_RESET; + } + if( status & PORT_SUSP ) + { + ps->wPortStatus |= USB_PORT_STAT_SUSPEND; + } + if( PORT_USB11( status ) ) + { + ps->wPortStatus |= USB_PORT_STAT_LOW_SPEED; + } + + //always power on + ps->wPortStatus |= USB_PORT_STAT_POWER; + + //now set change field + if( ( status & PORT_CSC ) && !( ps->wPortStatus & USB_PORT_STAT_LOW_SPEED ) ) + { + ps->wPortChange |= USB_PORT_STAT_C_CONNECTION; + } + if( ( status & PORT_PEC ) && !( ps->wPortStatus & USB_PORT_STAT_LOW_SPEED ) ) + { + ps->wPortChange |= USB_PORT_STAT_C_ENABLE; + } + + //don't touch other fields, might be filled by + //other function + + usb_dbg_print( DBGLVL_MAXIMUM, ( \ + "ehci_rh_submit_urb(): get port status, wPortStatus=0x%x, wPortChange=0x%x, address=0x%x\n", \ + ps->wPortStatus, + ps->wPortChange, + ps ) ); + + psret->wPortChange = ps->wPortChange; + psret->wPortStatus = ps->wPortStatus; + + purb->status = STATUS_SUCCESS; + + break; + } + else if( psetup->bmRequestType == 0x23 \ + && psetup->bRequest == USB_REQ_CLEAR_FEATURE ) + { + //clear-port-feature + if( psetup->wIndex == 0 || psetup->wIndex > port_count ) + { + purb->status = STATUS_INVALID_PARAMETER; + break; + } + + i = EHCI_PORTSC + 4 * ( psetup->wIndex - 1 ); // USBPORTSC1; + ps = &hub_ext->rh_port_status[ psetup->wIndex ]; + + purb->status = STATUS_SUCCESS; + switch( psetup->wValue ) + { + case USB_PORT_FEAT_C_CONNECTION: + { + SET_RH2_PORTSTAT( i, USBPORTSC_CSC ); + status = EHCI_READ_PORT_ULONG( ( PULONG ) ( ehci->port_base + i ) ); + usb_dbg_print( DBGLVL_MAXIMUM, ( "ehci_rh_submit_urb(): clear csc, port%d=0x%x\n", psetup->wIndex ) ); + ps->wPortChange &= ~USB_PORT_STAT_C_CONNECTION; + break; + } + case USB_PORT_FEAT_C_ENABLE: + { + SET_RH2_PORTSTAT( i, USBPORTSC_PEC ); + status = EHCI_READ_PORT_ULONG( ( PULONG ) ( ehci->port_base + i ) ); + usb_dbg_print( DBGLVL_MAXIMUM, ( "ehci_rh_submit_urb(): clear pec, port%d=0x%x\n", psetup->wIndex ) ); + ps->wPortChange &= ~USB_PORT_STAT_C_ENABLE; + break; + } + case USB_PORT_FEAT_C_RESET: + { + ps->wPortChange &= ~USB_PORT_STAT_C_RESET; + //the reset signal is down in rh_timer_svc_reset_port_completion + // enable or not is set by host controller + // status = EHCI_READ_PORT_ULONG( ( PUSHORT ) ( ehci->port_base + i ) ); + usb_dbg_print( DBGLVL_MAXIMUM, ( "ehci_rh_submit_urb(): clear pr, enable pe, port%d=0x%x\n", psetup->wIndex ) ); + break; + } + case USB_PORT_FEAT_ENABLE: + { + ps->wPortStatus &= ~USB_PORT_STAT_ENABLE; + CLR_RH2_PORTSTAT( i, USBPORTSC_PE ); + status = EHCI_READ_PORT_ULONG( ( PULONG ) ( ehci->port_base + i ) ); + usb_dbg_print( DBGLVL_MAXIMUM, ( "ehci_rh_submit_urb(): clear pe, port%d=0x%x\n", psetup->wIndex ) ); + break; + } + default: + purb->status = STATUS_UNSUCCESSFUL; + } + break; + } + else if( psetup->bmRequestType == 0xd3 \ + && psetup->bRequest == HUB_REQ_GET_STATE ) + { + // get bus state + if( psetup->wIndex == 0 ||\ + psetup->wIndex > port_count ||\ + psetup->wLength == 0 ) + { + purb->status = STATUS_INVALID_PARAMETER; + break; + } + + i = EHCI_PORTSC + 4 * ( psetup->wIndex - 1 ); // USBPORTSC1; + status = EHCI_READ_PORT_ULONG( ( PULONG )( ehci->port_base + i ) ); + purb->data_buffer[ 0 ] = ( status & USBPORTSC_LS ); + + // reverse the order + purb->data_buffer[ 0 ] ^= 0x3; + purb->status = STATUS_SUCCESS; + break; + } + else if( psetup->bmRequestType == 0x23 \ + && psetup->bRequest == USB_REQ_SET_FEATURE ) + { + //reset port + if( psetup->wValue != USB_PORT_FEAT_RESET ) + { + purb->status = STATUS_INVALID_PARAMETER; + ehci_dbg_print( DBGLVL_MAXIMUM, ("ehci_rh_submit_urb(): set feature with wValue=0x%x\n", psetup->wValue ) ); + break; + } + + i = EHCI_PORTSC + 4 * ( psetup->wIndex - 1 ); // USBPORTSC1; + + ptimer = alloc_timer_svc( &dev_mgr->timer_svc_pool, 1 ); + ptimer->threshold = 0; // within [ 50ms, 60ms ], one tick is 10 ms + ptimer->context = ( ULONG )purb; + ptimer->pdev = pdev; + ptimer->func = rh_timer_svc_reset_port_completion; + + //start the timer + pdev->ref_count += 2; //one for timer and one for purb + + status = EHCI_READ_PORT_ULONG( ( PULONG ) ( ehci->port_base + i ) ); + usb_dbg_print( DBGLVL_MAXIMUM, ( "ehci_rh_submit_urb(): reset port, port%d=0x%x\n", psetup->wIndex, status ) ); + InsertTailList(&dev_mgr->timer_svc_list, &ptimer->timer_svc_link ); + purb->status = STATUS_PENDING; + } + else + { + purb->status = STATUS_INVALID_PARAMETER; + } + break; + } + case USB_ENDPOINT_XFER_INT: + { + ptimer = alloc_timer_svc( &dev_mgr->timer_svc_pool, 1 ); + ptimer->threshold = RH_INTERVAL; + ptimer->context = ( ULONG )purb; + ptimer->pdev = pdev; + ptimer->func = rh_timer_svc_int_completion; + + //start the timer + InsertTailList(&dev_mgr->timer_svc_list, &ptimer->timer_svc_link ); + + usb_dbg_print( DBGLVL_MAXIMUM, ( "ehci_rh_submit_urb(): current rh's ref_count=0x%x\n", pdev->ref_count ) ); + pdev->ref_count += 2; //one for timer and one for purb + + purb->status = STATUS_PENDING; + break; + } + case USB_ENDPOINT_XFER_BULK: + case USB_ENDPOINT_XFER_ISOC: + default: + { + purb->status = STATUS_INVALID_PARAMETER; + break; + } + } + unlock_dev( pdev, FALSE ); + KeReleaseSpinLock( &dev_mgr->timer_svc_list_lock, old_irql ); + return purb->status; +} + +//must have rh dev_lock acquired +BOOL +ehci_rh_reset_port( +PHCD hcd, +UCHAR port_idx +) +{ + ULONG i; + PEHCI_DEV ehci; + ULONG status; + UCHAR port_count; + + if( hcd == NULL ) + return FALSE; + + ehci = ehci_from_hcd( hcd ); + port_count = ( UCHAR )( ( PEHCI_HCS_CONTENT )&ehci->ehci_caps.hcs_params )->port_count; + + if( port_idx < 1 || port_idx > port_count ) + return FALSE; + + i = ( ULONG )( EHCI_PORTSC + 4 * ( port_idx - 1 ) ); + + // assert the reset signal,(implicitly disable the port) + SET_RH2_PORTSTAT( i, PORT_PR ); + + usb_wait_ms_dpc( 50 ); + // clear the reset signal, delay port enable till clearing port feature + CLR_RH2_PORTSTAT( i, PORT_PR ); + + // wait the port stable + usb_wait_ms_dpc( 2 ); + + status = EHCI_READ_PORT_ULONG( ( PULONG )( ehci->port_base + i ) ); + if( !( status & PORT_PE ) ) + { + // release the ownership from ehci to companion hc + status |= PORT_OWNER; + EHCI_WRITE_PORT_ULONG( ( PULONG )( ehci->port_base + i ), status ); + // the host controller will set PORTSC automatically + return FALSE; + } + usb_wait_us_dpc( 10 ); + // SET_RH_PORTSTAT( i, PORT_PE ); + + //recovery time 10ms + usb_wait_ms_dpc( 10 ); + + // clear PORT_PEC and PORT_PCC + SET_RH2_PORTSTAT( i, 0x0a ); + + status = EHCI_READ_PORT_ULONG( ( PULONG )( ehci->port_base + i ) ); + usb_dbg_print( DBGLVL_MAXIMUM, ( "ehci_rh_reset_port(): status after written=0x%x\n", status ) ); + return TRUE; +} + +NTSTATUS +ehci_dispatch_irp( +IN PDEVICE_OBJECT DeviceObject, +IN PIRP irp +) +{ + PEHCI_DEVICE_EXTENSION pdev_ext; + PUSB_DEV_MANAGER dev_mgr; + PEHCI_DEV ehci; + + pdev_ext = DeviceObject->DeviceExtension; + ehci = pdev_ext->ehci; + + dev_mgr = ehci->hcd_interf.hcd_get_dev_mgr( &ehci->hcd_interf ); + return dev_mgr_dispatch( dev_mgr, irp ); +} + +//the following are for hcd interface methods +VOID +ehci_set_dev_mgr( +PHCD hcd, +PUSB_DEV_MANAGER dev_mgr +) + +{ + hcd->dev_mgr = dev_mgr; +} + +PUSB_DEV_MANAGER +ehci_get_dev_mgr( +PHCD hcd +) +{ + return hcd->dev_mgr; +} + +ULONG +ehci_get_type( +PHCD hcd +) +{ + return HCD_TYPE_EHCI; // ( hcd->flags & HCD_TYPE_MASK ); +} + +VOID +ehci_set_id( +PHCD hcd, +UCHAR id +) +{ + hcd->flags &= ~HCD_ID_MASK; + hcd->flags |= ( HCD_ID_MASK & id ); +} + +UCHAR +ehci_get_id( +PHCD hcd +) +{ + return ( UCHAR )( hcd->flags & HCD_ID_MASK ); +} + + +UCHAR +ehci_alloc_addr( +PHCD hcd +) +{ + LONG i; + if( hcd == NULL ) + return 0; + + for( i = 1; i < MAX_DEVS; i ++ ) + { + if( hcd->dev_addr_map[ i >> 3 ] & ( 1 << ( i & 7 ) ) ) + { + continue; + } + else + { + break; + } + } + + if( i >= MAX_DEVS ) + return 0xff; + + hcd->dev_addr_map[ i >> 3 ] |= ( 1 << ( i & 7 ) ); + hcd->conn_count++; + return ( BYTE )i; +} + +VOID +ehci_free_addr( +PHCD hcd, +UCHAR addr +) +{ + if( addr & 0x80 ) + return; + + if( hcd == NULL ) + return; + + hcd->dev_addr_map[ addr >> 3 ] &= ~( 1 << ( addr & 7 ) ); + return; + +} + +NTSTATUS +ehci_submit_urb2( +PHCD hcd, +PUSB_DEV pdev, +PUSB_ENDPOINT pendp, +PURB purb ) +{ + return ehci_submit_urb( ehci_from_hcd( hcd ), pdev, pendp, purb ); +} + +PUSB_DEV +ehci_get_root_hub( +PHCD hcd +) +{ + return ehci_from_hcd( hcd )->root_hub; +} +VOID +ehci_set_root_hub( +PHCD hcd, +PUSB_DEV root_hub +) +{ + if( hcd == NULL || root_hub == NULL ) + return; + ehci_from_hcd( hcd )->root_hub = root_hub; + return; +} + +BOOL +ehci_remove_device2( +PHCD hcd, +PUSB_DEV pdev +) +{ + if( hcd == NULL || pdev == NULL ) + return FALSE; + + return ehci_remove_device( ehci_from_hcd( hcd ), pdev ); +} + +BOOL +ehci_hcd_release( +PHCD hcd +) +{ + PEHCI_DEV ehci; + PEHCI_DEVICE_EXTENSION pdev_ext; + + if( hcd == NULL ) + return FALSE; + + ehci = ehci_from_hcd( hcd ); + pdev_ext = ehci->pdev_ext; + return ehci_release( pdev_ext->pdev_obj ); +} + +NTSTATUS +ehci_cancel_urb2( +PHCD hcd, +PUSB_DEV pdev, +PUSB_ENDPOINT pendp, +PURB purb +) +{ + PEHCI_DEV ehci; + if( hcd == NULL ) + return STATUS_INVALID_PARAMETER; + + ehci = ehci_from_hcd( hcd ); + return ehci_cancel_urb( ehci, pdev, pendp, purb ); +} +BOOL +ehci_rh_get_dev_change( +PHCD hcd, +PBYTE buf +) //must have the rh dev_lock acquired +{ + PEHCI_DEV ehci; + LONG port_count, i; + ULONG status; + + if( hcd == NULL ) + return FALSE; + + ehci = ehci_from_hcd( hcd ); + port_count = HCS_N_PORTS( ehci->ehci_caps.hcs_params ); + for( i = 0; i < port_count; i++ ) + { + status = EHCI_READ_PORT_ULONG( ( PULONG )( ehci->port_base + EHCI_PORTSC + ( i << 2 ) ) ); + ehci_dbg_print( DBGLVL_MAXIMUM, ( "ehci_rh_get_dev_change(): erh port%d status=0x%x\n", i, status ) ); + + if( status & ( PORT_PEC | PORT_CSC | PORT_OCC ) ) + { + buf[ ( i + 1 ) >> 3 ] |= ( 1 << ( ( i + 1 ) & 7 ) ); + } + } + return TRUE; +} + +NTSTATUS +ehci_hcd_dispatch( +PHCD hcd, +LONG disp_code, +PVOID param +) +{ + PEHCI_DEV ehci; + + if( hcd == NULL ) + return STATUS_INVALID_PARAMETER; + ehci = ehci_from_hcd( hcd ); + switch( disp_code ) + { + case HCD_DISP_READ_PORT_COUNT: + { + if( param == NULL ) + return STATUS_INVALID_PARAMETER; + *( ( PUCHAR )param ) = ( UCHAR )HCS_N_PORTS( ehci->ehci_caps.hcs_params ); + return STATUS_SUCCESS; + } + case HCD_DISP_READ_RH_DEV_CHANGE: + { + if( ehci_rh_get_dev_change( hcd, param ) == FALSE ) + return STATUS_INVALID_PARAMETER; + return STATUS_SUCCESS; + } + } + return STATUS_NOT_IMPLEMENTED; +} + +//------------------------------------------------------------------------------ +// EHCI routines follows +// +VOID +ehci_init_int8_qh( +PEHCI_QH_CONTENT qh +); + +BOOLEAN +ehci_cal_cpu_freq( +PVOID context +) +{ + usb_cal_cpu_freq(); + return TRUE; +} + +PDEVICE_OBJECT +ehci_probe( +PDRIVER_OBJECT drvr_obj, +PUNICODE_STRING reg_path, +PUSB_DEV_MANAGER dev_mgr +) +{ + LONG bus, i, j, ret; + PCI_SLOT_NUMBER slot_num; + PPCI_COMMON_CONFIG pci_config; + PDEVICE_OBJECT pdev; + BYTE buffer[ sizeof( PCI_COMMON_CONFIG ) ]; + PEHCI_DEVICE_EXTENSION pdev_ext; + + slot_num.u.AsULONG = 0; + pci_config = ( PPCI_COMMON_CONFIG ) buffer; + pdev = NULL; + + //scan the bus to find ehci controller + for( bus = 0; bus < 2; bus++ ) /*enum only bus0 and bus1*/ + { + for( i = 0; i < PCI_MAX_DEVICES; i++ ) + { + slot_num.u.bits.DeviceNumber = i; + for( j = 0; j < PCI_MAX_FUNCTIONS; j++ ) + { + slot_num.u.bits.FunctionNumber = j; + + ret = HalGetBusData( + PCIConfiguration, + bus, + slot_num.u.AsULONG, + pci_config, + PCI_COMMON_HDR_LENGTH ); + + if( ret == 0 ) /*no this bus*/ + break; + + if( ret == 2 ) /*no device on the slot*/ + break; + + if( pci_config->BaseClass == 0x0c && pci_config->SubClass == 0x03 && pci_config->ProgIf == 0x20 ) + { + //well, we find our usb host controller( EHCI ), create device + pdev = ehci_alloc( drvr_obj, reg_path, ( ( bus << 8 ) | ( i << 3 ) | j ), dev_mgr ); + + if( !pdev ) + continue; + } + } + + if( ret == 0 ) + break; + } + } + + if( pdev ) + { + pdev_ext = pdev->DeviceExtension; + if( pdev_ext ) + { + // acquire higher irql to eliminate pre-empty + KeSynchronizeExecution( pdev_ext->ehci_int, ehci_cal_cpu_freq, NULL ); + } + } + return NULL; +} + +PDEVICE_OBJECT +ehci_alloc( +PDRIVER_OBJECT drvr_obj, +PUNICODE_STRING reg_path, +ULONG bus_addr, +PUSB_DEV_MANAGER dev_mgr +) +{ + + LONG frd_num, prd_num; + UCHAR buffer[ PCI_COMMON_HDR_LENGTH ]; + PDEVICE_OBJECT pdev; + PEHCI_DEVICE_EXTENSION pdev_ext; + ULONG vector, addr_space; + LONG bus, i; + KIRQL irql; + KAFFINITY affinity; + UCHAR hcd_id; + + DEVICE_DESCRIPTION dev_desc; + CM_PARTIAL_RESOURCE_DESCRIPTOR *pprd; + PCI_SLOT_NUMBER slot_num; + NTSTATUS status; + + + pdev = ehci_create_device( drvr_obj, dev_mgr ); + + if( pdev == NULL ) + return NULL; + + pdev_ext = pdev->DeviceExtension; + + pdev_ext->pci_addr = bus_addr; + bus = ( bus_addr >> 8 ); + + slot_num.u.AsULONG = 0; + slot_num.u.bits.DeviceNumber = ( ( bus_addr & 0xff ) >> 3 ); + slot_num.u.bits.FunctionNumber = ( bus_addr & 0x07 ); + + //now create adapter object + RtlZeroMemory( &dev_desc, sizeof( dev_desc ) ); + + dev_desc.Version = DEVICE_DESCRIPTION_VERSION; + dev_desc.Master = TRUE; + dev_desc.ScatterGather = TRUE; + dev_desc.Dma32BitAddresses = TRUE; + dev_desc.BusNumber = bus; + dev_desc.InterfaceType = PCIBus; + dev_desc.MaximumLength = EHCI_MAX_SIZE_TRANSFER; + + pdev_ext->map_regs = 2; // we do not use it seriously + + pdev_ext->padapter = HalGetAdapter( &dev_desc, + &pdev_ext->map_regs); + + ehci_dbg_print( DBGLVL_MAXIMUM, ( "ehci_alloc(): padapter=0x%x\n", pdev_ext->padapter ) ); + if( pdev_ext->padapter == NULL ) + { + //fatal error + ehci_delete_device( pdev ); + return NULL; + } + + DbgPrint("ehci_alloc(): reg_path=0x%x, \n \ + ehci_alloc(): PCIBus=0x%x, bus=0x%x, bus_addr=0x%x \n \ + ehci_alloc(): slot_num=0x%x, &res_list=0x%x \n \ + ehci_alloc(): adapter=0x%x \n", \ + ( DWORD )reg_path, \ + ( DWORD )PCIBus, \ + ( DWORD )bus, \ + ( DWORD )bus_addr,\ + ( DWORD )slot_num.u.AsULONG, \ + ( DWORD )&pdev_ext->res_list, \ + pdev_ext->padapter); + + //let's allocate resources for this device + DbgPrint( "ehci_alloc(): about to assign slot res\n" ); + if( ( status = HalAssignSlotResources( + reg_path, + NULL, //no class name yet + drvr_obj, + NULL, //no support of another ehci controller + PCIBus, + bus, + slot_num.u.AsULONG, + &pdev_ext->res_list ) ) + != STATUS_SUCCESS ) + { + DbgPrint( "ehci_alloc(): error assign slot res, 0x%x\n", status ); + release_adapter( pdev_ext->padapter ); + pdev_ext->padapter = NULL; + ehci_delete_device( pdev ); + return NULL; + } + + //parse the resource list + for( frd_num = 0; frd_num < ( LONG )pdev_ext->res_list->Count; frd_num ++ ) + { + for( prd_num = 0; prd_num < ( LONG )pdev_ext->res_list->List[ frd_num ].PartialResourceList.Count; prd_num ++ ) + { + pprd = &pdev_ext->res_list->List[ frd_num ].PartialResourceList.PartialDescriptors[ prd_num ]; + if( pprd->Type == CmResourceTypePort ) + { + RtlCopyMemory( &pdev_ext->res_port, &pprd->u.Port, sizeof( pprd->u.Port ) ); + + } + else if( pprd->Type == CmResourceTypeInterrupt ) + { + RtlCopyMemory( &pdev_ext->res_interrupt, &pprd->u.Interrupt, sizeof( pprd->u.Interrupt ) ); + } + else if( pprd->Type == CmResourceTypeMemory ) + { + RtlCopyMemory( &pdev_ext->res_memory, &pprd->u.Memory, sizeof( pprd->u.Memory ) ); + } + } + } + + //for port, translate them to system address + addr_space = 0; + if( HalTranslateBusAddress( + PCIBus, + bus, + pdev_ext->res_port.Start, + &addr_space, //io space + &pdev_ext->ehci->ehci_reg_base + ) + != ( BOOLEAN )TRUE ) + { + DbgPrint( "ehci_alloc(): error, can not translate bus address\n" ); + release_adapter( pdev_ext->padapter ); + pdev_ext->padapter = NULL; + ehci_delete_device( pdev ); + return NULL; + } + + DbgPrint( "ehci_alloc(): address space=0x%x\n, reg_base=0x%x\n", \ + addr_space, pdev_ext->ehci->ehci_reg_base.u.LowPart ); + + if( addr_space == 0 ) + { + //port has been mapped to memory space + pdev_ext->ehci->port_mapped = TRUE; + pdev_ext->ehci->port_base = ( PBYTE )MmMapIoSpace( + pdev_ext->ehci->ehci_reg_base, + pdev_ext->res_port.Length, + FALSE ); + + //fatal error can not map the registers + if( pdev_ext->ehci->port_base == NULL ) + { + release_adapter( pdev_ext->padapter ); + pdev_ext->padapter = NULL; + ehci_delete_device( pdev ); + return NULL; + } + } + else + { + //io space + pdev_ext->ehci->port_mapped = FALSE; + pdev_ext->ehci->port_base = ( PBYTE )pdev_ext->ehci->ehci_reg_base.LowPart; + } + + //before we connect the interrupt, we have to init ehci + pdev_ext->ehci->pdev_ext = pdev_ext; + + //init ehci_caps + // i = ( ( PEHCI_HCS_CONTENT )( &pdev_ext->ehci->ehci_caps.hcs_params ) )->length; + + ehci_get_capabilities( pdev_ext->ehci, pdev_ext->ehci->port_base ); + i = pdev_ext->ehci->ehci_caps.length; + pdev_ext->ehci->port_base += i; + + if( ehci_init_schedule( pdev_ext->ehci, pdev_ext->padapter ) == FALSE ) + { + release_adapter( pdev_ext->padapter ); + pdev_ext->padapter = NULL; + ehci_delete_device( pdev ); + return NULL; + } + + InitializeListHead( &pdev_ext->ehci->urb_list ); + KeInitializeSpinLock( &pdev_ext->ehci->pending_endp_list_lock ); + InitializeListHead( &pdev_ext->ehci->pending_endp_list ); + + ehci_dbg_print( DBGLVL_MAXIMUM, ( "ehci_alloc(): pending_endp_list=0x%x\n", \ + &pdev_ext->ehci->pending_endp_list ) ); + + init_pending_endp_pool( &pdev_ext->ehci->pending_endp_pool ); + + KeInitializeTimer( &pdev_ext->ehci->reset_timer ); + + vector = HalGetInterruptVector( + PCIBus, + bus, + pdev_ext->res_interrupt.level, + pdev_ext->res_interrupt.vector, + &irql, + &affinity); + + //connect the interrupt + DbgPrint( "ehci_alloc(): the int=0x%x\n", vector ); + if( IoConnectInterrupt( + &pdev_ext->ehci_int, + ehci_isr, + pdev_ext->ehci, + NULL, //&pdev_ext->ehci->frame_list_lock, + vector, + irql, + irql, + LevelSensitive, + TRUE, //share the vector + affinity, + FALSE ) //No float save + != STATUS_SUCCESS ) + { + ehci_release( pdev ); + return NULL; + } + + KeInitializeDpc( &pdev_ext->ehci_dpc, + ehci_dpc_callback, + ( PVOID )pdev_ext->ehci ); + + return pdev; +} + +PDEVICE_OBJECT +ehci_create_device( +PDRIVER_OBJECT drvr_obj, +PUSB_DEV_MANAGER dev_mgr +) +{ + NTSTATUS status; + PDEVICE_OBJECT pdev; + PEHCI_DEVICE_EXTENSION pdev_ext; + + UNICODE_STRING dev_name; + UNICODE_STRING symb_name; + + STRING string, another_string; + CHAR str_dev_name[ 64 ], str_symb_name[ 64 ]; + UCHAR hcd_id; + + if( drvr_obj == NULL ) + return NULL; + + //note: hcd count wont increment till the hcd is registered in dev_mgr + sprintf( str_dev_name, "%s%d", EHCI_DEVICE_NAME, dev_mgr->hcd_count ); + sprintf( str_symb_name, "%s%d",EHCI_DOS_DEVICE_NAME, dev_mgr->hcd_count ); + + RtlInitString( &string, str_dev_name ); + RtlAnsiStringToUnicodeString( &dev_name, &string, TRUE ); + + pdev = NULL; + status = IoCreateDevice( + drvr_obj, + sizeof( EHCI_DEVICE_EXTENSION ) + sizeof( EHCI_DEV ), + &dev_name, + FILE_EHCI_DEV_TYPE, + 0, + FALSE, + &pdev); + + if( status != STATUS_SUCCESS || pdev == NULL ) + { + RtlFreeUnicodeString( &dev_name ); + ehci_dbg_print( DBGLVL_MAXIMUM, ( "ehci_create_device(): error create device 0x%x\n", status ) ); + return NULL; + } + + pdev_ext = pdev->DeviceExtension; + RtlZeroMemory( + pdev_ext, + sizeof( EHCI_DEVICE_EXTENSION ) + + sizeof( EHCI_DEV) ); + + pdev_ext->dev_ext_hdr.type = NTDEV_TYPE_HCD; + pdev_ext->dev_ext_hdr.dispatch = ehci_dispatch_irp; + pdev_ext->dev_ext_hdr.start_io = NULL; //we do not support startio + pdev_ext->dev_ext_hdr.dev_mgr = dev_mgr; + + pdev_ext->pdev_obj = pdev; + pdev_ext->pdrvr_obj = drvr_obj; + + pdev_ext->ehci = ( PEHCI_DEV ) &( pdev_ext[ 1 ] ); + + RtlInitString( &another_string, str_symb_name ); + RtlAnsiStringToUnicodeString( &symb_name, &another_string, TRUE ); + //RtlInitUnicodeString( &symb_name, DOS_DEVICE_NAME ); + + IoCreateSymbolicLink( &symb_name, &dev_name ); + + ehci_dbg_print( DBGLVL_MAXIMUM, ( "ehci_create_device(): dev=0x%x\n, pdev_ext= 0x%x, ehci=0x%x, dev_mgr=0x%x\n", \ + pdev,\ + pdev_ext,\ + pdev_ext->ehci, \ + dev_mgr ) ); + + RtlFreeUnicodeString( &dev_name ); + RtlFreeUnicodeString( &symb_name ); + + //register with dev_mgr though it is not initilized + ehci_init_hcd_interface( pdev_ext->ehci ); + hcd_id = dev_mgr_register_hcd( dev_mgr, &pdev_ext->ehci->hcd_interf ); + + pdev_ext->ehci->hcd_interf.hcd_set_id( &pdev_ext->ehci->hcd_interf, hcd_id ); + pdev_ext->ehci->hcd_interf.hcd_set_dev_mgr( &pdev_ext->ehci->hcd_interf, dev_mgr ); + + return pdev; + +} + +VOID +ehci_init_hcd_interface( +PEHCI_DEV ehci +) +{ + ehci->hcd_interf.hcd_set_dev_mgr = ehci_set_dev_mgr; + ehci->hcd_interf.hcd_get_dev_mgr = ehci_get_dev_mgr; + ehci->hcd_interf.hcd_get_type = ehci_get_type; + ehci->hcd_interf.hcd_set_id = ehci_set_id; + ehci->hcd_interf.hcd_get_id = ehci_get_id; + ehci->hcd_interf.hcd_alloc_addr = ehci_alloc_addr; + ehci->hcd_interf.hcd_free_addr = ehci_free_addr; + ehci->hcd_interf.hcd_submit_urb = ehci_submit_urb2; + ehci->hcd_interf.hcd_generic_urb_completion = ehci_generic_urb_completion; + ehci->hcd_interf.hcd_get_root_hub = ehci_get_root_hub; + ehci->hcd_interf.hcd_set_root_hub = ehci_set_root_hub; + ehci->hcd_interf.hcd_remove_device = ehci_remove_device2; + ehci->hcd_interf.hcd_rh_reset_port = ehci_rh_reset_port; + ehci->hcd_interf.hcd_release = ehci_hcd_release; + ehci->hcd_interf.hcd_cancel_urb = ehci_cancel_urb2; + ehci->hcd_interf.hcd_start = ehci_start; + ehci->hcd_interf.hcd_dispatch = ehci_hcd_dispatch; + + ehci->hcd_interf.flags = HCD_TYPE_EHCI; //hcd types | hcd id +} + +BOOL +ehci_init_schedule( +PEHCI_DEV ehci, +PADAPTER_OBJECT padapter +) +{ + PEHCI_DEVICE_EXTENSION pdev_ext; + BOOL ret; + LONG i; + PEHCI_QH_CONTENT pqh_content; + PEHCI_QH pqh; + PEHCI_QTD ptd; + PEHCI_ELEM_LINKS pelnk; + PEHCI_FSTN pfstn; + + if( ehci == NULL ) + return FALSE; + + pdev_ext = ehci->pdev_ext; + if( pdev_ext == NULL ) + return FALSE; + + // padapter = pdev_ext->padapter; + if( ehci->frame_count == 0 ) + ehci->frame_count = EHCI_DEFAULT_FRAMES; + + // allocate pools + for( i = 0; i < 5; i++ ) + { + ret = elem_pool_init_pool( &ehci->elem_pools[ i ], i, padapter ); + if( ret == FALSE ) + break; + } + + if( ret == FALSE ) + { + i--; + for( ; i >= 0; i-- ) + elem_pool_destroy_pool( &ehci->elem_pools[ i ] ); + return FALSE; + } + + // allocate periodic frame list + ehci->frame_list = \ + HalAllocateCommonBuffer( + padapter, + sizeof(ULONG) * ehci->frame_count, + &ehci->frame_list_phys_addr, + FALSE); + if( ehci->frame_list == NULL ) + goto ERROR_OUT; + + RtlZeroMemory( ehci->frame_list, sizeof( ULONG ) * ehci->frame_count ); + ehci->frame_list_cpu = usb_alloc_mem( NonPagedPool, sizeof( LIST_HEAD ) * ehci->frame_count ); + + if( ehci->frame_list_cpu == NULL ) + goto ERROR_OUT; + + for( i = 0; i < ( LONG )ehci->frame_count; i++ ) + { + InitializeListHead( &ehci->frame_list_cpu[ i ].td_link ); + } + + for( i = 0; i < 8; i++ ) + { + InitializeListHead( &ehci->periodic_list_cpu[ i ] ); + } + + InitializeListHead( &ehci->async_list_cpu ); + + // init frame band budget array + ehci->frame_bw = usb_alloc_mem( NonPagedPool, sizeof( USHORT ) * ehci->frame_count * 8 ); + if( ehci->frame_bw == NULL ) + goto ERROR_OUT; + + for( i = 0; i < ( LONG )ehci->frame_count * 8; i++ ) + { + ehci->frame_bw[ i ] = EHCI_MAX_SYNC_BUS_TIME; + } + ehci->min_bw = EHCI_MAX_SYNC_BUS_TIME; + + // chain the 1ms interrupt qh to the schedule + if( ( pelnk = elem_pool_alloc_elem( &ehci->elem_pools[ EHCI_QH_POOL_IDX ] ) ) == NULL ) + goto ERROR_OUT; + + pqh_content = ( PEHCI_QH_CONTENT )( ( ULONG )pelnk->phys_part & PHYS_PART_ADDR_MASK ); + ehci_init_int8_qh( pqh_content ); + + // chain qh to the shadow list + InsertTailList( &ehci->periodic_list_cpu[ EHCI_SCHED_INT8_INDEX ], &pelnk->sched_link ); + + // chain it to the periodic schedule, we use it as a docking point + // for req of 8- uframes request + pqh = ( PEHCI_QH )pqh_content; + + for( i = 0; i < ( LONG )ehci->frame_count; i++ ) + { + ehci->frame_list[ i ] = pqh->phys_addr; + } + + // allocate fstn + /*if( ( pelnk = elem_pool_alloc_elem( &ehci->elem_pools[ EHCI_FSTN_POOL_IDX ] ) ) == NULL ) + goto ERROR_OUT; + + pfstn = ( PEHCI_FSTN )( ( ULONG )pelnk->phys_part & PHYS_PART_ADDR_MASK ); + pfstn->hw_next = EHCI_PTR_TERM; + pfstn->hw_prev = EHCI_PTR_TERM | ( INIT_LIST_FLAG_QH << 1 ); + InsertTailList( &ehci->periodic_list_cpu[ EHCI_SCHED_FSTN_INDEX ], &pelnk->sched_link ); + pqh->hw_next = pfstn->phys_addr;*/ + + // allocate for async list head + if( ( pelnk = elem_pool_alloc_elem( &ehci->elem_pools[ EHCI_QH_POOL_IDX ] ) ) == NULL ) + goto ERROR_OUT; + + // init the async list head + pqh = ( PEHCI_QH )( ( ULONG )pelnk->phys_part & PHYS_PART_ADDR_MASK ); + pqh_content = ( PEHCI_QH_CONTENT )pqh; + ehci_init_int8_qh( pqh_content ); + pqh->hw_next = pqh->phys_addr; + pqh_content->s_mask = 0; + pqh_content->is_async_head = 1; + pqh_content->endp_addr = 0; + ehci->skel_async_qh = pqh; + InsertTailList( &ehci->async_list_cpu, &pqh->elem_head_link->sched_link ); + return TRUE; + +ERROR_OUT: + ehci_destroy_schedule( ehci ); + return FALSE; +} + +BOOL +ehci_destroy_schedule( +PEHCI_DEV ehci +) +{ + LONG i; + if( ehci == NULL ) + return FALSE; + + if( ehci->frame_bw ) + usb_free_mem( ehci->frame_bw ); + ehci->frame_bw = NULL; + + if( ehci->frame_list_cpu ) + usb_free_mem( ehci->frame_list_cpu ); + ehci->frame_list_cpu = NULL; + + if( ehci->frame_list ) + HalFreeCommonBuffer( \ + ehci->pdev_ext->padapter, + sizeof(ULONG) * ehci->frame_count, + ehci->frame_list_phys_addr, + ehci->frame_list, + FALSE ); + + ehci->frame_list = NULL; + ehci->frame_list_phys_addr.LowPart = 0; + ehci->frame_list_phys_addr.HighPart = 0; + + for( i = 0; i < 5; i++ ) + elem_pool_destroy_pool( &ehci->elem_pools[ i ] ); + + return TRUE; +} + +VOID +ehci_init_int8_qh( +PEHCI_QH_CONTENT qh +) +{ + if( qh == NULL ) + return; + // DWORD 0 + qh->terminal = EHCI_PTR_TERM; + qh->ptr_type = 0; + qh->reserved = 0; + qh->next_link = 0; + + // DWORD 1 + qh->dev_addr = 126; // a fake addr + qh->inactive = 0; + qh->endp_addr = 1; // a fake endp + qh->endp_spd = USB_SPEED_HIGH; + qh->data_toggle = 0; + qh->is_async_head = 0; + qh->max_packet_size = 64; + qh->is_ctrl_endp = 0; + qh->reload_counter = 0; + + // DWORD 2 + qh->s_mask = 0x80; // we are interrupt qh + qh->c_mask = 0; + qh->hub_addr = 0; + qh->port_idx = 0; + qh->mult = 1; + + // DWORD 3 + qh->cur_qtd_ptr = 0; // a terminal + + // overlay + // !active and !halted + RtlZeroMemory( &qh->cur_qtd, get_elem_phys_part_size( INIT_LIST_FLAG_QTD ) ); + qh->cur_qtd.alt_terminal = 1; // don't use this + qh->cur_qtd.terminal = 1; + qh->cur_qtd.status = QTD_STS_HALT; +} + +VOID +ehci_get_capabilities( +PEHCI_DEV ehci, +PBYTE base +) +// fetch capabilities register from ehci +{ + NTSTATUS status; + PEHCI_CAPS pcap; + PEHCI_HCS_CONTENT phcs; + LONG i; + + if( ehci == NULL ) + return; + + pcap = &ehci->ehci_caps; + pcap->length = EHCI_READ_PORT_UCHAR( ( PUCHAR )( base + 0 ) ); + pcap->reserved = EHCI_READ_PORT_UCHAR( ( PUCHAR )( base + 1 ) ); + pcap->hci_version = EHCI_READ_PORT_USHORT( ( PUSHORT )( base + 2 ) ); + pcap->hcs_params = EHCI_READ_PORT_ULONG( ( PULONG )( base + 4 ) ); + pcap->hcc_params = EHCI_READ_PORT_ULONG( ( PULONG )( base + 8 ) ); + + phcs = ( PEHCI_HCS_CONTENT )&pcap->hcs_params; + if( phcs->port_rout_rules ) + { + for( i = 0; i < 8; i++ ) + pcap->portroute [ i ] = EHCI_READ_PORT_UCHAR( ( PUCHAR )( base + 12 + i ) ); + } + return; +} + +BOOL +ehci_delete_device( +PDEVICE_OBJECT pdev +) +{ + STRING string; + UNICODE_STRING symb_name; + CHAR str_symb_name[ 64 ]; + PEHCI_DEVICE_EXTENSION pdev_ext; + + if( pdev == NULL ) + return FALSE; + + pdev_ext = pdev->DeviceExtension; + + sprintf( str_symb_name, + "%s%d", + EHCI_DOS_DEVICE_NAME, + pdev_ext->ehci->hcd_interf.hcd_get_id( &pdev_ext->ehci->hcd_interf ) ); + + RtlInitString( &string, str_symb_name ); + RtlAnsiStringToUnicodeString( &symb_name, &string, TRUE ); + IoDeleteSymbolicLink( &symb_name ); + RtlFreeUnicodeString( &symb_name ); + + if( pdev_ext->res_list ) + ExFreePool( pdev_ext->res_list ); // not allocated by usb_alloc_mem + + IoDeleteDevice( pdev ); + ehci_dbg_print( DBGLVL_MAXIMUM, ( "ehci_delete_device(): device deleted\n" ) ); + return TRUE; +} + +VOID +ehci_stop( +PEHCI_DEV ehci +) +{ + PBYTE base; + PEHCI_USBCMD_CONTENT usbcmd; + LONG tmp; + + base = ehci->port_base; + // turn off all the interrupt + EHCI_WRITE_PORT_ULONG( ( PULONG )( base + EHCI_USBINTR ), 0 ); + tmp = EHCI_READ_PORT_ULONG( ( PULONG )( base + EHCI_USBCMD ) ); + usbcmd = ( PEHCI_USBCMD_CONTENT )&tmp; + usbcmd->run_stop = 0; + EHCI_WRITE_PORT_ULONG( ( PULONG )( base + EHCI_USBCMD ), tmp ); +} + +BOOL +ehci_release( +PDEVICE_OBJECT pdev +) +{ + PEHCI_DEVICE_EXTENSION pdev_ext; + PEHCI_DEV ehci; + KIRQL disp_level; + KIRQL old_level; + + if( pdev == NULL ) + return FALSE; + + pdev_ext = pdev->DeviceExtension; + + if( pdev_ext == NULL ) + return FALSE; + + ehci = pdev_ext->ehci; + if( ehci == NULL ) + return FALSE; + + ehci_stop( ehci ); + + if( pdev_ext->ehci_int ) + { + IoDisconnectInterrupt( pdev_ext->ehci_int ); + pdev_ext->ehci_int = NULL; + } + else + TRAP(); + destroy_pending_endp_pool( &pdev_ext->ehci->pending_endp_pool ); + + ehci_destroy_schedule( ehci ); + + release_adapter( pdev_ext->padapter ); + pdev_ext->padapter = NULL; + + ehci_delete_device( pdev ); + + return FALSE; + +} + +BOOL +ehci_start( +PHCD hcd +) +{ + ULONG tmp; + PBYTE base; + LONG i; + PEHCI_USBCMD_CONTENT usbcmd; + LARGE_INTEGER interval; + PEHCI_DEV ehci; + + if( hcd == NULL ) + return FALSE; + + ehci = struct_ptr( hcd, EHCI_DEV, hcd_interf ); + base = ehci->port_base; + + // stop the controller + tmp = EHCI_READ_PORT_ULONG( ( PULONG )( base + EHCI_USBCMD ) ); + usbcmd = ( PEHCI_USBCMD_CONTENT )&tmp; + usbcmd->run_stop = 0; + EHCI_WRITE_PORT_ULONG( ( PULONG )( base + EHCI_USBCMD ), tmp ); + + // wait the controller stop( ehci spec, 16 microframe ) + usb_wait_ms_dpc( 2 ); + + // reset the controller + usbcmd = ( PEHCI_USBCMD_CONTENT )&tmp; + usbcmd->hcreset = TRUE; + EHCI_WRITE_PORT_ULONG( ( PULONG )( base + EHCI_USBCMD ), tmp ); + + for(;;) + { + // interval.QuadPart = -100 * 10000; // 10 ms + // KeDelayExecutionThread( KernelMode, FALSE, &interval ); + KeStallExecutionProcessor( 10 ); + tmp = EHCI_READ_PORT_ULONG( ( PULONG )( base + EHCI_USBCMD ) ); + if( !usbcmd->hcreset ) + break; + } + + // prepare the registers + EHCI_WRITE_PORT_ULONG( ( PULONG )( base + EHCI_CTRLDSSEGMENT ), 0 ); + + // turn on all the int + EHCI_WRITE_PORT_ULONG( ( PULONG )( base + EHCI_USBINTR ), \ + EHCI_USBINTR_INTE | \ + EHCI_USBINTR_ERR | \ + EHCI_USBINTR_ASYNC | \ + EHCI_USBINTR_HSERR + // EHCI_USBINTR_FLROVR | \ // it is noisy + // EHCI_USBINTR_PC | // we detect it by polling + ); + // write the list base reg + EHCI_WRITE_PORT_ULONG( ( PULONG )( base + EHCI_PERIODICLISTBASE ), \ + ehci->frame_list_phys_addr.LowPart ); + + EHCI_WRITE_PORT_ULONG( ( PULONG )( base + EHCI_ASYNCLISTBASE ), \ + ehci->skel_async_qh->phys_addr & ~( 0x1f ) ); + + usbcmd->int_threshold = 1; + EHCI_WRITE_PORT_ULONG( ( PULONG )( base + EHCI_USBCMD ), tmp ); + + // let's rock + usbcmd->run_stop = 1; + EHCI_WRITE_PORT_ULONG( ( PULONG )( base + EHCI_USBCMD ), tmp ); + + // set the configuration flag + EHCI_WRITE_PORT_ULONG( ( PULONG )( base + EHCI_CONFIGFLAG ), 1 ); + + // enable the list traversaling + usbcmd->async_enable = 1; + usbcmd->periodic_enable = 1; + EHCI_WRITE_PORT_ULONG( ( PULONG )( base + EHCI_USBCMD ), tmp ); + + return TRUE; +} + +VOID +ehci_run_stop( +PEHCI_DEV ehci, +BOOL start +) +{ + PEHCI_USBCMD_CONTENT usbcmd; + PEHCI_USBSTS_CONTENT usbsts; + ULONG tmp; + PBYTE base; + + if( ehci == NULL ) + return; + + base = ehci->port_base; + usbcmd = ( PEHCI_USBCMD_CONTENT )&tmp; + usbsts = ( PEHCI_USBSTS_CONTENT )&tmp; + + tmp = EHCI_READ_PORT_ULONG( ( PULONG )( base + EHCI_USBSTS ) ); + if( start && usbsts->hc_halted == 0 ) + { + TRAP(); + usb_dbg_print( DBGLVL_MEDIUM, ( "ehci_run_stop(): WARNING: hc running, can not start again\n" ) ); + return; + } + tmp = EHCI_READ_PORT_ULONG( ( PULONG )( base + EHCI_USBCMD ) ); + usbcmd->run_stop = start ? 1 : 0; + EHCI_WRITE_PORT_ULONG( ( PULONG )( base + EHCI_USBCMD ), tmp ); + if( start ) + usb_wait_ms_dpc( 2 ); //ehci spec 16 microframes +} + +VOID +ehci_find_min_bandwidth( +PEHCI_DEV ehci +) +{ + LONG i; + if( ehci == NULL ) + return; + + for( i = 0; i < ( LONG )( ehci->frame_count << 3 ); i++ ) + { + ehci->min_bw = ehci->min_bw < ehci->frame_bw[ i ] ? ehci->min_bw : ehci->frame_bw[ i ] ; + } + return; +} + +BOOL +ehci_claim_bw_for_int( +PEHCI_DEV ehci, +PURB purb, +BOOL release +) +// should have pending_endp_list_lock acquired, and purb->pipe prepared +{ + PURB_HS_PIPE_CONTENT pipe_content; + LONG i, j; + ULONG interval, bus_time, ss_time, cs_time; + BOOL bw_avail; + ULONG max_packet_size; + + if( ehci == NULL || purb == NULL ) + return FALSE; + + if( purb->pipe == 0 ) + return FALSE; + + bw_avail = FALSE; + pipe_content = ( PURB_HS_PIPE_CONTENT )&purb->pipe; + interval = REAL_INTERVAL; + if( pipe_content->speed_high == 0 ) + { + // translate to high speed uframe count + interval <<= 3; + } + + if( interval > ( ehci->frame_count << 3 ) ) + interval = ( ehci->frame_count << 3 ); + + max_packet_size = ( 1 << pipe_content->max_packet_size ); + if( pipe_content->speed_high ) + { + // this is a high speed endp + + bus_time = usb_calc_bus_time( + USB_SPEED_HIGH, + ( pipe_content->trans_dir ) << 7, + FALSE, + min( purb->data_length, ( LONG )max_packet_size ) + ); + + // multiple transactions per uframe + if( purb->data_length > ( LONG )max_packet_size ) + { + bus_time *= ( purb->data_length ) / max_packet_size; + bus_time += usb_calc_bus_time( + USB_SPEED_HIGH, + ( pipe_content->trans_dir ) << 7, + FALSE, + purb->data_length % max_packet_size + ); + } + bus_time = ( bus_time + 1 ) >> 1; + + if( release ) + { + for( i = pipe_content->start_uframe + ( purb->int_start_frame << 3 ); i < ( LONG )( ehci->frame_count << 3 ); i += interval ) + { + ehci->frame_bw[ i ] += ( USHORT )bus_time; + } + goto LBL_OUT; + } + if( bus_time < ehci->min_bw ) + { + // start the poll from uframe zero of each frame + bw_avail = TRUE; + pipe_content->start_uframe = 0; + for( i = 0; i < ( LONG )( ehci->frame_count << 3 ); i += interval ) + { + ehci->frame_bw[ i ] -= ( USHORT )bus_time; + if( ehci->frame_bw[ i ] < ehci->min_bw ) + ehci->min_bw = ehci->frame_bw[ i ]; + } + } + else // bother to find a pattern + { + for( j = 0; j < ( LONG )interval; j++ ) + { + for( i = j; i < ( LONG )( ehci->frame_count << 3 ); i += interval ) + { + if( ehci->frame_bw[ i ] < bus_time ) + break; + } + if( i > ( LONG )( ehci->frame_count << 3 ) ) + { + bw_avail = TRUE; + break; + } + } + if( bw_avail ) + { + for( i = j; i < ( LONG )( ehci->frame_count << 3 ); i += interval ) + { + ehci->frame_bw[ i ] -= ( USHORT )bus_time; + if( ehci->frame_bw[ i ] < ehci->min_bw ) + ehci->min_bw = ehci->frame_bw[ i ]; + } + pipe_content->start_uframe = j & 7; + purb->int_start_frame = j >> 3; + } + } + } + else // full/low speed pipe + { + // split condition is considered + if( pipe_content->trans_dir ) + { + // an input interrupt, with handshake + // 55 is 144 - 90 + 1, turnaround time is one byte not the worst case 90 bytes, + // refer to ehci-1.0 table 4-5 p64 + ss_time = 231 * 25 / 12; + // cs_time = ( 55 * 8 + ( LONG )( ( ( 19 + 7 * 8 * purb->data_length ) / 6 ) ) ) * 25 / 12; + cs_time = ( 55 * 8 + ( LONG )( ( ( 7 * 8 * purb->data_length ) / 6 ) ) ) * 25 / 12; + } + else + { + // according to ehci-1.0 table 4-5 p64 + ss_time = ( 49 * 8 + ( LONG )( ( ( 7 * 8 * purb->data_length ) / 6 ) ) ) * 25 / 12; + // 287 = 237 + 48( handshake ) + 8( turn around time ) + cs_time = 287 * 25 / 12; + } + ss_time >>=1, cs_time >>=1; + if( release ) + { + for( i = pipe_content->start_uframe + ( purb->int_start_frame << 3 ); i < ( LONG )( ehci->frame_count << 3 ); i += interval ) + { + ehci->frame_bw[ i ] += ( USHORT )ss_time; + ehci->frame_bw[ i + 2 ] += ( USHORT )cs_time; + ehci->frame_bw[ i + 3 ] += ( USHORT )cs_time; + if( ( i & 0x07 ) != 0x06 ) + ehci->frame_bw[ i + 4 ] += ( USHORT )cs_time; + } + goto LBL_OUT; + } + if( ss_time < ehci->min_bw && cs_time < ehci->min_bw ) + { + pipe_content->start_uframe = 0; + bw_avail = TRUE; + for( i = 0; i < ( LONG )( ehci->frame_count << 3 ); i += interval ) + { + ehci->frame_bw[ i ] -= ( USHORT )ss_time; + ehci->min_bw = min( ehci->frame_bw[ i ], ehci->min_bw ); + ehci->frame_bw[ i + 2 ] -= ( USHORT )cs_time; + ehci->min_bw = min( ehci->frame_bw[ i + 2 ], ehci->min_bw ); + ehci->frame_bw[ i + 3 ] -= ( USHORT )cs_time; + ehci->min_bw = min( ehci->frame_bw[ i + 3 ], ehci->min_bw ); + if( ( i & 0x07 ) != 0x06 ) + { + ehci->frame_bw[ i + 4 ] -= ( USHORT )cs_time; + ehci->min_bw = min( ehci->frame_bw[ i + 4 ], ehci->min_bw ); + } + } + } + else + { + for( j = 0; j < ( LONG )interval; j++ ) + { + if( ( j & 0x7 )== 7 ) // start-split not allowed at this uframe + continue; + + for( i = j; i < ( LONG )( ehci->frame_count << 3 ); i += interval ) + { + if( ehci->frame_bw[ i ] < ss_time ) + break; + if( ehci->frame_bw[ i + 2 ] < cs_time ) + break; + if( ehci->frame_bw[ i + 3 ] < cs_time ) + break; + if( ( i & 0x7 ) != 6 ) + if( ehci->frame_bw[ i + 4 ] < cs_time ) + break; + } + if( i > ( LONG )( ehci->frame_count << 3 ) ) + { + bw_avail = TRUE; + break; + } + } + + pipe_content->start_uframe = j & 7; + purb->int_start_frame = j >> 3; + + for( i = j; i < ( LONG )( ehci->frame_count << 3 ); i += interval ) + { + ehci->frame_bw[ i ] -= ( USHORT )ss_time; + ehci->min_bw = min( ehci->frame_bw[ i ], ehci->min_bw ); + ehci->frame_bw[ i + 2 ] -= ( USHORT )cs_time; + ehci->min_bw = min( ehci->frame_bw[ i + 2 ], ehci->min_bw ); + ehci->frame_bw[ i + 3 ] -= ( USHORT )cs_time; + ehci->min_bw = min( ehci->frame_bw[ i + 3 ], ehci->min_bw ); + if( ( i & 0x7 ) != 6 ) + { + ehci->frame_bw[ i + 4 ] -= ( USHORT )cs_time; + ehci->min_bw = min( ehci->frame_bw[ i + 4 ], ehci->min_bw ); + } + } + } + } + +LBL_OUT: + if( !release ) + return bw_avail; + else + { + ehci_find_min_bandwidth( ehci ); + return TRUE; + } +} + +LONG +ehci_get_cache_policy( +PEHCI_DEV ehci +) +{ + PEHCI_HCC_CONTENT hcc_content; + + hcc_content = ( PEHCI_HCC_CONTENT ) &ehci->ehci_caps; + if( hcc_content->iso_sched_threshold >= 8 ) + return 16; + if( hcc_content->iso_sched_threshold == 0 ) + return 2; + return hcc_content->iso_sched_threshold + 2; +} + +// 25/12 is bus-time per bit ( 1000 / 480 ) +#define BEST_BUDGET_TIME_UFRAME ( ( 188 * 7 / 6 ) * 25 / 12 ) + +// in: 231 is sum of split token + host ipg + token, 8 is bus turn-around time, 67 is full speed data token in DATA packet +// out: 49 byte is sum of split token+ host ipg + token + host ipg + data packet +#define iso_max_data_load( dir ) ( dir == USB_DIR_IN ? \ + ( ( 188 * 8 - 231 - 8 - 67 + ( 8 - 1 ) ) / 8 ) : ( 188 - 49 ) ) + +#define iso_uframes_data_load( dir, data_load, uf_cnt ) + +BOOL +ehci_claim_bw_for_iso( +PEHCI_DEV ehci, +PURB purb, +BOOL release +) +{ + PURB_HS_PIPE_CONTENT pipe_content; + LONG i, j, k; + ULONG interval, bus_time, ss_time, cs_time, remainder; + BOOL bw_avail; + ULONG cur_uframe, start_uframe, max_time, max_packet_size; + PBYTE base; + if( ehci == NULL || purb == NULL ) + return FALSE; + + pipe_content = ( PURB_HS_PIPE_CONTENT )&purb->pipe; + base = ehci->port_base; + cur_uframe = EHCI_READ_PORT_ULONG( ( PULONG )( base + EHCI_FRINDEX ) ) + 1; + bw_avail = FALSE; + + max_packet_size = purb->params[ 0 ]; //( 1 << pipe_content->max_packet_size ); + + if( pipe_content->speed_high ) + { + interval = REAL_INTERVAL; + if( purb->iso_frame_count == 0 || purb->iso_frame_count + 2 * 8 > ( LONG )( ehci->frame_count << 3 ) ) + return FALSE; + + for( i = 0, max_time = 0; i < ( LONG )purb->iso_frame_count; i++ ) + { + bus_time = usb_calc_bus_time( + USB_SPEED_HIGH, + ( pipe_content->trans_dir ) << 7, + TRUE, + min( purb->iso_packet_desc[ i ].length, ( LONG )max_packet_size ) + ); + // NOTE: we did not use endp_mult_count here, because the comparison is enough + // to calculate the bandwidth + if( purb->iso_packet_desc[ i ].length > ( LONG )max_packet_size ) + { + // multiple transactions per uframe + bus_time *= purb->iso_packet_desc[ i ].length / max_packet_size; + bus_time += usb_calc_bus_time( + USB_SPEED_HIGH, + ( pipe_content->trans_dir ) << 7, + TRUE, + purb->iso_packet_desc[ i ].length % max_packet_size + ); + } + bus_time = ( bus_time + 1 ) >> 1; + max_time = max( bus_time, max_time ); + purb->iso_packet_desc[ i ].bus_time = bus_time; + } + + if( release ) + { + // it is a release operation + for( i = purb->iso_start_frame, k = 0; k < ( LONG )purb->iso_frame_count; k++, i = ( i + interval ) % ( ehci->frame_count << 3 ) ) + { + ehci->frame_bw[ i ] += ( USHORT )purb->iso_packet_desc[ k ].bus_time; + } + ehci_find_min_bandwidth( ehci ); + return TRUE; + } + + if( max_time < ehci->min_bw ) + { + start_uframe = cur_uframe + ehci_get_cache_policy( ehci ); // avoid cache + for( i = start_uframe, j = 0; j < ( LONG )purb->iso_frame_count; i = ( i + interval ) % ( ehci->frame_count << 3 ), j++ ) + { + ehci->frame_bw[ i ] -= ( USHORT )purb->iso_packet_desc[ j ].bus_time; + ehci->min_bw = min( ehci->frame_bw[ j ], ehci->min_bw ); + } + purb->iso_start_frame = start_uframe; + return TRUE; + } + else // max_time >= ehci->min_bw + { + for( j = 0; j < ( LONG )interval; j++ ) + { + start_uframe = cur_uframe + ehci_get_cache_policy( ehci ) + j; + for( i = start_uframe, k = 0; k < ( LONG )purb->iso_frame_count; k++, i = ( i + interval ) % ( ehci->frame_count << 3 ) ) + { + if( ehci->frame_bw[ i ] < ( USHORT )purb->iso_packet_desc[ k ].bus_time ) + { + break; + } + } + + if( k < ( LONG )purb->iso_frame_count ) + continue; + bw_avail = TRUE; + break; + } + if( bw_avail ) + { + // allocate the bandwidth + for( i = start_uframe, k = 0; k < ( LONG )purb->iso_frame_count; k++, i = ( i + interval ) % ( ehci->frame_count << 3 ) ) + { + ehci->frame_bw[ i ] -= ( USHORT )purb->iso_packet_desc[ k ].bus_time; + ehci->min_bw = min( ehci->min_bw, ehci->frame_bw[ i ] ); + } + purb->iso_start_frame = start_uframe; + } + } + } + else // not high speed endpoint + { + // split transfer + if( purb->iso_frame_count == 0 || purb->iso_frame_count + 2 > ( LONG )ehci->frame_count ) + return FALSE; + + if( max_packet_size > 1023 ) + return FALSE; + + remainder = 0; + + // + // calculate for each frame + // in: 231 is sum of split token + host ipg + token, 8 is bus turn-around time, 67 is full speed data token in DATA packet + // out: 49 byte is sum of split token+ host ipg + token + host ipg + data packet + // bit-stuffing is for high speed bus transfer + // + + if( pipe_content->trans_dir ) + { + // an input transfer, no handshake + ss_time = 231 * 25 / 12; + // cs_time = ( 231 + 8 + 67 + ( LONG )( ( ( 19 + 7 * 8 * 188 ) / 6 ) ) ) * 25 / 12; + cs_time = ( 231 + 8 + 67 + ( LONG )( ( ( 7 * 8 * 188 ) / 6 ) ) ) * 25 / 12; + } + else + { + // an output transfer according to ehci-1.0 table 4-5 p64 + // ss_time = ( 49 * 8 + ( LONG )( ( ( 19 + 7 * 8 * 188 ) / 6 ) ) ) * 25 / 12; + ss_time = ( 49 * 8 + ( LONG )( ( ( 7 * 8 * 188 ) / 6 ) ) ) * 25 / 12; + cs_time = 0; + for( i = 0; i < ( LONG )purb->iso_frame_count; i++ ) + { + // remainder = ( 49 * 8 + ( LONG )( ( ( 19 + 7 * 8 * ( purb->iso_packet_desc[ i ].length % 188 ) ) / 6 ) ) ) * 25 / 12; + remainder = ( 49 * 8 + ( LONG )( ( ( 7 * 8 * ( purb->iso_packet_desc[ i ].length % 188 ) ) / 6 ) ) ) * 25 / 12; + remainder >>= 1; + purb->iso_packet_desc[ i ].params.bus_time = ( USHORT )remainder; + } + } + + ss_time >>= 1; + cs_time >>= 1; + cur_uframe = ( cur_uframe + 7 ) & ( ~0x07 ); + + j = ehci->frame_count << 3; + if( release ) + { + if( pipe_content->trans_dir ) + { + for( i = 0; i < ( LONG )purb->iso_frame_count; i++ ) + { + ehci->frame_bw[ purb->iso_packet_desc[ i ].params.start_uframe ] += ( USHORT )ss_time; + for( k = 0; k < ( purb->iso_packet_desc[ i ].length + 187 ) / 188; k++ ) + { + ehci->frame_bw[ ( cur_uframe + 0x12 + ( i << 3 ) + k ) % j ] += ( USHORT )cs_time; + } + + // two extra complete-split + ehci->frame_bw[ ( cur_uframe + 0x12 + ( i << 3 ) + k ) % j ] += ( USHORT )cs_time; + ehci->frame_bw[ ( cur_uframe + 0x13 + ( i << 3 ) + k ) % j ] += ( USHORT )cs_time; + } + } + else + { + for( i = 0; i < ( LONG )purb->iso_frame_count; i++ ) + { + for( k = 0; k < ( purb->iso_packet_desc[ i ].length + 187 ) / 188; k++ ) + { + ehci->frame_bw[ ( cur_uframe + 0x10 + ( i << 3 ) + k ) % j ] += ( USHORT )ss_time; + } + } + } + ehci_find_min_bandwidth( ehci ); + } + + // search for available bw + if( ss_time < ehci->min_bw && cs_time < ehci->min_bw ) + { + if( pipe_content->trans_dir ) + { + for( i = 0; i < ( LONG )purb->iso_frame_count; i++ ) + { + ehci->frame_bw[ ( cur_uframe + 0x10 + ( i << 3 ) ) % j ] -= ( USHORT )ss_time; + ehci->min_bw = min( ehci->frame_bw[ ( cur_uframe + 0x10 + ( i << 3 ) ) % j ], ehci->min_bw ); + + for( k = 0; k < ( purb->iso_packet_desc[ i ].length + 187 ) / 188; k++ ) + { + ehci->frame_bw[ ( cur_uframe + 0x12 + ( i << 3 ) + k ) % j ] -= ( USHORT )cs_time; + ehci->min_bw = min( ehci->frame_bw[ ( cur_uframe + 0x12 + ( i << 3 ) + k ) %j ], ehci->min_bw ); + } + + // two extra complete-split + ehci->frame_bw[ ( cur_uframe + 0x12 + ( i << 3 ) + k ) % j ] -= ( USHORT )cs_time; + ehci->min_bw = min( ehci->frame_bw[ cur_uframe + 0x12 + ( i << 3 ) + k ], ehci->min_bw ); + ehci->frame_bw[ ( cur_uframe + 0x13 + ( i << 3 ) + k ) % j ] -= ( USHORT )cs_time; + ehci->min_bw = min( ehci->frame_bw[ cur_uframe + 0x13 + ( i << 3 ) + k ], ehci->min_bw ); + } + } + else // iso output + { + for( i = 0; i < ( LONG )purb->iso_frame_count; i++ ) + { + for( k = 0; k < ( purb->iso_packet_desc[ i ].length + 187 ) / 188; k++ ) + { + ehci->frame_bw[ ( cur_uframe + 0x10 + ( i << 3 ) + k ) % j ] -= ( USHORT )ss_time; + ehci->min_bw = min( ehci->frame_bw[ ( cur_uframe + 0x11 + ( i << 3 ) + k ) %j ], ehci->min_bw ); + } + } + } + purb->iso_start_frame = 0; + for( i = 0; i < ( LONG )purb->iso_frame_count; i++ ) + { + if( i == 0 ) + purb->iso_packet_desc[ i ].params.start_uframe = ( USHORT )( cur_uframe + 0x10 ); + else + purb->iso_packet_desc[ i ].params.start_uframe = purb->iso_packet_desc[ i - 1 ].params.start_uframe + 0x8; + } + bw_avail = TRUE; + } + else // take the pain to find one + { + BOOL large; + long temp, base; + + for( j = ( cur_uframe >> 3 ) + 2; j != ( LONG )( cur_uframe >> 3 ); j = ( j + 1 ) % ehci->frame_count ) + { + temp = 0; + + for( i = 0; i < ( LONG )purb->iso_frame_count; i++ ) + { + large = purb->iso_packet_desc[ i ].length > 579; + base = ( purb->iso_packet_desc[ i ].length + 187 ) / 188; + + if( base > 6 ) + return FALSE; + + if( pipe_content->trans_dir ) + { + // input split iso, for those large than 579, schedule it at the uframe boundary + for( temp = 0; temp < ( large == FALSE ) ? ( 8 - base - 1 ) : 1; temp++ ) + { + k = ( ( ( j + i ) << 3 ) + temp ) % ( ehci->frame_count << 3 ); + if( ehci->frame_bw[ k ] > ss_time ) + continue; + + k = base; + while( k != 0 ) + { + if( ehci->frame_bw[ ( ( ( j + i ) << 3 ) + 1 + temp + k ) % ( ehci->frame_count << 3 ) ] < cs_time ) + break; + k--; + } + if( k > 0 ) // not available + continue; + + // the first following extra cs + k = ( ( ( j + i ) << 3 ) + 2 + temp + base ) % ( ehci->frame_count << 3 ); + if( ehci->frame_bw[ k ] < cs_time ) + continue; + + if( base < 6 ) + { + // very large one does not have this second extra cs + if( ehci->frame_bw[ ( k + 1 ) % ( ehci->frame_count << 3 ) ] < cs_time ) + continue; + } + } + + if( temp == 8 - 1 - base ) // no bandwidth for ss + break; + } + else // output + { + // note: 8 - 1 - base has different meaning from the above + // it is to avoid the ss on H-Frame 7, but the above one is + // the latency of the classic bus. + for( temp = 0; temp < 8 - 1 - base; temp++ ) + { + if( ehci->frame_bw[ ( ( j + i ) << 3 ) % ( ehci->frame_count << 3 ) + temp ] > ss_time ) + continue; + + for( k = temp; k < temp + base; k++ ) + { + if( ehci->frame_bw[ ( ( j + i ) << 3 ) % ( ehci->frame_count << 3 ) + k ] < ss_time ) + break; + } + } + + if( temp == 8 - 1 - base ) + break; + } + + purb->iso_packet_desc[ i ].params.start_uframe = ( USHORT )( ( ( ( j + i ) << 3 ) + temp ) % ( ehci->frame_count << 3 ) ); + // next frame + } + if( i < ( LONG )purb->iso_frame_count ) + { + // skip to the next section + j = ( j + i ) % ( ehci->frame_count << 3 ); + continue; + } + bw_avail = TRUE; + break; + } + // FIXME: Should we claim bw for the last complete split sitd? this is not done + // yet. + if( bw_avail ) + { + if( pipe_content->trans_dir ) + { + // input iso + for( i = 0; i < ( LONG )purb->iso_frame_count; i++ ) + { + j = purb->iso_packet_desc[ i ].length; + temp = ( purb->iso_packet_desc[ i ].params.start_uframe ) % ( ehci->frame_count << 3 ); + ehci->frame_bw[ temp ] -= ( USHORT )ss_time; + for( k = 0; k < ( j + 187 ) / 188; j++ ) + { + ehci->frame_bw[ temp + 2 + k ] -= ( USHORT )cs_time; + } + ehci->frame_bw[ temp + 2 + k ] -= ( USHORT )cs_time; + if( ( j + 187 ) / 188 < 6 ) //ehci restriction + { + ehci->frame_bw[ temp + 3 + k ] -= ( USHORT )cs_time; + } + } + } + else //output iso + { + for( i = 0; i < ( LONG )purb->iso_frame_count; i++ ) + { + j = purb->iso_packet_desc[ i ].length; + temp =( purb->iso_packet_desc[ i ].params.start_uframe ) % ( ehci->frame_count << 3 ); + for( k = 0; k < j / 188; j++ ) + { + ehci->frame_bw[ temp + k ] -= ( USHORT )ss_time; + } + if( j % 188 ) + ehci->frame_bw[ temp + k ] -= purb->iso_packet_desc[ i ].params.bus_time; + } + } + } + } + } + return bw_avail; +} + +BOOL +ehci_claim_bandwidth( +PEHCI_DEV ehci, +PURB purb, +BOOL claim_bw )//true to claim band-width, false to free band-width +// should have pending_endp_list_lock acquired, and purb->pipe prepared +{ + PURB_HS_PIPE_CONTENT pipe_content; + BOOL ret; + + if( ehci == NULL || purb == NULL ) + return FALSE; + + ret = FALSE; + pipe_content = ( PURB_HS_PIPE_CONTENT )&purb->pipe; + if( pipe_content->trans_type == USB_ENDPOINT_XFER_ISOC ) + { + ret = ehci_claim_bw_for_iso( ehci, purb, claim_bw ? FALSE : TRUE ); + } + else if( pipe_content->trans_type == USB_ENDPOINT_XFER_INT ) + { + ret = ehci_claim_bw_for_int( ehci, purb, claim_bw ? FALSE : TRUE ); + } + else + TRAP(); + return ret; +} + +BOOL +ehci_can_remove( +PURB purb, +BOOL door_bell_rings, +ULONG cur_frame +) +// test if the purb can be removed from the schedule, by current frame index and +// purb states +{ + PURB_HS_PIPE_CONTENT pipe_content; + ULONG interval; + + pipe_content = ( PURB_HS_PIPE_CONTENT )&purb->pipe; + interval = REAL_INTERVAL; + + switch( purb->flags & URB_FLAG_STATE_MASK ) + { + case URB_FLAG_STATE_PENDING: + { + // not impossible + TRAP(); + break; + } + case URB_FLAG_STATE_IN_PROCESS: + { + break; + } + case URB_FLAG_STATE_DOORBELL: + { + if( ( pipe_content->trans_type == USB_ENDPOINT_XFER_BULK || \ + pipe_content->trans_type == USB_ENDPOINT_XFER_CONTROL ) && \ + door_bell_rings == TRUE ) + { + return TRUE; + } + else if( ( pipe_content->trans_type == USB_ENDPOINT_XFER_BULK || \ + pipe_content->trans_type == USB_ENDPOINT_XFER_CONTROL ) ) + { + break; + } + else + { + TRAP(); + break; + } + } + case URB_FLAG_STATE_WAIT_FRAME: + { + // need more processing + if( ( purb->flags & URB_FLAG_FORCE_CANCEL ) == 0 ) + { + TRAP(); + break; + } + if( pipe_content->trans_type == USB_ENDPOINT_XFER_INT ) + { + return door_bell_rings; + } + else // isochronous can not be canceled + { + TRAP(); + break; + } + } + } + return FALSE; +} + +NTSTATUS +ehci_remove_urb_from_schedule( +PEHCI_DEV ehci, +PURB purb +); + +static VOID +ehci_deactivate_urb( +PURB purb +) +{ + PURB_HS_PIPE_CONTENT pipe_content; + PLIST_ENTRY pthis, pnext; + PEHCI_QH_CONTENT pqh_content; + PEHCI_QTD_CONTENT pqtd_content; + + if( purb == NULL ) + return; + + pipe_content = ( PURB_HS_PIPE_CONTENT )&purb->pipe; + switch( pipe_content->trans_type ) + { + case USB_ENDPOINT_XFER_CONTROL: + case USB_ENDPOINT_XFER_BULK: + case USB_ENDPOINT_XFER_INT: + { + ListFirst( &purb->trasac_list, pthis ); + pqh_content = ( PEHCI_QH_CONTENT )qh_from_list_entry( pthis ); + ListNext( &purb->trasac_list, pthis, pnext ); + do + { + pqtd_content = ( PEHCI_QTD_CONTENT )qtd_from_list_entry( pthis ); + if( pqtd_content->status & QTD_STS_ACTIVE ) + { + pqtd_content->status &= ~QTD_STS_ACTIVE; + } + ListNext( &purb->trasac_list, pthis, pnext ); + pthis = pnext; + + }while( pthis ); + break; + } + case USB_ENDPOINT_XFER_ISOC: + { + // fall through + } + default: + TRAP(); + } + return; +} + +static VOID +ehci_insert_bulk_schedule( +PEHCI_DEV ehci, +PURB purb +) +// list head is only a handle, the qh and qtd are following it. +{ + PLIST_ENTRY list_head; + PEHCI_QH pqh, pqhprev, pqhnext; + PEHCI_QTD ptd; + PLIST_ENTRY pthis, pprev, pnext; + + if( ehci == NULL || purb == NULL ) + return; + + list_head = &purb->trasac_list; + ListFirst( list_head, pthis ); + if( pthis == NULL ) + return; + + if( elem_type_list_entry( pthis ) != INIT_LIST_FLAG_QH ) + return; + + pqh = qh_from_list_entry( pthis ); + // the last qh + ListFirstPrev( &ehci->async_list_cpu, pprev ); + pqhprev = qh_from_schedule( pprev ); + + // the first qh + ListFirst( &ehci->async_list_cpu, pnext ); + pqhnext = qh_from_schedule( pnext ); + + if( pprev == &ehci->async_list_cpu ) + { + // always a qh in async list + TRAP(); + return; + } + pqh->hw_next = pqhnext->phys_addr; + InsertTailList( &ehci->async_list_cpu, &pqh->elem_head_link->sched_link ); + pqhprev->hw_next = pqh->phys_addr; + return; +} + +static VOID +ehci_remove_bulk_from_schedule( +PEHCI_DEV ehci, +PURB purb +) +// executed in isr, and have frame_list_lock acquired, so +// never try to acquire any spin-lock +// remove the bulk purb from schedule, and mark it not in +// the schedule +{ + PLIST_ENTRY list_head; + PEHCI_QH pqh, pqhprev, pqhnext; + PEHCI_QTD ptd; + PEHCI_QH_CONTENT pqhc; + PLIST_ENTRY pthis, pprev, pnext; + + if( ehci == NULL || purb == NULL ) + return; + + list_head = &purb->trasac_list; + ListFirst( list_head, pthis ); + if( pthis == NULL ) + { + TRAP(); + return; + } + pqh = qh_from_list_entry( pthis ); + pqhc = ( PEHCI_QH_CONTENT) pqh; + + if( pqhc->is_async_head ) + TRAP(); + + ListFirst( &pqh->elem_head_link->sched_link, pnext ); + ListFirstPrev( &pqh->elem_head_link->sched_link, pprev ); + + if( pprev == &ehci->async_list_cpu ) + { + // we will at least have a qh with H-bit 1 in the async-list + TRAP(); + } + else if( pnext == &ehci->async_list_cpu ) + { + // remove the last one + pqhprev = qh_from_schedule( pprev ); + ListFirst( &ehci->async_list_cpu, pnext ); + pqhnext = qh_from_schedule( pnext ); + pqhprev->hw_next = pqhnext->phys_addr; + } + else + { + pqhprev = qh_from_schedule( pprev ); + pqhnext = qh_from_schedule( pnext ); + pqhprev->hw_next = pqhnext->phys_addr; + } + RemoveEntryList( &pqh->elem_head_link->sched_link ); + return; +} + + +static VOID +ehci_insert_fstn_schedule( +PEHCI_DEV ehci, +PURB purb +) +{ + + PURB_HS_PIPE_CONTENT pipe_content, pc; + PLIST_ENTRY pthis, list_head, pnext, pprev; + PEHCI_ELEM_LINKS elem_link; + PEHCI_QH pqh, pqhprev, pqhnext; + PEHCI_FSTN pfstn; + PURB purb1; + + ULONG interval, start_frame, start_uframe; + LONG i; + + if( ehci == NULL || purb == NULL ) + return; + + pipe_content = ( PURB_HS_PIPE_CONTENT )&purb->pipe; + interval = ( 1 << ( pipe_content->interval + 3 ) ); + list_head = &purb->trasac_list; + start_frame = purb->int_start_frame; + start_uframe = ( start_frame << 3 ) + 1; //( start_frame << 3 ) + pipe_content->start_uframe; + + if( ( start_frame << 3 ) >= interval ) + TRAP(); + + ListFirstPrev( list_head, pprev ); + + if( elem_type_list_entry( pprev ) != INIT_LIST_FLAG_FSTN ) + { + TRAP(); + return; + } + + pfstn = fstn_from_list_entry( pprev ); + + if( interval == 8 ) + { + ListFirst( &ehci->periodic_list_cpu[ EHCI_SCHED_INT8_INDEX ], pthis ); + + // skip the first one + ListNext( &ehci->periodic_list_cpu[ EHCI_SCHED_INT8_INDEX ], pthis, pnext ); + pprev = pthis; + pthis = pnext; + + while( pthis ) + { + purb1 = qh_from_schedule( pthis )->elem_head_link->purb; + pc = ( PURB_HS_PIPE_CONTENT )&purb1->pipe; + if( pc->speed_high ) + { + TRAP(); + return; + } + if( ( 1 << ( pc->interval + 3 ) ) > ( LONG )interval ) + { + TRAP(); + continue; + } + else if( ( 1 << ( pc->interval + 3 ) < ( LONG )interval ) ) + { + break; + } + else if( elem_type( pthis, FALSE ) == INIT_LIST_FLAG_FSTN ) + { + ListNext( &ehci->periodic_list_cpu[ EHCI_SCHED_INT8_INDEX ], pthis, pnext ); + pprev = pthis; + pthis = pnext; + } + else if( pc->start_uframe <= 1 ) + { + ListNext( &ehci->periodic_list_cpu[ EHCI_SCHED_INT8_INDEX ], pthis, pnext ); + pprev = pthis; + pthis = pnext; + } + break; + } + if( pprev == NULL ) + { + TRAP(); + return; + } + if( pthis == NULL ) + { + //the last one + InsertTailList( &ehci->periodic_list_cpu[ EHCI_SCHED_INT8_INDEX ], \ + &pfstn->elem_head_link->sched_link ); + } + else + { + if( elem_type( pprev, FALSE ) == INIT_LIST_FLAG_FSTN ) + { + InsertHeadList( &fstn_from_schedule( pprev )->elem_head_link->sched_link, \ + &pfstn->elem_head_link->sched_link ); + } + else + { + InsertHeadList( &qh_from_schedule( pprev )->elem_head_link->sched_link, \ + &pfstn->elem_head_link->sched_link ); + } + } + pfstn->hw_next = qh_from_schedule( pprev )->hw_next; + qh_from_schedule( pprev )->hw_next = pfstn->phys_addr; + } + else + { + start_frame++; + for( i = start_frame; i < ( LONG )start_frame + 1; i += ( interval >> 3 ) ) + { + list_head = &ehci->frame_list_cpu[ i ].td_link; + ListFirst( list_head, pthis ); + + pprev = list_head; + while( pthis ) + { + // skip itds and sitds + if( elem_type( pthis, FALSE ) == INIT_LIST_FLAG_ITD || + elem_type( pthis, FALSE ) == INIT_LIST_FLAG_SITD ) + { + ListNext( list_head, pthis, pnext ); + pprev = pthis; + pthis = pnext; + continue; + } + break; + } + + while( pthis ) + { + // find the insertion point + ULONG u; + + pqhnext = qh_from_schedule( pthis ); + if( elem_type( pthis, FALSE ) == INIT_LIST_FLAG_FSTN ) + purb1 = fstn_from_schedule( pthis )->elem_head_link->purb; + else + purb1 = pqhnext->elem_head_link->purb; + + if( purb1 == NULL ) + TRAP(); + + pc = ( PURB_HS_PIPE_CONTENT )&purb1->pipe; + u = 1 << ( pc->speed_high ? ( 1 << pc->interval ) : ( 1 << ( pc->interval + 3 ) ) ); + + if( u > interval ) + { + ListNext( list_head, pthis, pnext ); + pprev = pthis; + pthis = pnext; + continue; + } + else if( u == interval ) + { + if( start_uframe >= \ + ( elem_type( pthis, FALSE ) == INIT_LIST_FLAG_FSTN ? \ + 1 : pc->start_uframe ) + ( purb1->int_start_frame << 3 ) ) + { + ListNext( list_head, pthis, pnext ); + pprev = pthis; + pthis = pnext; + continue; + } + else + break; + } + else if( u < interval ) + { + break; + } + } + + if( pprev == list_head ) + { + // insert to the list head + pnext = pfstn->elem_head_link->sched_link.Flink = list_head->Flink; + list_head->Flink = &pfstn->elem_head_link->sched_link; + pfstn->hw_next = ehci->frame_list[ i ]; // point to following node + ehci->frame_list[ i ] = pfstn->phys_addr; + } + else + { + pnext = pfstn->elem_head_link->sched_link.Flink = pprev->Flink; + pprev->Flink = &pfstn->elem_head_link->sched_link; + + // fstn can be handled correctly + pfstn->hw_next = qh_from_schedule( pprev )->hw_next; + qh_from_schedule( pprev )->hw_next = pfstn->phys_addr; + } + } + // the pointer to next node of this fstn is alway same across the frame list. + for( i = start_frame + ( interval >> 3 ); i < ( LONG )ehci->frame_count; i += ( interval >> 3 ) ) + { + pprev = list_head = &ehci->frame_list_cpu[ i ].td_link; + ListFirst( list_head, pthis ); + + while( pthis ) + { + if( pthis == pnext ) + { + break; + } + pprev = pthis; + ListNext( list_head, pthis, pthis ); + } + + pprev->Flink = &pfstn->elem_head_link->sched_link; + if( pprev == list_head ) + ehci->frame_list[ i ] = pfstn->phys_addr; + else + qh_from_schedule( pprev )->hw_next = pfstn->phys_addr; + } + } +} + +static VOID +ehci_remove_fstn_from_schedule( +PEHCI_DEV ehci, +PURB purb +) +{ + PURB_HS_PIPE_CONTENT pipe_content; + PLIST_ENTRY pthis, list_head, pnext, pprev; + PEHCI_FSTN pfstn; + PURB purb1; + + ULONG interval, start_frame, start_uframe; + LONG i; + + if( ehci == NULL || purb == NULL ) + return; + + pipe_content = ( PURB_HS_PIPE_CONTENT )&purb->pipe; + interval = ( 1 << ( pipe_content->interval + 3 ) ); + list_head = &purb->trasac_list; + start_frame = purb->int_start_frame; + start_uframe = 1; + + if( ( start_frame << 3 ) >= interval ) + TRAP(); + start_frame++; + + ListFirstPrev( list_head, pprev ); + if( elem_type_list_entry( pprev ) != INIT_LIST_FLAG_FSTN ) + { + TRAP(); + return; + } + + pfstn = fstn_from_list_entry( pprev ); + if( interval < 8 ) + { + TRAP(); + return; + } + if( interval == 8 ) + { + ListFirstPrev( &pfstn->elem_head_link->sched_link, pprev ); + qh_from_schedule( pprev )->hw_next = pfstn->hw_next; + RemoveEntryList( &pfstn->elem_head_link->sched_link ); + } + else + { + for( i = start_frame; i < ( LONG )ehci->frame_count; i++ ) + { + ListFirst( &ehci->frame_list_cpu[ i ].td_link, pthis ); + if( pthis == NULL ) + { + TRAP(); + return; + } + pprev = &ehci->frame_list_cpu[ i ].td_link; + while( pthis && pthis != &pfstn->elem_head_link->sched_link ) + { + pprev = pthis; + ListNext( &ehci->frame_list_cpu[ i ].td_link, pthis, pnext ); + pthis = pnext; + } + if( pthis == NULL ) + { + TRAP(); + return; + } + qh_from_schedule( pprev )->hw_next = pfstn->hw_next; + pprev->Flink = pfstn->elem_head_link->sched_link.Flink; + } + } + return; +} + +static VOID +ehci_insert_int_schedule( +PEHCI_DEV ehci, +PURB purb +) +{ + PURB_HS_PIPE_CONTENT pipe_content, pc; + PLIST_ENTRY pthis, list_head, pnext, pprev; + PEHCI_ELEM_LINKS elem_link; + PEHCI_QH pqh, pqhprev, pqhnext; + PURB purb1; + + ULONG interval, u, start_frame, start_uframe; + LONG i; + UCHAR need_fstn; + + if( ehci == NULL || purb == NULL ) + return; + + pipe_content = ( PURB_HS_PIPE_CONTENT )&purb->pipe; + interval = REAL_INTERVAL; + start_uframe = ( purb->int_start_frame << 3 ) + pipe_content->start_uframe; + start_frame = purb->int_start_frame; + need_fstn = FALSE; + list_head = &purb->trasac_list; + + ListFirst( list_head, pthis ); + if( pthis == NULL ) + return; + + pqh = qh_from_list_entry( pthis ); + + if( !pipe_content->speed_high ) + { + interval = ( interval << 3 ); + ListFirstPrev( list_head, pprev ); + if( elem_type_list_entry( pprev ) == INIT_LIST_FLAG_FSTN ) + need_fstn = TRUE; + } + + if( interval < 16 ) + { + pqhprev = pqhnext = NULL; + if( interval == 1 ) + { + list_head = &ehci->periodic_list_cpu[ EHCI_SCHED_FSTN_INDEX ]; + ListFirst( list_head, pthis ); + InsertTailList( list_head, &pqh->elem_head_link->sched_link ); + ListFirstPrev( &pqh->elem_head_link->sched_link, pprev ); + pqh->hw_next = EHCI_PTR_TERM; + if( pprev == pthis ) + { + fstn_from_schedule( pthis )->hw_next = pqh->phys_addr; + } + else + { + qh_from_schedule( pthis )->hw_next = pqh->phys_addr; + } + } + else // interval == 2 or 4 or 8 + { + list_head = &ehci->periodic_list_cpu[ EHCI_SCHED_INT8_INDEX ]; + ListFirst( list_head, pthis ); + pprev = NULL; + while( pthis ) + { + elem_link = struct_ptr( pthis, EHCI_ELEM_LINKS, sched_link ); + purb1 = elem_link->purb; + pc = ( PURB_HS_PIPE_CONTENT )purb1->pipe; + u = ( pc->speed_high ? ( 1 << pc->interval ) : ( 1 << ( pc->interval + 3 ) ) ); + + if( interval < u ) + { + ListFirstPrev( pthis, pprev ); + break; + } + else if( interval > u ) + { + ListNext( list_head, pthis, pnext ); + pprev = pthis; + pthis = pnext; + continue; + } + // FIXME: is this right to fix fstn's start_uf 1??? + else if( start_uframe <= \ + ( elem_type( pthis, FALSE ) == INIT_LIST_FLAG_FSTN ? \ + 1 : pc->start_uframe ) + ( purb1->int_start_frame << 3 ) ) + { + ListNext( list_head, pthis, pnext ); + pprev = pthis; + pthis = pnext; + continue; + } + else // interval is equal, and start_uframe is greater + { + ListFirstPrev( pthis, pprev ); + break; + } + } + if( pprev == NULL ) + { + // at least one dummy qh is there + TRAP(); + } + else if( pnext == NULL ) + { + // the last one in this chain, fstn can be handled correctly + InsertTailList( list_head, &pqh->elem_head_link->sched_link ); + pqhprev = qh_from_schedule( pprev ); + pqh->hw_next = pqhprev->hw_next; + pqhprev->hw_next = pqh->phys_addr; + } + else + { pqhprev = qh_from_schedule( pprev ); + if( elem_type( pprev, FALSE ) == INIT_LIST_FLAG_QH ) + { + InsertHeadList( &pqhprev->elem_head_link->sched_link, &pqh->elem_head_link->sched_link ); + } + else if( elem_type( pprev, FALSE ) == INIT_LIST_FLAG_FSTN ) + { + InsertHeadList( &fstn_from_schedule( pprev )->elem_head_link->sched_link, &pqh->elem_head_link->sched_link ); + } + pqh->hw_next = pqhprev->hw_next; + pqhprev->hw_next = pqh->phys_addr; + } + } + } + else // interval >= 16 + { + if( ( start_frame << 3 ) >= interval ) + TRAP(); + + for( i = start_frame; i < ( LONG )start_frame + 1; i += ( interval >> 3 ) ) + { + list_head = &ehci->frame_list_cpu[ i ].td_link; + ListFirst( list_head, pthis ); + + pprev = list_head; + while( pthis ) + { + // skip itds and sitds + if( elem_type( pthis, FALSE ) == INIT_LIST_FLAG_ITD || + elem_type( pthis, FALSE ) == INIT_LIST_FLAG_SITD ) + { + ListNext( list_head, pthis, pnext ); + pprev = pthis; + pthis = pnext; + continue; + } + break; + } + + while( pthis ) + { + // find the insertion point + + pqhnext = qh_from_schedule( pthis ); + if( elem_type( pthis, FALSE ) == INIT_LIST_FLAG_FSTN ) + purb1 = fstn_from_schedule( pthis )->elem_head_link->purb; + else + purb1 = pqhnext->elem_head_link->purb; + + if( purb1 == NULL ) + TRAP(); + + pc = ( PURB_HS_PIPE_CONTENT )&purb1->pipe; + u = 1 << ( pc->speed_high ? ( 1 << pc->interval ) : ( 1 << ( pc->interval + 3 ) ) ); + + if( u > interval ) + { + ListNext( list_head, pthis, pnext ); + pprev = pthis; + pthis = pnext; + continue; + } + else if( u == interval ) + { + if( start_uframe >= \ + ( elem_type( pthis, FALSE ) == INIT_LIST_FLAG_FSTN ? \ + 1 : pc->start_uframe ) + ( purb1->int_start_frame << 3 ) ) + { + ListNext( list_head, pthis, pnext ); + pprev = pthis; + pthis = pnext; + continue; + } + else + break; + } + else if( u < interval ) + { + break; + } + } + if( pprev == list_head ) + { + // insert to the list head + pnext = pqh->elem_head_link->sched_link.Flink = list_head->Flink; + list_head->Flink = &pqh->elem_head_link->sched_link; + pqh->hw_next = ehci->frame_list[ i ]; // point to following node + ehci->frame_list[ i ] = pqh->phys_addr; + } + else + { + pnext = pqh->elem_head_link->sched_link.Flink = pprev->Flink; + pprev->Flink = &pqh->elem_head_link->sched_link; + + // fstn can be handled correctly + pqh->hw_next = qh_from_schedule( pprev )->hw_next; + qh_from_schedule( pprev )->hw_next = pqh->phys_addr; + } + } + for( i = start_frame + ( interval >> 3 ); i < ( LONG )ehci->frame_count; i += ( interval >> 3 ) ) + { + pprev = list_head = &ehci->frame_list_cpu[ i ].td_link; + ListFirst( list_head, pthis ); + + while( pthis ) + { + if( pthis == pnext ) + { + break; + } + pprev = pthis; + ListNext( list_head, pthis, pthis ); + } + + pprev->Flink = &pqh->elem_head_link->sched_link; + if( pprev == list_head ) + ehci->frame_list[ i ] = pqh->phys_addr; + else + qh_from_schedule( pprev )->hw_next = pqh->phys_addr; + } + } + + if( need_fstn ) + ehci_insert_fstn_schedule( ehci, purb ); + + return; +} + +static VOID +ehci_remove_int_from_schedule( +PEHCI_DEV ehci, +PURB purb +) +{ + PURB_HS_PIPE_CONTENT pipe_content, pc; + PLIST_ENTRY pthis, list_head, pnext, pprev, pcur; + PEHCI_ELEM_LINKS elem_link; + PEHCI_QH pqh, pqhprev, pqhnext; + PURB purb1; + + ULONG interval, start_frame, start_uframe, u; + LONG i; + + if( ehci == NULL || purb == NULL ) + return; + + pipe_content = ( PURB_HS_PIPE_CONTENT )&purb->pipe; + interval = REAL_INTERVAL; + start_uframe = ( purb->int_start_frame << 3 ) + pipe_content->start_uframe; + start_frame = purb->int_start_frame; + + ListFirst( &purb->trasac_list, pthis ); + if( pthis == NULL ) + return; + + pqh = qh_from_list_entry( pthis ); + list_head = &purb->trasac_list; + + if( IsListEmpty( list_head ) ) + { + TRAP(); + return; + } + + if( !pipe_content->speed_high ) + { + interval = ( interval << 3 ); + } + + if( interval >= 256 * 8 ) + return; + + if( interval < 16 ) + { + ListFirstPrev( &pqh->elem_head_link->sched_link, pprev ); + RemoveEntryList( &pqh->elem_head_link->sched_link ); + if( interval > 1 ) + { + pqhprev = qh_from_schedule( pprev ); + pqhprev->hw_next = pqh->hw_next; + } + else + { + ListFirst( &ehci->frame_list_cpu[ EHCI_SCHED_FSTN_INDEX ].td_link, list_head ); + if( elem_type( pprev, FALSE ) == INIT_LIST_FLAG_FSTN ) + { + fstn_from_schedule( list_head )->hw_next = pqh->hw_next; + } + else + { + qh_from_schedule( pprev )->hw_next = pqh->hw_next; + } + } + } + else if( interval >= 16 ) + { + ListFirst( list_head, pthis ); + pthis = &pqh->elem_head_link->sched_link; + + for( i = start_uframe; i < ( LONG )( ehci->frame_count << 3 ); i += interval ) + { + ListFirst( &ehci->frame_list_cpu[ i ].td_link, pcur ); + pprev = NULL; + while( pthis != pcur && pcur ) + { + ListNext( &ehci->frame_list_cpu[ i ].td_link, pcur, pnext ); + pprev = pcur; + pcur = pnext; + } + + if( pcur == NULL ) + { + TRAP(); + continue; + } + else if( pprev == NULL ) + { + // the first one in the frame list + ehci->frame_list_cpu[ i ].td_link.Flink = pthis->Flink; + ehci->frame_list[ i ] = qh_from_schedule( pthis )->hw_next; + } + else + { + if( elem_type( pprev, FALSE ) == INIT_LIST_FLAG_QH ) + { + qh_from_schedule( pprev )->elem_head_link->sched_link.Flink = pqh->elem_head_link->sched_link.Flink; + qh_from_schedule( pprev )->hw_next = pqh->hw_next; + } + else if( elem_type( pprev, FALSE ) == INIT_LIST_FLAG_ITD ) + { + itd_from_schedule( pprev )->elem_head_link->sched_link.Flink = pqh->elem_head_link->sched_link.Flink; + itd_from_schedule( pprev )->hw_next = pqh->hw_next; + } + else if( elem_type( pprev, FALSE ) == INIT_LIST_FLAG_SITD ) + { + sitd_from_schedule( pprev )->elem_head_link->sched_link.Flink = pqh->elem_head_link->sched_link.Flink; + sitd_from_schedule( pprev )->hw_next = pqh->hw_next; + } + else if( elem_type( pprev, FALSE ) == INIT_LIST_FLAG_FSTN ) + { + fstn_from_schedule( pprev )->elem_head_link->sched_link.Flink = pqh->elem_head_link->sched_link.Flink; + fstn_from_schedule( pprev )->hw_next = pqh->hw_next; + } + else + TRAP(); + } + } + } + + ListFirstPrev( &purb->trasac_list, pprev ); + if( elem_type_list_entry( pprev ) == INIT_LIST_FLAG_FSTN ) + ehci_remove_fstn_from_schedule( ehci, purb ); + return; +} + +static VOID +ehci_insert_iso_schedule( +PEHCI_DEV ehci, +PURB purb +) +{ + PURB_HS_PIPE_CONTENT pipe_content; + PLIST_ENTRY pthis, list_head, pnext, pprev, pcur; + + ULONG interval, start_frame; + LONG i; + + if( ehci == NULL || purb == NULL ) + return; + + pipe_content = ( PURB_HS_PIPE_CONTENT )&purb->pipe; + + interval = 8; + if( pipe_content->speed_high ) + interval = REAL_INTERVAL; + + start_frame = purb->iso_start_frame; + + ListFirst( &purb->trasac_list, pthis ); + if( pthis == NULL ) + { + TRAP(); + return; + } + + list_head = &purb->trasac_list; + if( IsListEmpty( list_head ) ) + { + TRAP(); + return; + } + + i = start_frame; + while( pthis ) + { + if( pipe_content->speed_high ) + { + itd_from_list_entry( pthis )->elem_head_link->sched_link.Flink = ehci->frame_list_cpu[ i ].td_link.Flink; + itd_from_list_entry( pthis )->hw_next = ehci->frame_list[ i ]; + + ehci->frame_list[ i ] = itd_from_list_entry( pthis )->phys_addr; + ehci->frame_list_cpu[ i ].td_link.Flink = pthis; + } + else + { + sitd_from_list_entry( pthis )->elem_head_link->sched_link.Flink = ehci->frame_list_cpu[ i ].td_link.Flink; + sitd_from_list_entry( pthis )->hw_next = ehci->frame_list[ i ]; + + ehci->frame_list[ i ] = sitd_from_list_entry( pthis )->phys_addr; + ehci->frame_list_cpu[ i ].td_link.Flink = pthis; + } + + ListNext( list_head, pthis, pnext ); + pthis = pnext; + + if( interval <= 8 ) + i++; + else + i += ( interval >> 3 ); + } + return; +} + +static VOID +ehci_remove_iso_from_schedule( +PEHCI_DEV ehci, +PURB purb +) +{ + PURB_HS_PIPE_CONTENT pipe_content; + PLIST_ENTRY pthis, list_head, pnext, pprev, pcur; + + ULONG interval, start_frame; + LONG i; + + if( ehci == NULL || purb == NULL ) + return; + + pipe_content = ( PURB_HS_PIPE_CONTENT )&purb->pipe; + + interval = 8; + if( pipe_content->speed_high ) + interval = REAL_INTERVAL; + + start_frame = purb->iso_start_frame; + + ListFirst( &purb->trasac_list, pthis ); + if( pthis == NULL ) + { + TRAP(); + return; + } + + list_head = &purb->trasac_list; + if( IsListEmpty( list_head ) ) + { + TRAP(); + return; + } + + i = start_frame; + while( pthis ) + { + // for the possible existance of sitd back pointer, we can not use for(...) + ListFirst( &ehci->frame_list_cpu[ i ].td_link, pcur ); + pprev = &ehci->frame_list_cpu[ i ].td_link; + while( pcur ) + { + if( pcur != pthis ) + { + ListNext( &ehci->frame_list_cpu[ i ].td_link, pcur, pnext ); + pprev = pcur; + pcur = pnext; + continue; + } + break; + } + + if( pcur == NULL ) + { + TRAP(); + } + pprev->Flink = pcur->Flink; + if( pprev != &ehci->frame_list_cpu[ i ].td_link ) + qh_from_schedule( pprev )->hw_next = qh_from_schedule( pcur )->hw_next; + else + ehci->frame_list[ i ] = qh_from_schedule( pcur )->hw_next; + + ListNext( list_head, pthis, pnext ); + pthis = pnext; + + if( interval <= 8 ) + i++; + else + i += ( interval >> 3 ); + } + return; +} + +NTSTATUS +ehci_isr_removing_urb( +PEHCI_DEV ehci, +PURB purb, +BOOL doorbell_rings, +ULONG cur_frame +) +{ + UCHAR type; + PLIST_ENTRY pthis, plast; + PURB_HS_PIPE_CONTENT pipe_content; + PEHCI_ITD_CONTENT pitd_content; + PEHCI_SITD_CONTENT psitd_content; + PEHCI_QH_CONTENT pqh_content; + PEHCI_QTD_CONTENT pqtd_content; + LONG i; + + if( purb == NULL || ehci == NULL ) + return STATUS_INVALID_PARAMETER; + + if( ( purb->flags & URB_FLAG_STATE_MASK ) == URB_FLAG_STATE_FINISHED ) + return STATUS_SUCCESS; + + type = 0; + pipe_content = ( PURB_HS_PIPE_CONTENT )&purb->pipe; + + switch( purb->flags & URB_FLAG_STATE_MASK ) + { + case URB_FLAG_STATE_IN_PROCESS: + { + // determine the removal type: complete, error or cancel + ListFirst( &purb->trasac_list, pthis ); + if( purb->flags & URB_FLAG_FORCE_CANCEL ) + { + type = 3; + } + else + { + if( pipe_content->trans_type == USB_ENDPOINT_XFER_BULK || \ + pipe_content->trans_type == USB_ENDPOINT_XFER_INT || \ + pipe_content->trans_type == USB_ENDPOINT_XFER_CONTROL ) + { + pqh_content = ( PEHCI_QH_CONTENT )( ( ULONG )struct_ptr( pthis, EHCI_ELEM_LINKS, elem_link )->phys_part & PHYS_PART_ADDR_MASK ); + if( EHCI_QH_ERROR( pqh_content ) ) + { + purb->status = pqh_content->cur_qtd.status; + type = 2; + } + else + { + pqtd_content = &pqh_content->cur_qtd; + if( pqtd_content->terminal && \ + ( ( pqtd_content->status & QTD_STS_ACTIVE ) == 0 ) ) + { + type = 1; + } + // else, not finished + } + } + else if( pipe_content->trans_type == USB_ENDPOINT_XFER_ISOC ) + { + // FIXME: do we need to check if current frame falls out of the + // frame range of iso transfer + // inspect the last td to determine if finished + ListFirstPrev( &purb->trasac_list, pthis ); + if( pthis ) + { + if( pipe_content->speed_high ) + { + pitd_content = ( PEHCI_ITD_CONTENT )( ( ULONG )struct_ptr( pthis, EHCI_ELEM_LINKS, elem_link )->phys_part & PHYS_PART_ADDR_MASK ); + for( i = 0; i < 8; i++ ) + { + if( pitd_content->status_slot[ i ].trans_length && \ + pitd_content->status_slot[ i ].status & 0x08 ) + { + break; + } + } + if( i == 8 ) + { + // the itds are all inactive + type = 1; + } + } + else + { + psitd_content = ( PEHCI_SITD_CONTENT )( ( ULONG )struct_ptr( pthis, EHCI_ELEM_LINKS, elem_link )->phys_part & PHYS_PART_ADDR_MASK ); + if( ( psitd_content->status & 0x80 ) == 0 ) + { + type = 1; + } + } + } + else // empty transaction list in purb + TRAP(); + } + else // unknown transfer type + TRAP(); + + } // end of not force cancel + + if( type == 0 ) + return STATUS_SUCCESS; + + switch( type ) + { + case 1: + { + if( pipe_content->trans_type == USB_ENDPOINT_XFER_CONTROL || \ + pipe_content->trans_type == USB_ENDPOINT_XFER_BULK ) + { + ehci_remove_bulk_from_schedule( ehci, purb ); + purb->flags &= ~URB_FLAG_STATE_MASK; + purb->flags |= URB_FLAG_STATE_DOORBELL; + purb->status = 0; + press_doorbell( ehci ); + return STATUS_SUCCESS; + } + else if( pipe_content->trans_type == USB_ENDPOINT_XFER_ISOC ) + { + ehci_remove_iso_from_schedule( ehci, purb ); + } + else if( pipe_content->trans_type == USB_ENDPOINT_XFER_INT ) + { + ehci_remove_int_from_schedule( ehci, purb ); + } + else // unknown transfer type + TRAP(); + + purb->flags &= ~URB_FLAG_STATE_MASK; + purb->flags |= URB_FLAG_STATE_FINISHED; + + // notify dpc the purb can be completed; + purb->flags &= ~URB_FLAG_IN_SCHEDULE; + purb->status = 0; + + return STATUS_SUCCESS; + } + case 2: + { + if( pipe_content->trans_type == USB_ENDPOINT_XFER_CONTROL || \ + pipe_content->trans_type == USB_ENDPOINT_XFER_BULK ) + { + ehci_deactivate_urb( purb ); + ehci_remove_bulk_from_schedule( ehci, purb ); + purb->flags &= ~URB_FLAG_STATE_MASK; + purb->flags |= URB_FLAG_STATE_DOORBELL; + press_doorbell( ehci ); + } + else if( pipe_content->trans_type == USB_ENDPOINT_XFER_INT ) + { + ehci_remove_int_from_schedule( ehci, purb ); + + purb->flags &= ~URB_FLAG_STATE_MASK; + purb->flags |= URB_FLAG_STATE_FINISHED; + purb->flags &= ~URB_FLAG_IN_SCHEDULE; + } + else // unknown transfer or iso transfer + TRAP(); + return STATUS_SUCCESS; + } + case 3: + { + if( pipe_content->trans_type == USB_ENDPOINT_XFER_CONTROL || \ + pipe_content->trans_type == USB_ENDPOINT_XFER_BULK || \ + pipe_content->trans_type == USB_ENDPOINT_XFER_INT ) + { + ehci_deactivate_urb( purb ); + if( pipe_content->trans_type == USB_ENDPOINT_XFER_BULK || \ + pipe_content->trans_type == USB_ENDPOINT_XFER_CONTROL ) + ehci_remove_bulk_from_schedule( ehci, purb ); + else + ehci_remove_int_from_schedule( ehci, purb ); + + purb->flags &= ~URB_FLAG_STATE_MASK; + purb->flags |= URB_FLAG_STATE_DOORBELL; + + press_doorbell( ehci ); + + } + else // unknown transfer or iso transfer + DO_NOTHING; + purb->status = 0; + return STATUS_SUCCESS; + } + default: + TRAP(); + } + } + case URB_FLAG_STATE_DOORBELL: + { + if( doorbell_rings == FALSE ) + return STATUS_SUCCESS; + + purb->flags &= ~URB_FLAG_STATE_MASK; + purb->flags |= URB_FLAG_STATE_FINISHED; + purb->flags &= ~URB_FLAG_IN_SCHEDULE; + return STATUS_SUCCESS; + } + } + return STATUS_SUCCESS; +} + +static ULONG +ehci_scan_iso_error( +PEHCI_DEV ehci, +PURB purb +) +// we only report the first error of the ITDs, purb->status is the status code +// return the raw status for ehci_set_error_code +{ + PURB_HS_PIPE_CONTENT pipe_content; + PEHCI_SITD_CONTENT psitd_content; + PEHCI_ITD_CONTENT pitd_content; + PLIST_ENTRY pthis, pnext; + LONG i; + + if( ehci == NULL || purb == NULL ) + return 0; + + pipe_content = ( PURB_HS_PIPE_CONTENT )&purb->pipe; + if( pipe_content->trans_type != USB_ENDPOINT_XFER_ISOC ) + { + return 0; + } + + ListFirst( &purb->trasac_list, pthis ); + if( pipe_content->speed_high ) + { + while( pthis ) + { + pitd_content = ( PEHCI_ITD_CONTENT )itd_from_list_entry( pthis ); + for( i = 0; i < 8; i++ ) + { + if( pitd_content->status_slot[ i ].status & ITD_ANY_ERROR ) + break; + } + if( i < 8 ) + { + // error occured + return purb->status = pitd_content->status_slot[ i ].status; + } + ListNext( &purb->trasac_list, pthis, pnext ); + pthis = pnext; + } + } + else + { + while( pthis ) + { + psitd_content = ( PEHCI_SITD_CONTENT )sitd_from_list_entry( pthis ); + if( psitd_content->status & SITD_ANY_ERROR ) + { + // error occured + if( psitd_content->s_mask == 0x04 && \ + psitd_content->c_mask == 0x70 && \ + psitd_content->bytes_to_transfer == 1 ) + return purb->status = 0; + + return purb->status = psitd_content->status; + } + ListNext( &purb->trasac_list, pthis, pnext ); + pthis = pnext; + } + } + return 0; +} + +BOOLEAN +ehci_isr( +PKINTERRUPT interrupt, +PVOID context +) + // we can not use endp here for it is within the dev scope, and + // we can not acquire the dev-lock, fortunately we saved some + // info in purb->pipe in ehci_internal_submit_XXX. +{ + + PEHCI_DEV ehci; + ULONG status, urb_count; + PLIST_ENTRY pthis, pnext; + PURB purb; + BOOL door_bell_rings; + ULONG cur_frame; + /* + * Read the interrupt status, and write it back to clear the + * interrupt cause + */ + ehci = ( PEHCI_DEV )context; + if( ehci == NULL ) + return FALSE; + + status = EHCI_READ_PORT_ULONG( ( PULONG )( ehci->port_base + EHCI_USBSTS ) ); + cur_frame = EHCI_READ_PORT_ULONG( ( PULONG )( ehci->port_base + EHCI_FRINDEX ) ); + + status &= ( EHCI_ERROR_INT | STS_INT | STS_IAA ); + if( !status ) /* shared interrupt, not mine */ + { + ehci_dbg_print( DBGLVL_MAXIMUM, ( "ehci_isr(): not our int\n" ) ); + return FALSE; + } + + /* clear it*/ + EHCI_WRITE_PORT_ULONG( ( PULONG )( ehci->port_base + EHCI_USBSTS ), status ); + + if( status & EHCI_ERROR_INT ) + { + ehci_dbg_print( DBGLVL_MAXIMUM, ( "ehci_isr(): current ehci status=0x%x\n", status ) ); + } + else + { + ehci_dbg_print( DBGLVL_MAXIMUM, ( "ehci_isr(): congratulations, no error occurs\n" ) ); + } + + + if ( status & STS_FATAL ) + { + DbgPrint( "ehci_isr(): host system error, PCI problems?\n"); + for( ; ; ); + + } + + if ( status & STS_HALT ) //&& !ehci->is_suspended + { + DbgPrint( "ehci_isr(): host controller halted. very bad\n"); + /* FIXME: Reset the controller, fix the offending TD */ + // reset is performed in dpc + } + + + door_bell_rings = ( ( status & STS_IAA ) != 0 ); + + // scan to remove those due +#ifdef DBG + urb_count = dbg_count_list( &ehci->urb_list ); + ehci_dbg_print( DBGLVL_MAXIMUM, ("ehci_isr(): urb# in process is %d\n", urb_count ) ); +#endif + + ListFirst( &ehci->urb_list, pthis ); + while( pthis ) + { + purb = ( PURB )pthis; + ehci_isr_removing_urb( ehci, purb, door_bell_rings, cur_frame ); + ListNext( &ehci->urb_list, pthis, pnext ); + pthis = pnext; + } + + KeInsertQueueDpc( &ehci->pdev_ext->ehci_dpc, ( PVOID )status, 0 ); + return TRUE; +} + +#ifndef INCLUDE_EHCI +VOID +ehci_unload( +IN PDRIVER_OBJECT DriverObject +) +{ + PDEVICE_OBJECT pdev; + PEHCI_DEVICE_EXTENSION pdev_ext; + PUSB_DEV_MANAGER dev_mgr; + LONG i; + + pdev = DriverObject->DeviceObject; + + if( pdev == NULL ) + return; + + pdev_ext = pdev->DeviceExtension; + if( pdev_ext == NULL ) + return; + + dev_mgr = &g_dev_mgr; + if( dev_mgr == NULL ) + return; + // + // set the termination flag + // + dev_mgr->term_flag = TRUE; + // + // wake up the thread if it is + // + KeSetEvent( &dev_mgr->wake_up_event, 0, FALSE ); + KeWaitForSingleObject( + dev_mgr->pthread, + Executive, + KernelMode, + TRUE, + NULL); + ObDereferenceObject( dev_mgr->pthread ); + dev_mgr->pthread = NULL; + + dev_mgr_release_hcd( dev_mgr ); + return; +} + +NTSTATUS +generic_dispatch_irp( +IN PDEVICE_OBJECT dev_obj, +IN PIRP irp +) +{ + PDEVEXT_HEADER dev_ext; + + dev_ext = ( PDEVEXT_HEADER )dev_obj->DeviceExtension; + + if( dev_ext && dev_ext->dispatch ) + return dev_ext->dispatch( dev_obj, irp ); + + irp->IoStatus.Information = 0; + + EXIT_DISPATCH( STATUS_UNSUCCESSFUL, irp ); +} + +VOID +generic_start_io( +IN PDEVICE_OBJECT dev_obj, +IN PIRP irp +) +{ + PDEVEXT_HEADER dev_ext; + + KIRQL old_irql; + + IoAcquireCancelSpinLock( &old_irql ); + if (irp != dev_obj->CurrentIrp || irp->Cancel) + { + IoReleaseCancelSpinLock(old_irql); + return; + } + else + { + IoSetCancelRoutine(irp, NULL); + IoReleaseCancelSpinLock(old_irql); + } + + dev_ext = ( PDEVEXT_HEADER )dev_obj->DeviceExtension; + + if( dev_ext && dev_ext->start_io ) + { + dev_ext->start_io( dev_obj, irp ); + return; + } + + irp->IoStatus.Information = 0; + irp->IoStatus.Status = STATUS_UNSUCCESSFUL; + + IoStartNextPacket( dev_obj, FALSE ); + IoCompleteRequest( irp, IO_NO_INCREMENT ); +} + +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ) +{ + NTSTATUS ntStatus = STATUS_SUCCESS; + BOOLEAN fRes; + +#if DBG + // should be done before any debug output is done. + // read our debug verbosity level from the registry + //NetacOD_GetRegistryDword( NetacOD_REGISTRY_PARAMETERS_PATH, //absolute registry path + // L"DebugLevel", // REG_DWORD ValueName + // &gDebugLevel ); // Value receiver + + // debug_level = DBGLVL_MAXIMUM; +#endif + + ehci_dbg_print_cond( DBGLVL_MINIMUM , DEBUG_UHCI, ("Entering DriverEntry(), RegistryPath=\n %ws\n", RegistryPath->Buffer )); + + // Remember our driver object, for when we create our child PDO + usb_driver_obj = DriverObject; + + // + // Create dispatch points for create, close, unload + DriverObject->MajorFunction[ IRP_MJ_CREATE ] = generic_dispatch_irp; + DriverObject->MajorFunction[ IRP_MJ_CLOSE ] = generic_dispatch_irp; + DriverObject->DriverUnload = ehci_unload; + + // User mode DeviceIoControl() calls will be routed here + DriverObject->MajorFunction[ IRP_MJ_DEVICE_CONTROL ] = generic_dispatch_irp; + DriverObject->MajorFunction[ IRP_MJ_INTERNAL_DEVICE_CONTROL ] = generic_dispatch_irp; + + // User mode ReadFile()/WriteFile() calls will be routed here + DriverObject->MajorFunction[ IRP_MJ_WRITE ] = generic_dispatch_irp; + DriverObject->MajorFunction[ IRP_MJ_READ ] = generic_dispatch_irp; + + DriverObject->MajorFunction[ IRP_MJ_SHUTDOWN ] = generic_dispatch_irp; + DriverObject->MajorFunction[ IRP_MJ_SCSI ] = generic_dispatch_irp; + DriverObject->MajorFunction[ IRP_MJ_FLUSH_BUFFERS ] = generic_dispatch_irp; + + DriverObject->DriverStartIo = generic_start_io; + // routines for handling system PNP and power management requests + //DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = generic_dispatch_irp; + + // The Functional Device Object (FDO) will not be created for PNP devices until + // this routine is called upon device plug-in. + RtlZeroMemory( &g_dev_mgr, sizeof( USB_DEV_MANAGER ) ); + g_dev_mgr.usb_driver_obj = DriverObject; + + ehci_probe( DriverObject, RegistryPath, &g_dev_mgr ); + + if( dev_mgr_strobe( &g_dev_mgr ) == FALSE ) + { + dev_mgr_release_hcd( &g_dev_mgr ); + return STATUS_UNSUCCESSFUL; + } + + dev_mgr_start_hcd( &g_dev_mgr ); + ehci_dbg_print_cond( DBGLVL_DEFAULT, DEBUG_UHCI, ("DriverEntry(): exiting... (%x)\n", ntStatus)); + return STATUS_SUCCESS; +} +#endif + +//note: the initialization will be in the following order +// dev_mgr_strobe +// ehci_start + +// to kill dev_mgr_thread: +// dev_mgr->term_flag = TRUE; +// KeSetEvent( &dev_mgr->wake_up_event ); +// this piece of code must run at passive-level +// +// diff --git a/reactos/drivers/usb/nt4compat/usbdriver/ehci.h b/reactos/drivers/usb/nt4compat/usbdriver/ehci.h new file mode 100644 index 00000000000..4b4d38b764f --- /dev/null +++ b/reactos/drivers/usb/nt4compat/usbdriver/ehci.h @@ -0,0 +1,810 @@ +/* + * Copyright (c) 2001-2002 by David Brownell + * + * This program 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 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; if not, write to the Free Software Foundation, + * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include "hcd.h" +#include "usb.h" +#include "td.h" +#ifndef __EHCI_H__ +#define __EHCI_H__ + +#define EHCI_QH_SIZE sizeof( EHCI_QH ) +#define EHCI_ITD_SIZE sizeof( EHCI_ITD ) +#define EHCI_SITD_SIZE sizeof( EHCI_SITD ) +#define EHCI_FSTN_SIZE sizeof( EHCI_FSTN ) +#define EHCI_QTD_SIZE sizeof( EHCI_QTD ) + +#define INIT_LIST_FLAG_TYPE 0x0f +#define INIT_LIST_FLAG_ITD 0x00 +#define INIT_LIST_FLAG_QH 0x01 +#define INIT_LIST_FLAG_SITD 0x02 +#define INIT_LIST_FLAG_FSTN 0x03 +#define INIT_LIST_FLAG_QTD 0x04 + +#define EHCI_MAX_SIZE_TRANSFER 0x100000 // 1 MB per transfer +#define EHCI_MAX_ELEMS_POOL 1024 +#define EHCI_MAX_LISTS_POOL 0x08 +#define EHCI_MAX_QHS_LIST 256 // 4 pages +#define EHCI_MAX_ITDS_LIST ( PAGE_SIZE / EHCI_ITD_SIZE * 2 ) // 2 pages +#define EHCI_MAX_SITDS_LIST ( PAGE_SIZE / EHCI_SITD_SIZE )// 1 page +#define EHCI_MAX_FSTNS_LIST ( PAGE_SIZE / EHCI_FSTN_SIZE )// 1 page +#define EHCI_MAX_QTDS_LIST 256 // 4 pages + +#define EHCI_PTR_TERM 0x01 +#define EHCI_NAK_RL_COUNT 0x04 + +#define EHCI_QTD_MAX_TRANS_SIZE 20480 +#define PHYS_PART_TYPE_MASK 0x01f +#define PHYS_PART_ADDR_MASK ( ~PHYS_PART_TYPE_MASK ) + +typedef struct _EHCI_ELEM_LINKS +{ + LIST_ENTRY elem_link; // link for one urb request + LIST_ENTRY sched_link; // to shadow link of schedule + struct _EHCI_ELEM_POOL* pool_link; + struct _EHCI_ELEM_LIST* list_link; // opaque to client, which list this td belongs to + PVOID phys_part; // point to EHCI_XXX, the lower 5 bits is used to indicate the type of the element + PURB purb; + +} EHCI_ELEM_LINKS, *PEHCI_ELEM_LINKS; + +typedef struct _INIT_ELEM_LIST_CONTEXT +{ + PADAPTER_OBJECT padapter; + struct _EHCI_ELEM_POOL *pool; + +} INIT_ELEM_LIST_CONTEXT, *PINIT_ELEM_LIST_CONTEXT; + +typedef VOID ( *PDESTROY_ELEM_LIST )( struct _EHCI_ELEM_LIST* plist ); +typedef PLIST_ENTRY ( *PGET_LIST_HEAD )( struct _EHCI_ELEM_LIST* plist ); +typedef LONG ( *PGET_TOTAL_COUNT )( struct _EHCI_ELEM_LIST* plist ); +typedef LONG ( *PGET_ELEM_SIZE )( struct _EHCI_ELEM_LIST* plist ); +typedef LONG ( *PGET_LINK_OFFSET )( struct _EHCI_ELEM_LIST* plist ); +typedef LONG ( *PADD_REF )( struct _EHCI_ELEM_LIST* plist ); +typedef LONG ( *PRELEASE_REF )( struct _EHCI_ELEM_LIST* plist ); +typedef LONG ( *PGET_REF )( struct _EHCI_ELEM_LIST* plist ); + +typedef struct _EHCI_ELEM_LIST +{ + PDESTROY_ELEM_LIST destroy_list; + PGET_LIST_HEAD get_list_head; + PGET_TOTAL_COUNT get_total_count; + PGET_ELEM_SIZE get_elem_size; + PGET_LINK_OFFSET get_link_offset; + PADD_REF add_ref; + PRELEASE_REF release_ref; + PGET_REF get_ref; + + // private area + LONG flags; // contails element's type info + LONG total_count; + LONG elem_size; + LIST_HEAD free_list; // chains of all the elements + struct _EHCI_ELEM_POOL *parent_pool; + LONG reference; + + // used to represent the physical memory + PPHYSICAL_ADDRESS phys_addrs; // array of non-contigous memory + PBYTE *phys_bufs; + PEHCI_ELEM_LINKS elem_head_buf; + PADAPTER_OBJECT padapter; + +} EHCI_ELEM_LIST, *PEHCI_ELEM_LIST; + +BOOL elem_pool_init_pool( struct _EHCI_ELEM_POOL* pool, LONG flags, PVOID context); +VOID elem_pool_destroy_pool ( struct _EHCI_ELEM_POOL* pool ); +PEHCI_ELEM_LINKS elem_pool_alloc_elem ( struct _EHCI_ELEM_POOL* pool ); +VOID elem_pool_free_elem ( PEHCI_ELEM_LINKS elem_link ); +LONG elem_pool_get_total_count ( struct _EHCI_ELEM_POOL* pool ); +BOOL elem_pool_is_empty ( struct _EHCI_ELEM_POOL* pool ); +LONG elem_pool_get_free_count ( struct _EHCI_ELEM_POOL* pool ); +LONG elem_pool_get_link_offset ( struct _EHCI_ELEM_POOL* pool ); +PEHCI_ELEM_LINKS elem_pool_alloc_elems ( struct _EHCI_ELEM_POOL* pool, LONG count ); +BOOL elem_pool_free_elems( PEHCI_ELEM_LINKS elem_chains ); +LONG elem_pool_get_type( struct _EHCI_ELEM_POOL* pool ); + +// the following are private functions +BOOL elem_pool_expand_pool( struct _EHCI_ELEM_POOL* pool, LONG elem_count ); +BOOL elem_pool_collect_garbage( struct _EHCI_ELEM_POOL* pool ); + +// lock operations +BOOL elem_pool_lock( struct _EHCI_ELEM_POOL* pool, BOOL at_dpc ); +BOOL elem_pool_unlock( struct _EHCI_ELEM_POOL* pool, BOOL at_dpc ); + +// helper +LONG get_elem_phys_part_size( ULONG type ); + +typedef struct _EHCI_ELEM_POOL +{ + LONG flags; + LONG free_count; // free count of all the lists currently allocated + LONG link_offset; // a cache for elem_list->get_link_offset + LONG list_count; // lists currently allocated + PEHCI_ELEM_LIST elem_lists[ EHCI_MAX_LISTS_POOL ]; + +} EHCI_ELEM_POOL, *PEHCI_ELEM_POOL; + +/*-------------------------------------------------------------------------*/ + +#define QTD_NEXT(dma) cpu_to_le32((u32)dma) + +/* + * EHCI Specification 0.95 Section 3.5 + * QTD: describe data transfer components (buffer, direction, ...) + * See Fig 3-6 "Queue Element Transfer Descriptor Block Diagram". + * + * These are associated only with "QH" (Queue Head) structures, + * used with control, bulk, and interrupt transfers. + */ +#define QTD_TOGGLE (1 << 31) /* data toggle */ +#define QTD_LENGTH(tok) (((tok)>>16) & 0x7fff) +#define QTD_IOC (1 << 15) /* interrupt on complete */ +#define QTD_CERR(tok) (((tok)>>10) & 0x3) +#define QTD_PID(tok) (((tok)>>8) & 0x3) +#define QTD_STS_ACTIVE (1 << 7) /* HC may execute this */ +#define QTD_STS_HALT (1 << 6) /* halted on error */ +#define QTD_STS_DBE (1 << 5) /* data buffer error (in HC) */ +#define QTD_STS_BABBLE (1 << 4) /* device was babbling (qtd halted) */ +#define QTD_STS_XACT (1 << 3) /* device gave illegal response */ +#define QTD_STS_MMF (1 << 2) /* incomplete split transaction */ +#define QTD_STS_STS (1 << 1) /* split transaction state */ +#define QTD_STS_PING (1 << 0) /* issue PING? */ +#define QTD_ANY_ERROR ( QTD_STS_HALT | QTD_STS_DBE | QTD_STS_XACT | QTD_STS_MMF | QTD_STS_BABBLE ) + +#define QTD_PID_SETUP 0x02 +#define QTD_PID_IN 0x01 +#define QTD_PID_OUT 0x00 + +#pragma pack( push, usb_align, 1 ) + +typedef struct _EHCI_QTD_CONTENT +{ + // DWORD 0 + ULONG terminal : 1; + ULONG reserved : 4; + ULONG next_qtd : 27; + + // DWORD 1 + ULONG alt_terminal : 1; + ULONG reserved1 : 4; + ULONG alt_qtd : 27; + + // DWORD 2 + ULONG status : 8; + ULONG pid : 2; + ULONG err_count : 2; + ULONG cur_page : 3; + ULONG ioc : 1; + ULONG bytes_to_transfer : 15; + ULONG data_toggle : 1; + + // DWORD 3 + ULONG cur_offset : 12; + ULONG page0 : 20; + + // DWORD 4- + ULONG pages[ 4 ]; + +} EHCI_QTD_CONTENT, *PEHCI_QTD_CONTENT; + +typedef struct _EHCI_QTD +{ + /* first part defined by EHCI spec */ + ULONG hw_next; /* see EHCI 3.5.1 */ + ULONG hw_alt_next; /* see EHCI 3.5.2 */ + ULONG hw_token; /* see EHCI 3.5.3 */ + ULONG hw_buf [5]; /* see EHCI 3.5.4 */ + + /* the rest is HCD-private */ + PEHCI_ELEM_LINKS elem_head_link; + ULONG phys_addr; /* qtd address */ + USHORT bytes_to_transfer; /* the bytes_to_transfer in hw_token will drop to zero when completed*/ + USHORT reserved2; + ULONG reserved[ 5 ]; + +} EHCI_QTD, *PEHCI_QTD; //__attribute__ ((aligned (32))); + +#define QTD_MASK cpu_to_le32 (~0x1f) /* mask NakCnt+T in qh->hw_alt_next */ + +/*-------------------------------------------------------------------------*/ + +/* type tag from {qh,itd,sitd,fstn}->hw_next */ +#define Q_NEXT_TYPE(dma) ((dma) & __constant_cpu_to_le32 (3 << 1)) + +/* values for that type tag */ +#define Q_TYPE_ITD (0 << 1) +#define Q_TYPE_QH (1 << 1) +#define Q_TYPE_SITD (2 << 1) +#define Q_TYPE_FSTN (3 << 1) + +/* next async queue entry, or pointer to interrupt/periodic QH */ +#define QH_NEXT(dma) (cpu_to_le32(((u32)dma)&~0x01f)|Q_TYPE_QH) + +/* for periodic/async schedules and qtd lists, mark end of list */ +#define EHCI_LIST_END __constant_cpu_to_le32(1) /* "null pointer" to hw */ + +/*-------------------------------------------------------------------------*/ + +/* + * EHCI Specification 0.95 Section 3.6 + * QH: describes control/bulk/interrupt endpoints + * See Fig 3-7 "Queue Head Structure Layout". + * + * These appear in both the async and (for interrupt) periodic schedules. + */ + +#define QH_HEAD 0x00008000 +#define QH_STATE_LINKED 1 /* HC sees this */ +#define QH_STATE_UNLINK 2 /* HC may still see this */ +#define QH_STATE_IDLE 3 /* HC doesn't see this */ +#define QH_STATE_UNLINK_WAIT 4 /* LINKED and on reclaim q */ +#define QH_STATE_COMPLETING 5 /* don't touch token.HALT */ +#define NO_FRAME ((unsigned short)~0) /* pick new start */ + +typedef struct _EHCI_QH_CONTENT +{ + // DWORD 0 + ULONG terminal : 1; + ULONG ptr_type : 2; + ULONG reserved : 2; + ULONG next_link : 27; + + // DWORD 1 + ULONG dev_addr : 7; + ULONG inactive : 1; + ULONG endp_addr : 4; + ULONG endp_spd : 2; + ULONG data_toggle : 1; + ULONG is_async_head : 1; + ULONG max_packet_size : 11; + ULONG is_ctrl_endp : 1; + ULONG reload_counter : 4; + + // DWORD 2 + ULONG s_mask : 8; + ULONG c_mask : 8; + ULONG hub_addr : 7; + ULONG port_idx : 7; + ULONG mult : 2; + + // DWORD 3 + ULONG cur_qtd_ptr; + + // overlay + EHCI_QTD_CONTENT cur_qtd; + +} EHCI_QH_CONTENT, *PEHCI_QH_CONTENT; + +typedef struct _EHCI_QH +{ + /* first part defined by EHCI spec */ + ULONG hw_next; /* see EHCI 3.6.1 */ + ULONG hw_info1; /* see EHCI 3.6.2 */ + ULONG hw_info2; /* see EHCI 3.6.2 */ + ULONG hw_current; /* qtd list - see EHCI 3.6.4 */ + + /* qtd overlay (hardware parts of a struct ehci_qtd) */ + ULONG hw_qtd_next; + ULONG hw_alt_next; + ULONG hw_token; + ULONG hw_buf [5]; + + /* the rest is HCD-private */ + PEHCI_ELEM_LINKS elem_head_link; + ULONG phys_addr; /* address of qh */ + ULONG reserved[ 2 ]; + +} EHCI_QH, *PEHCI_QH; // __attribute__ ((aligned (32))); + +/*-------------------------------------------------------------------------*/ + +/* + * EHCI Specification 0.95 Section 3.3 + * Fig 3-4 "Isochronous Transaction Descriptor (iTD)" + * + * Schedule records for high speed iso xfers + */ + +#define EHCI_ISOC_ACTIVE (1<<31) /* activate transfer this slot */ +#define EHCI_ISOC_BUF_ERR (1<<30) /* Data buffer error */ +#define EHCI_ISOC_BABBLE (1<<29) /* babble detected */ +#define EHCI_ISOC_XACTERR (1<<28) /* XactErr - transaction error */ +#define EHCI_ITD_LENGTH(tok) (((tok)>>16) & 0x7fff) +#define EHCI_ITD_IOC (1 << 15) /* interrupt on complete */ + +#define ITD_STS_ACTIVE 8 +#define ITD_STS_BUFERR 4 +#define ITD_STS_BABBLE 2 +#define ITD_STS_XACTERR 1 + +#define ITD_ANY_ERROR ( ITD_STS_BUFERR | ITD_STS_BABBLE | ITD_STS_XACTERR ) + +typedef struct _EHCI_ITD_STATUS_SLOT +{ + ULONG offset : 12; + ULONG page_sel : 3; + ULONG ioc : 1; + ULONG trans_length : 12; + ULONG status : 4; + +} EHCI_ITD_STATUS_SLOT, *PEHCI_ITD_STATUS_SLOT; + +typedef struct _EHCI_ITD_CONTENT +{ + // DWORD 0; + ULONG terminal : 1; + ULONG ptr_type : 2; + ULONG reserved : 2; + ULONG next_link : 27; + // DWORD 1-8; + EHCI_ITD_STATUS_SLOT status_slot[ 8 ]; + // DWORD 9; + ULONG dev_addr : 7; + ULONG reserved1 : 1; + ULONG endp_num : 4; + ULONG page0 : 20; + // DWORD 10; + ULONG max_packet_size : 11; + ULONG io_dir : 1; + ULONG page1 : 20; + // DWORD 11; + ULONG mult : 2; + ULONG reserved2 : 10; + ULONG page2 : 20; + // DWORD 12-; + ULONG pages[ 4 ]; + +} EHCI_ITD_CONTENT, *PEHCI_ITD_CONTENT; + +typedef struct _EHCI_ITD { + + /* first part defined by EHCI spec */ + ULONG hw_next; /* see EHCI 3.3.1 */ + ULONG hw_transaction [8]; /* see EHCI 3.3.2 */ + ULONG hw_bufp [7]; /* see EHCI 3.3.3 */ + + /* the rest is EHCI-private */ + PEHCI_ELEM_LINKS elem_head_link; + ULONG phys_addr; /* for this itd */ + ULONG buf_phys_addr; /* buffer address */ + ULONG reserved[ 5 ]; + +} EHCI_ITD, *PEHCI_ITD; // __attribute__ ((aligned (32))); + +/*-------------------------------------------------------------------------*/ + +/* + * EHCI Specification 0.95 Section 3.4 + * siTD, aka split-transaction isochronous Transfer Descriptor + * ... describe low/full speed iso xfers through TT in hubs + * see Figure 3-5 "Split-transaction Isochronous Transaction Descriptor (siTD) + */ +#define SITD_STS_ACTIVE 0x80 +#define SITD_STS_ERR 0x40 // error from device +#define SITD_STS_DBE 0x20 // data buffer error +#define SITD_STS_BABBLE 0x10 // data more than expected +#define SITD_STS_XACTERR 0x08 // general error on hc +#define SITD_STS_MISSFRM 0x04 // missd micro frames +#define SITD_STS_SC 0x02 // state is whether start-split( 0 ) or complete-split( 1 ) +#define SITD_STS_RESERVED 0x01 +#define SITD_ANY_ERROR ( SITD_STS_ERR | SITD_STS_DBE | SITD_STS_BABBLE | SITD_STS_XACTERR | SITD_STS_MISSFRM ) + +typedef struct _EHCI_SITD_CONTENT +{ + // DWORD 0; + ULONG terminal : 1; + ULONG ptr_type : 2; + ULONG reserved : 2; + ULONG next_link : 27; + // DWORD 1; + ULONG dev_addr : 7; + ULONG reserved1 : 1; + ULONG endp_num : 4; + ULONG reserved2 : 4; + ULONG hub_addr : 7; + ULONG reserved3 : 1; + ULONG port_idx : 7; + ULONG io_dir : 1; + // DWORD 2; + ULONG s_mask : 8; + ULONG c_mask : 8; + ULONG reserved4 : 16; + // DWORD 3: + ULONG status : 8; + ULONG c_prog_mask : 8; + ULONG bytes_to_transfer : 10; + ULONG reserved5 : 4; + ULONG page_sel : 1; + ULONG ioc : 1; + // DWORD 4; + ULONG cur_offset : 12; + ULONG page0 : 20; + // DWORD 5; + ULONG trans_count : 3; + ULONG trans_pos : 2; + ULONG reserved6 : 7; + ULONG page1 : 20; + // DWORD 6; + ULONG back_terminal : 1; + ULONG reserved7 : 4; + ULONG back_ptr : 27; + +} EHCI_SITD_CONTENT, *PEHCI_SITD_CONTENT; + +typedef struct _EHCI_SITD +{ + /* first part defined by EHCI spec */ + ULONG hw_next; + /* uses bit field macros above - see EHCI 0.95 Table 3-8 */ + + ULONG hw_fullspeed_ep; /* see EHCI table 3-9 */ + ULONG hw_uframe; /* see EHCI table 3-10 */ + ULONG hw_tx_results1; /* see EHCI table 3-11 */ + ULONG hw_tx_results2; /* see EHCI table 3-12 */ + ULONG hw_tx_results3; /* see EHCI table 3-12 */ + ULONG hw_backpointer; /* see EHCI table 3-13 */ + + /* the rest is HCD-private */ + PEHCI_ELEM_LINKS elem_head_link; + ULONG phys_addr; + ULONG buf_phys_addr; /* buffer address */ + + USHORT usecs; /* start bandwidth */ + USHORT c_usecs; /* completion bandwidth */ + ULONG reserved[ 5 ]; + +} EHCI_SITD, *PEHCI_SITD; // __attribute__ ((aligned (32))); + +/*-------------------------------------------------------------------------*/ + +/* + * EHCI Specification 0.96 Section 3.7 + * Periodic Frame Span Traversal Node (FSTN) + * + * Manages split interrupt transactions (using TT) that span frame boundaries + * into uframes 0/1; see 4.12.2.2. In those uframes, a "save place" FSTN + * makes the HC jump (back) to a QH to scan for fs/ls QH completions until + * it hits a "restore" FSTN; then it returns to finish other uframe 0/1 work. + */ +typedef struct _EHCI_FSTN +{ + ULONG hw_next; /* any periodic q entry */ + ULONG hw_prev; /* qh or EHCI_LIST_END */ + + /* the rest is HCD-private */ + PEHCI_ELEM_LINKS elem_head_link; + ULONG phys_addr; + ULONG reserved[ 4 ]; + +} EHCI_FSTN, *PEHCI_FSTN; // __attribute__ ((aligned (32))); + + +/* NOTE: urb->transfer_flags expected to not use this bit !!! */ +#define EHCI_STATE_UNLINK 0x8000 /* urb being unlinked */ + +/*-------------------------------------------------------------------------*/ + +/* EHCI register interface, corresponds to EHCI Revision 0.95 specification */ + +/* Section 2.2 Host Controller Capability Registers */ +#define HCS_DEBUG_PORT(p) (((p)>>20)&0xf) /* bits 23:20, debug port? */ +#define HCS_INDICATOR(p) ((p)&(1 << 16)) /* true: has port indicators */ +#define HCS_N_CC(p) (((p)>>12)&0xf) /* bits 15:12, #companion HCs */ +#define HCS_N_PCC(p) (((p)>>8)&0xf) /* bits 11:8, ports per CC */ +#define HCS_PORTROUTED(p) ((p)&(1 << 7)) /* true: port routing */ +#define HCS_PPC(p) ((p)&(1 << 4)) /* true: port power control */ +#define HCS_N_PORTS(p) (((p)>>0)&0xf) /* bits 3:0, ports on HC */ + +#define HCC_EXT_CAPS(p) (((p)>>8)&0xff) /* for pci extended caps */ +#define HCC_ISOC_CACHE(p) ((p)&(1 << 7)) /* true: can cache isoc frame */ +#define HCC_ISOC_THRES(p) (((p)>>4)&0x7) /* bits 6:4, uframes cached */ +#define HCC_CANPARK(p) ((p)&(1 << 2)) /* true: can park on async qh */ +#define HCC_PGM_FRAMELISTLEN(p) ((p)&(1 << 1)) /* true: periodic_size changes*/ +#define HCC_64BIT_ADDR(p) ((p)&(1)) /* true: can use 64-bit addr */ + +/* 23:16 is r/w intr rate, in microframes; default "8" == 1/msec */ +#define CMD_PARK (1<<11) /* enable "park" on async qh */ +#define CMD_PARK_CNT(c) (((c)>>8)&3) /* how many transfers to park for */ +#define CMD_LRESET (1<<7) /* partial reset (no ports, etc) */ +#define CMD_IAAD (1<<6) /* "doorbell" interrupt async advance */ +#define CMD_ASE (1<<5) /* async schedule enable */ +#define CMD_PSE (1<<4) /* periodic schedule enable */ +/* 3:2 is periodic frame list size */ +#define CMD_RESET (1<<1) /* reset HC not bus */ +#define CMD_RUN (1<<0) /* start/stop HC */ + +/* these STS_* flags are also intr_enable bits (USBINTR) */ +#define STS_IAA (1<<5) /* Interrupted on async advance */ +#define STS_FATAL (1<<4) /* such as some PCI access errors */ +#define STS_FLR (1<<3) /* frame list rolled over */ +#define STS_PCD (1<<2) /* port change detect */ +#define STS_ERR (1<<1) /* "error" completion (overflow, ...) */ +#define STS_INT (1<<0) /* "normal" completion (short, ...) */ + +#define STS_ASS (1<<15) /* Async Schedule Status */ +#define STS_PSS (1<<14) /* Periodic Schedule Status */ +#define STS_RECL (1<<13) /* Reclamation */ +#define STS_HALT (1<<12) /* Not running (any reason) */ + +/* 31:23 reserved */ +#define PORT_WKOC_E (1<<22) /* wake on overcurrent (enable) */ +#define PORT_WKDISC_E (1<<21) /* wake on disconnect (enable) */ +#define PORT_WKCONN_E (1<<20) /* wake on connect (enable) */ +/* 19:16 for port testing */ +/* 15:14 for using port indicator leds (if HCS_INDICATOR allows) */ +#define PORT_OWNER (1<<13) /* true: companion hc owns this port */ +#define PORT_POWER (1<<12) /* true: has power (see PPC) */ +#define PORT_USB11(x) (((x)&(3<<10))==(1<<10)) /* USB 1.1 device */ +/* 11:10 for detecting lowspeed devices (reset vs release ownership) */ +/* 9 reserved */ +#define PORT_PR (1<<8) /* reset port */ +#define PORT_SUSP (1<<7) /* suspend port */ +#define PORT_RESUME (1<<6) /* resume it */ +#define PORT_OCC (1<<5) /* over current change */ +#define PORT_OC (1<<4) /* over current active */ +#define PORT_PEC (1<<3) /* port enable change */ +#define PORT_PE (1<<2) /* port enable */ +#define PORT_CSC (1<<1) /* connect status change */ +#define PORT_CCS (1<<0) /* device connected */ + +#define FLAG_CF (1<<0) /* true: we'll support "high speed" */ + +typedef struct _EHCI_HCS_CONTENT +{ + ULONG port_count : 4; + ULONG port_power_control : 1; + ULONG reserved : 2; + ULONG port_rout_rules : 1; + ULONG port_per_chc : 4; + ULONG chc_count : 4; + ULONG port_indicator : 1; + ULONG reserved2 : 3; + ULONG dbg_port_num : 4; + ULONG reserved3 : 8; + +} EHCI_HCS_CONTENT, *PEHCI_HCS_CONTENT; + +typedef struct _EHCI_HCC_CONTENT +{ + ULONG cur_addr_bits : 1; /* 0: 32 bit addressing 1: 64 bit addressing */ + ULONG var_frame_list : 1; /* 0: 1024 frames, 1: support other number of frames */ + ULONG park_mode : 1; + ULONG reserved : 1; + ULONG iso_sched_threshold : 4; + ULONG eecp_capable : 8; + ULONG reserved2 : 16; + +} EHCI_HCC_CONTENT, *PEHCI_HCC_CONTENT; + +typedef struct _EHCI_CAPS { + UCHAR length; /* CAPLENGTH - size of this struct */ + UCHAR reserved; /* offset 0x1 */ + USHORT hci_version; /* HCIVERSION - offset 0x2 */ + ULONG hcs_params; /* HCSPARAMS - offset 0x4 */ + + ULONG hcc_params; /* HCCPARAMS - offset 0x8 */ + UCHAR portroute [8]; /* nibbles for routing - offset 0xC */ + +} EHCI_CAPS, *PEHCI_CAPS; + +/* Section 2.3 Host Controller Operational Registers */ + +#define EHCI_USBCMD 0x00 +#define EHCI_USBSTS 0x04 +#define EHCI_USBINTR 0x08 +#define EHCI_FRINDEX 0x0c +#define EHCI_CTRLDSSEGMENT 0x10 +#define EHCI_PERIODICLISTBASE 0x14 +#define EHCI_ASYNCLISTBASE 0x18 +#define EHCI_CONFIGFLAG 0x40 +#define EHCI_PORTSC 0x44 + +#define EHCI_USBINTR_INTE 0x01 +#define EHCI_USBINTR_ERR 0x02 +#define EHCI_USBINTR_PC 0x04 +#define EHCI_USBINTR_FLROVR 0x08 +#define EHCI_USBINTR_HSERR 0x10 +#define EHCI_USBINTR_ASYNC 0x20 + +typedef struct _EHCI_USBCMD_CONTENT +{ + ULONG run_stop : 1; + ULONG hcreset : 1; + ULONG frame_list_size : 2; + ULONG periodic_enable : 1; + ULONG async_enable : 1; + ULONG door_bell : 1; + ULONG light_reset : 1; + ULONG async_park_count : 2; + ULONG reserved : 1; + ULONG async_park_enable : 1; + ULONG reserved1 : 4; + ULONG int_threshold : 8; + ULONG reserved2 : 8; + +} EHCI_USBCMD_CONTENT, *PEHCI_USBCMD_CONTENT; + +typedef struct _EHCI_USBSTS_CONTENT +{ + ULONG ioc : 1; + ULONG trasac_error : 1; + ULONG port_change : 1; + ULONG fl_rollover : 1; + ULONG host_system_error : 1; + ULONG async_advance : 1; + ULONG reserved : 6; + ULONG hc_halted : 1; + ULONG reclaimation : 1; + ULONG periodic_status : 1; + ULONG async_status : 1; + ULONG reserved1 : 16; + +} EHCI_USBSTS_CONTENT, *PEHCI_USBSTS_CONTENT; + +typedef struct _EHCI_RHPORTSC_CONTENT +{ + ULONG cur_connect : 1; + ULONG cur_connect_change : 1; + ULONG port_enable : 1; + ULONG port_enable_change : 1; + ULONG over_current : 1; + ULONG over_current_change : 1; + ULONG force_port_resume : 1; + ULONG suspend : 1; + ULONG port_reset : 1; + ULONG reserved : 1; + ULONG line_status : 2; + ULONG port_power : 1; + ULONG port_owner : 1; + ULONG port_indicator : 2; + ULONG port_test : 4; + ULONG we_connect : 1; + ULONG we_disconnect : 1; + ULONG we_over_current : 1; + ULONG reserved1 : 9; + +} EHCI_RHPORTSC_CONTENT, *PEHCI_RHPORTSC_CONTENT; + +typedef struct _EHCI_REGS { + + ULONG command; + ULONG status; + ULONG intr_enable; + ULONG frame_index; /* current microframe number */ + ULONG segment; /* address bits 63:32 if needed */ + ULONG frame_list; /* points to periodic list */ + ULONG async_next; /* address of next async queue head */ + ULONG reserved [9]; + ULONG configured_flag; + ULONG port_status [0]; /* up to N_PORTS */ + +} EHCI_REGS, *PEHCI_REGS; + +#pragma pack( pop, usb_align ) + +/* ehci_hcd->lock guards shared data against other CPUs: + * ehci_hcd: async, reclaim, periodic (and shadow), ... + * hcd_dev: ep[] + * ehci_qh: qh_next, qtd_list + * ehci_qtd: qtd_list + * + * Also, hold this lock when talking to HC registers or + * when updating hw_* fields in shared qh/qtd/... structures. + */ + +#define EHCI_MAX_ROOT_PORTS 15 /* see HCS_N_PORTS */ + +#define EHCI_DEVICE_NAME "\\Device\\EHCI" + +#define EHCI_DOS_DEVICE_NAME "\\DosDevices\\EHCI" + +#define EHCI_ITD_POOL_IDX INIT_LIST_FLAG_ITD +#define EHCI_QH_POOL_IDX INIT_LIST_FLAG_QH +#define EHCI_SITD_POOL_IDX INIT_LIST_FLAG_SITD +#define EHCI_FSTN_POOL_IDX INIT_LIST_FLAG_FSTN +#define EHCI_QTD_POOL_IDX INIT_LIST_FLAG_QTD + +#define EHCI_DEFAULT_FRAMES UHCI_MAX_FRAMES +#define EHCI_MAX_SYNC_BUS_TIME 50000 // stands for 100000 ns, only to get wrapped within one word + +#define EHCI_SCHED_INT8_INDEX 0 +#define EHCI_SCHED_INT4_INDEX 1 +#define EHCI_SCHED_INT2_INDEX 2 +#define EHCI_SCHED_FSTN_INDEX 3 +#define EHCI_SCHED_INT1_INDEX 4 + +#define qtd_pool ( &ehci->elem_pools[ EHCI_QTD_POOL_IDX ] ) +#define qh_pool ( &ehci->elem_pools[ EHCI_QH_POOL_IDX ] ) +#define fstn_pool ( &ehci->elem_pools[ EHCI_FSTN_POOL_IDX ] ) +#define itd_pool ( &ehci->elem_pools[ EHCI_ITD_POOL_IDX ] ) +#define sitd_pool ( &ehci->elem_pools[ EHCI_SITD_POOL_IDX ] ) + + +typedef struct _EHCI_DEV +{ + HCD hcd_interf; + + EHCI_CAPS ehci_caps; + + PHYSICAL_ADDRESS ehci_reg_base; // io space + BOOL port_mapped; + PBYTE port_base; // note: added by ehci_caps.length, operational regs base addr, not the actural base + + ULONG frame_count; + PHYSICAL_ADDRESS frame_list_phys_addr; + + KSPIN_LOCK frame_list_lock; // run at DIRQL + PULONG frame_list; // periodic schedule + + PFRAME_LIST_CPU_ENTRY frame_list_cpu; // periodic schedule shadow + + LIST_HEAD urb_list; // active urb-list + + LIST_HEAD async_list_cpu; + LIST_HEAD periodic_list_cpu[ 8 ]; // each slot for one periodic + PEHCI_QH skel_async_qh; + + + // + // pools for device specific data + // + EHCI_ELEM_POOL elem_pools[ 5 ]; + + // + //for iso and int bandwidth claim, bandwidth schedule + // + KSPIN_LOCK pending_endp_list_lock; //lock to access the following two + LIST_HEAD pending_endp_list; + UHCI_PENDING_ENDP_POOL pending_endp_pool; + PUSHORT frame_bw; //unit uFrame + USHORT min_bw; //the bottle-neck of the bandwidths across frame-list + + KTIMER reset_timer; //used to reset the host controller + struct _EHCI_DEVICE_EXTENSION *pdev_ext; + PUSB_DEV root_hub; //root hub + +} EHCI_DEV, *PEHCI_DEV; + +typedef UHCI_PORT EHCI_MEMORY; + +typedef struct _EHCI_DEVICE_EXTENSION +{ + //struct _USB_DEV_MANAGER *pdev_mgr; + DEVEXT_HEADER dev_ext_hdr; + PDEVICE_OBJECT pdev_obj; + PDRIVER_OBJECT pdrvr_obj; + PEHCI_DEV ehci; + + //device resources + PADAPTER_OBJECT padapter; + ULONG map_regs; + PCM_RESOURCE_LIST res_list; + ULONG pci_addr; // bus number | slot number | funciton number + UHCI_INTERRUPT res_interrupt; + union + { + UHCI_PORT res_port; + EHCI_MEMORY res_memory; + }; + + PKINTERRUPT ehci_int; + KDPC ehci_dpc; + +} EHCI_DEVICE_EXTENSION, *PEHCI_DEVICE_EXTENSION; + +/*-------------------------------------------------------------------------*/ + +#endif /* __EHCI_H__ */ diff --git a/reactos/drivers/usb/nt4compat/usbdriver/ehci.rc b/reactos/drivers/usb/nt4compat/usbdriver/ehci.rc new file mode 100644 index 00000000000..d5153dcf41d --- /dev/null +++ b/reactos/drivers/usb/nt4compat/usbdriver/ehci.rc @@ -0,0 +1,45 @@ +// Resource script for USBISO driver +// Generated by Walt Oney's driver wizard + +#include + +LANGUAGE LANG_ENGLISH, SUBLANG_NEUTRAL + + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 0,0,1,0 + PRODUCTVERSION 0,0,1,0 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "Comments", "This is a beta version of usb driver stack( ehci ), contact me at mypublic99@yahoo.com\0" + VALUE "CompanyName", "Woodhead Software\0" + VALUE "FileDescription", "ehci.sys\0" + VALUE "FileVersion", "0, 0, 1, 0\0" + VALUE "InternalName", "ehci.sys\0" + VALUE "LegalCopyright", "Copyright © 2002-2004 Woodhead Software\0" + VALUE "LegalTrademarks", "\0" + VALUE "OriginalFilename", "ehci.sys\0" + VALUE "PrivateBuild", "0.01\0" + VALUE "ProductName", "usb driver stack for windows NT\0" + VALUE "ProductVersion", "0, 0, 1, 0\0" + VALUE "SpecialBuild", "0131.d\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + diff --git a/reactos/drivers/usb/nt4compat/usbdriver/etd.c b/reactos/drivers/usb/nt4compat/usbdriver/etd.c new file mode 100644 index 00000000000..c15e34d777c --- /dev/null +++ b/reactos/drivers/usb/nt4compat/usbdriver/etd.c @@ -0,0 +1,733 @@ +/** + * etd.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 "ehci.h" +#include "debug.h" + +#define init_elem( ptr, type, ehci_elem_type ) \ +{\ + PEHCI_ELEM_LINKS tmp;\ + ptr = ( type* )( plist->phys_bufs[ i ] + sizeof( type ) * j );\ + ptr->hw_next = ehci_elem_type << 1;\ + if( ehci_elem_type == INIT_LIST_FLAG_QTD )\ + ptr->hw_next = 0;\ + tmp = ptr->elem_head_link = &plist->elem_head_buf[ i * elms_per_page + j ];\ + InitializeListHead( &tmp->elem_link );\ + InitializeListHead( &tmp->sched_link );\ + InsertTailList( &plist->free_list, &tmp->elem_link );\ + tmp->list_link = plist;\ + tmp->pool_link = pinit_ctx->pool;\ + tmp->phys_part = ( PVOID )( ( ULONG )ptr | ( ehci_elem_type << 1 ) );\ + if( ehci_elem_type != INIT_LIST_FLAG_QTD )\ + ptr->phys_addr = ( plist->phys_addrs[ i ].LowPart + sizeof( type ) * j ) | ( ehci_elem_type << 1 );\ + else \ + ptr->phys_addr = plist->phys_addrs[ i ].LowPart + sizeof( type ) * j ;\ +} + +// get the actual max list count of the pool, two limit, max_elem_pool and max_lists_pool +#define get_max_lists_count( pOOL, max_liSTS ) \ +{\ + LONG ii1;\ + switch( elem_pool_get_type( pOOL ) )\ + {\ + case INIT_LIST_FLAG_QTD:\ + ii1 = EHCI_MAX_QTDS_LIST;\ + break;\ + case INIT_LIST_FLAG_FSTN:\ + ii1 = EHCI_MAX_QHS_LIST;\ + break;\ + case INIT_LIST_FLAG_SITD:\ + ii1 = EHCI_MAX_ITDS_LIST;\ + break;\ + case INIT_LIST_FLAG_QH: \ + ii1 = EHCI_MAX_SITDS_LIST;\ + break;\ + case INIT_LIST_FLAG_ITD: \ + ii1 = EHCI_MAX_FSTNS_LIST;\ + break;\ + }\ + max_liSTS = ( EHCI_MAX_ELEMS_POOL / ii1 ) > EHCI_MAX_LISTS_POOL ? EHCI_MAX_LISTS_POOL : ( EHCI_MAX_ELEMS_POOL / ii1 );\ +} + +VOID +elem_list_destroy_elem_list( +PEHCI_ELEM_LIST plist +); + +PLIST_ENTRY +elem_list_get_list_head( +PEHCI_ELEM_LIST plist +); + +LONG +elem_list_get_total_count( +PEHCI_ELEM_LIST plist +); + +LONG +elem_list_get_elem_size( +PEHCI_ELEM_LIST plist +); + +LONG +elem_list_get_link_offset( +PEHCI_ELEM_LIST plist +); + +LONG +elem_list_add_ref( +PEHCI_ELEM_LIST plist +); + +LONG +elem_list_release_ref( +PEHCI_ELEM_LIST plist +); + +LONG +elem_list_get_ref( +PEHCI_ELEM_LIST plist +); + +BOOL +elem_pool_lock( +PEHCI_ELEM_POOL pool, +BOOL at_dpc +) +{ + return TRUE; +} +BOOL +elem_pool_unlock( +PEHCI_ELEM_POOL pool, +BOOL at_dpc +) +{ + return TRUE; +} +LONG +get_elem_phys_part_size( +ULONG type +) +{ + // type is INIT_LIST_FLAG_XXX + LONG size; + + size = 0; + switch( type ) + { + case INIT_LIST_FLAG_ITD: + size = 64; + break; + case INIT_LIST_FLAG_SITD: + size = 28; + break; + case INIT_LIST_FLAG_QTD: + size = 32; + break; + case INIT_LIST_FLAG_QH: + size = 48; + break; + case INIT_LIST_FLAG_FSTN: + size = 8; + break; + } + return size; +} + +BOOL +elem_list_init_elem_list( +PEHCI_ELEM_LIST plist, +LONG init_flags, +PVOID context, +LONG count +) +{ + LONG pages, i, j, elms_per_page; + PEHCI_QH pqh; + PEHCI_ITD pitd; + PEHCI_SITD psitd; + PEHCI_QTD pqtd; + PEHCI_FSTN pfstn; + PINIT_ELEM_LIST_CONTEXT pinit_ctx; + + if( plist == NULL || context == NULL ) + return FALSE; + + RtlZeroMemory( plist, sizeof( EHCI_ELEM_LIST ) ); + + pinit_ctx = context; + + plist->destroy_list = elem_list_destroy_elem_list; + plist->get_list_head = elem_list_get_list_head; + plist->get_total_count = elem_list_get_total_count; + plist->get_elem_size = elem_list_get_elem_size; + plist->get_link_offset = elem_list_get_link_offset; + plist->add_ref = elem_list_add_ref; + plist->release_ref = elem_list_release_ref; + plist->get_ref = elem_list_get_ref; + + InitializeListHead( &plist->free_list ); + + switch( init_flags & 0x0f ) + { + case INIT_LIST_FLAG_ITD: + plist->total_count = EHCI_MAX_ITDS_LIST; + plist->elem_size = sizeof( EHCI_ITD ); + break; + case INIT_LIST_FLAG_QH: + plist->total_count = EHCI_MAX_QHS_LIST; + plist->elem_size = sizeof( EHCI_QH ); + break; + case INIT_LIST_FLAG_SITD: + plist->total_count = EHCI_MAX_SITDS_LIST; + plist->elem_size = sizeof( EHCI_SITD ); + break; + case INIT_LIST_FLAG_FSTN: + plist->total_count = EHCI_MAX_FSTNS_LIST; + plist->elem_size = sizeof( EHCI_FSTN ); + break; + case INIT_LIST_FLAG_QTD: + plist->total_count = EHCI_MAX_QTDS_LIST; + plist->elem_size = sizeof( EHCI_QTD ); + break; + default: + goto ERROR_OUT; + } + if( plist->elem_size & 0x1f ) + { + plist->total_count = 0; + goto ERROR_OUT; + } + + plist->flags = init_flags; + plist->parent_pool = pinit_ctx->pool; + plist->padapter = pinit_ctx->padapter; + pages = ( ( plist->elem_size * plist->total_count ) + ( PAGE_SIZE - 1 ) ) / PAGE_SIZE; + elms_per_page = PAGE_SIZE / plist->elem_size; + + plist->phys_addrs = usb_alloc_mem( NonPagedPool, + ( sizeof( PHYSICAL_ADDRESS ) + sizeof( PBYTE ) ) * pages + \ + sizeof( EHCI_ELEM_LINKS ) * plist->total_count ); + + if( plist->phys_addrs == NULL ) + { + plist->total_count = 0; + goto ERROR_OUT; + } + + plist->phys_bufs = ( PBYTE* )&plist->phys_addrs[ pages ]; + plist->elem_head_buf = ( PEHCI_ELEM_LINKS )&plist->phys_bufs[ pages ]; + RtlZeroMemory( plist->phys_addrs, + ( sizeof( PHYSICAL_ADDRESS ) + sizeof( PBYTE ) ) * pages + \ + sizeof( EHCI_ELEM_LINKS ) * plist->total_count ); + + for( i = 0; i < pages; i++ ) + { + plist->phys_bufs[ i ] = HalAllocateCommonBuffer( + plist->padapter, + PAGE_SIZE, + &plist->phys_addrs[ i ], + FALSE); + + if( plist->phys_bufs[ i ] == NULL ) + { + // failed, roll back + for( j = i - 1; j >= 0; j -- ) + HalFreeCommonBuffer( + plist->padapter, + PAGE_SIZE, + plist->phys_addrs[ j ], + plist->phys_bufs[ j ], + FALSE ); + goto ERROR_OUT; + } + RtlZeroMemory( plist->phys_bufs[ i ], PAGE_SIZE ); + for( j = 0; j < elms_per_page; j++ ) + { + switch( init_flags & 0xf ) + { + case INIT_LIST_FLAG_QH: + { + init_elem( pqh, EHCI_QH, INIT_LIST_FLAG_QH ); + break; + } + case INIT_LIST_FLAG_ITD: + { + init_elem( pitd, EHCI_ITD, INIT_LIST_FLAG_ITD ); + break; + } + case INIT_LIST_FLAG_QTD: + { + init_elem( pqtd, EHCI_QTD, INIT_LIST_FLAG_QTD ); + break; + } + case INIT_LIST_FLAG_SITD: + { + init_elem( psitd, EHCI_SITD, INIT_LIST_FLAG_SITD ); + break; + } + case INIT_LIST_FLAG_FSTN: + { + init_elem( pfstn, EHCI_FSTN, INIT_LIST_FLAG_FSTN ); + break; + } + default: + TRAP(); + } + } + } + return TRUE; +ERROR_OUT: + + if( plist->phys_addrs != NULL ) + usb_free_mem( plist->phys_addrs ); + + RtlZeroMemory( plist, sizeof( EHCI_ELEM_LIST ) ); + return FALSE; +} + +VOID +elem_list_destroy_elem_list( +PEHCI_ELEM_LIST plist +) +{ + LONG i, pages; + + if( plist == NULL ) + return; + + pages = ( plist->total_count * plist->elem_size + PAGE_SIZE - 1 ) / PAGE_SIZE; + for( i = 0; i < pages; i++ ) + HalFreeCommonBuffer( + plist->padapter, + PAGE_SIZE, + plist->phys_addrs[ i ], + plist->phys_bufs[ i ], + FALSE ); + + usb_free_mem( plist->phys_addrs ); + RtlZeroMemory( plist, sizeof( EHCI_ELEM_LIST ) ); +} + +PLIST_ENTRY +elem_list_get_list_head( +PEHCI_ELEM_LIST plist +) +{ + if( plist == NULL ) + return NULL; + return &plist->free_list; +} + +LONG +elem_list_get_total_count( +PEHCI_ELEM_LIST plist +) +{ + if( plist == NULL ) + return 0; + return plist->total_count;; +} + +LONG +elem_list_get_elem_size( +PEHCI_ELEM_LIST plist +) +{ + if( plist == NULL ) + return 0; + return plist->elem_size; +} + +LONG +elem_list_get_link_offset( +PEHCI_ELEM_LIST plist +) +{ + if( plist == NULL ) + return 0; + + return get_elem_phys_part_size( plist->flags & 0xf ); +} + +LONG +elem_list_add_ref( +PEHCI_ELEM_LIST plist +) +{ + plist->reference++; + return plist->reference; +} + +LONG +elem_list_release_ref( +PEHCI_ELEM_LIST plist +) +{ + plist->reference--; + return plist->reference; +} + +LONG +elem_list_get_ref( +PEHCI_ELEM_LIST plist +) +{ + return plist->reference; +} + +// +// pool methods +// + +BOOL +elem_pool_init_pool( +PEHCI_ELEM_POOL pool, +LONG flags, +PVOID context +) +{ + PADAPTER_OBJECT padapter; + INIT_ELEM_LIST_CONTEXT init_ctx; + + if( pool == NULL || context == NULL ) + return FALSE; + + RtlZeroMemory( pool, sizeof( EHCI_ELEM_POOL ) ); + + init_ctx.pool = pool; + init_ctx.padapter = context; + + pool->elem_lists[ 0 ] = usb_alloc_mem( NonPagedPool, sizeof( EHCI_ELEM_LIST ) ); + + if( pool->elem_lists[ 0 ] == NULL ) + return FALSE; + + if( elem_list_init_elem_list( pool->elem_lists[ 0 ], flags, &init_ctx, 0 ) == FALSE ) + { + usb_free_mem( pool->elem_lists[ 0 ] ); + return FALSE; + } + pool->link_offset = pool->elem_lists[ 0 ]->get_link_offset( pool->elem_lists[ 0 ] ); + pool->free_count = pool->elem_lists[ 0 ]->get_total_count( pool->elem_lists[ 0 ] ); + pool->list_count = 1; + pool->flags = flags; + + return TRUE; +} + +LONG +elem_pool_get_link_offset( +PEHCI_ELEM_POOL elem_pool +) +{ + return elem_pool->link_offset; +} + +LONG +elem_pool_get_total_count( +PEHCI_ELEM_POOL elem_pool +) +{ + return elem_pool->elem_lists[ 0 ]->get_total_count( elem_pool->elem_lists[ 0 ] ) * elem_pool->list_count; +} + +VOID +elem_pool_destroy_pool( +PEHCI_ELEM_POOL pool +) +{ + LONG i; + if( pool == NULL ) + return; + for( i = pool->list_count - 1; i >= 0; i-- ) + { + pool->elem_lists[ i ]->destroy_list( pool->elem_lists[ i ] ); + usb_free_mem( pool->elem_lists[ i ] ); + pool->elem_lists[ i ] = NULL; + } + RtlZeroMemory( pool, sizeof( EHCI_ELEM_POOL ) ); + return; +} + +PEHCI_ELEM_LINKS +elem_pool_alloc_elem( +PEHCI_ELEM_POOL pool +) +{ + LONG i; + PEHCI_ELEM_LIST pel; + PLIST_HEAD lh; + PEHCI_ELEM_LINKS elnk; + + if( pool == NULL ) + return NULL; + + for( i = 0; i < pool->list_count; i++ ) + { + pel = pool->elem_lists[ i ]; + if( pel->get_ref( pel ) == pel->get_total_count( pel ) ) + continue; + break; + } + if( i == pool->list_count ) + { + if( elem_pool_expand_pool( pool, pel->get_total_count( pel ) ) == FALSE ) + return NULL; + pel = pool->elem_lists[ i ]; + } + + lh = pel->get_list_head( pel ); + elnk = ( PEHCI_ELEM_LINKS )RemoveHeadList( lh ); + InitializeListHead( &elnk->elem_link ); + InitializeListHead( &elnk->sched_link ); + + pel->add_ref( pel ); + pool->free_count--; + + return elnk; +} + +VOID +elem_pool_free_elem( +PEHCI_ELEM_LINKS elem_link +) +{ + PLIST_HEAD lh; + LONG ref; + PEHCI_ELEM_POOL pool; + if( elem_link == NULL ) + return; + pool = elem_link->pool_link; + lh = elem_link->list_link->get_list_head( elem_link->list_link ); + if( lh == NULL ) + return; + InsertHeadList( lh, ( PLIST_ENTRY )elem_link ); + ref = elem_link->list_link->release_ref( elem_link->list_link ); + pool->free_count++; + if( ref == 0 ) + elem_pool_collect_garbage( pool ); + return; +} + +BOOL +elem_pool_is_empty( +PEHCI_ELEM_POOL pool +) +{ + PEHCI_ELEM_LIST pel; + + if( pool == NULL ) + return TRUE; + pel = pool->elem_lists[ 0 ]; + return ( pool->list_count == 1 && pool->free_count == pel->get_total_count( pel ) ); +} + +LONG +elem_pool_get_free_count( +PEHCI_ELEM_POOL pool +) +{ + if( pool == NULL ) + return 0; + return pool->free_count; +} + +PEHCI_ELEM_LINKS +elem_pool_alloc_elems( +PEHCI_ELEM_POOL pool, +LONG count +) +{ + LIST_HEAD lh; + PLIST_ENTRY pthis; + LONG i, alloc_count, max_pool_lists; + PEHCI_ELEM_LIST pel; + PEHCI_ELEM_LINKS elnk; + // calculate to see if the count is affordable + + if( pool == NULL || count <= 0 ) + return NULL; + + get_max_lists_count( pool, max_pool_lists ); + InitializeListHead( &lh ); + pel = pool->elem_lists[ 0 ]; + if( count <= pool->free_count ) + alloc_count = 0; + else + alloc_count = count - pool->free_count; + + if( alloc_count > pel->get_total_count( pel ) * ( max_pool_lists - pool->list_count ) ) + return NULL; + + for( i = 0; i < count; i++ ) + { + if( ( elnk = elem_pool_alloc_elem( pool ) ) == NULL ) + { + // undo what we have done + while( IsListEmpty( &lh ) == FALSE ) + { + pthis = RemoveHeadList( &lh ); + elnk = struct_ptr( pthis, EHCI_ELEM_LINKS, elem_link ); + elem_pool_free_elem( elnk ); + } + return NULL; + } + InsertTailList( &lh, &elnk->elem_link ); + } + ListFirst( &lh, pthis ); + elnk = struct_ptr( pthis, EHCI_ELEM_LINKS, elem_link ); + RemoveEntryList( &lh ); + return elnk; +} + +BOOL +elem_pool_free_elems( +PEHCI_ELEM_LINKS elem_chains +) +{ + // note: no list head exists. + LIST_HEAD lh; + PEHCI_ELEM_LINKS elnk; + + InsertTailList( &elem_chains->elem_link, &lh ); + while( IsListEmpty( &lh ) == FALSE ) + { + elnk = ( PEHCI_ELEM_LINKS )RemoveHeadList( &lh ); + elem_pool_free_elem( elnk ); + } + return TRUE; +} +LONG +elem_pool_get_type( +PEHCI_ELEM_POOL pool +) +{ + if( pool == NULL ) + return -1; + return ( pool->flags & 0xf ); +} + +BOOL +elem_pool_expand_pool( +PEHCI_ELEM_POOL pool, +LONG elem_count +) +{ + LONG elem_cnt_list, list_count, i, j; + INIT_ELEM_LIST_CONTEXT init_ctx; + + if( pool == NULL || elem_count <= 0 || elem_count > EHCI_MAX_ELEMS_POOL ) + return FALSE; + + init_ctx.pool = pool; + init_ctx.padapter = pool->elem_lists[ 0 ]->padapter; + + elem_cnt_list = pool->elem_lists[ 0 ]->get_total_count( pool->elem_lists[ 0 ] ); + list_count = ( elem_count + elem_cnt_list - 1 ) / elem_cnt_list; + get_max_lists_count( pool, i ); + + if( list_count + pool->list_count > i ) + return FALSE; + + for( i = pool->list_count; i < list_count + pool->list_count; i++ ) + { + pool->elem_lists[ i ] = usb_alloc_mem( NonPagedPool, sizeof( EHCI_ELEM_LIST ) ); + if( elem_list_init_elem_list( pool->elem_lists[ i ], pool->flags, &init_ctx, 0 ) == FALSE ) + break; + } + + if( i < list_count + pool->list_count ) + { + // undo all we have done + for( j = pool->list_count; j < pool->list_count + i; j++ ) + { + pool->elem_lists[ j ]->destroy_list( pool->elem_lists[ j ] ); + usb_free_mem( pool->elem_lists[ j ] ); + pool->elem_lists[ j ] = NULL; + } + return FALSE; + } + + // update pool + pool->free_count += elem_cnt_list * list_count; + pool->list_count += list_count; + return TRUE; +} + +BOOL +elem_pool_collect_garbage( +PEHCI_ELEM_POOL pool +) +{ + LONG i, j, k, fl; + LONG free_elem_lists[ EHCI_MAX_LISTS_POOL - 1 ]; + PEHCI_ELEM_LIST pel; + + if( pool == NULL ) + return FALSE; + + for( i = 1, fl = 0; i < pool->list_count; i++ ) + { + if( pool->elem_lists[ i ]->get_ref( pool->elem_lists[ i ] ) == 0 ) + { + free_elem_lists[ fl++ ] = i; + } + } + for( j = fl - 1; j >= 0; j-- ) + { + pel = pool->elem_lists[ free_elem_lists[ j ] ]; + pel->destroy_list( pel ); + usb_free_mem( pel ); + + for( k = free_elem_lists[ j ] + 1; k < pool->list_count; k++ ) + { + // shrink the elem_lists + pool->elem_lists[ k - 1 ] = pool->elem_lists[ k ]; + } + pool->elem_lists[ k ] = NULL; + pel = pool->elem_lists[ 0 ]; + pool->free_count -= pel->get_total_count( pel ); + pool->list_count --; + } + return TRUE; +} + +BOOL +elem_pool_can_transfer( +PEHCI_ELEM_POOL pool, +LONG td_count +) +{ + LONG i; + if( pool == NULL || td_count <= 0 ) + return FALSE; + get_max_lists_count( pool, i ); + if( ( i - pool->list_count ) + * pool->elem_lists[ 0 ]->get_total_count( pool->elem_lists[ 0 ] ) + + pool->free_count < td_count ) + return FALSE; + return TRUE; +} + +//---------------------------------------------------------- + diff --git a/reactos/drivers/usb/nt4compat/usbdriver/gendrv.c b/reactos/drivers/usb/nt4compat/usbdriver/gendrv.c new file mode 100644 index 00000000000..f15c55d7f8b --- /dev/null +++ b/reactos/drivers/usb/nt4compat/usbdriver/gendrv.c @@ -0,0 +1,2076 @@ +/** + * gendrv.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 + */ + +//this driver is part of the dev manager responsible to manage non-driver device + +#include "td.h" +#include "ntddk.h" +#include "umss.h" +#include "usb.h" +#include "hub.h" +#include "debug.h" +#include "gendrv.h" +#include "stdio.h" + +#define if_dev( dev_obj ) \ +( ( ( ( PGENDRV_DEVICE_EXTENSION)dev_obj->DeviceExtension )->pdriver->driver_desc.flags & USB_DRIVER_FLAG_IF_CAPABLE ) != 0 ) + +#define GENDRV_EXIT_DISPATCH( dev_OBJ, staTUS, iRp ) \ +{\ + iRp->IoStatus.Status = staTUS;\ + if( staTUS != STATUS_PENDING)\ + {\ + IoCompleteRequest( iRp, IO_NO_INCREMENT);\ + return staTUS;\ + }\ + IoMarkIrpPending( iRp );\ + IoStartPacket( dev_OBJ, iRp, NULL, gendrv_cancel_queued_irp ); \ + return STATUS_PENDING;\ +} + +#define GENDRV_COMPLETE_START_IO( dev_OBJ, staTUS, iRP ) \ +{\ + iRP->IoStatus.Status = staTUS;\ + if( staTUS != STATUS_PENDING )\ + {\ + IoStartNextPacket( dev_OBJ, TRUE );\ + IoCompleteRequest( iRP, IO_NO_INCREMENT );\ + }\ + return;\ +} + +extern POBJECT_TYPE *IoDriverObjectType; + +extern VOID +disp_urb_completion( +PURB purb, +PVOID context +); + + +VOID +disp_noio_urb_completion( +PURB purb, +PVOID context +); + +NTSYSAPI +NTSTATUS +NTAPI +ZwLoadDriver( +IN PUNICODE_STRING DriverServiceName +); + +NTSYSAPI +NTSTATUS +NTAPI +ZwClose( +IN HANDLE Handle +); + +NTSYSAPI +NTSTATUS +NTAPI +ObOpenObjectByName( +IN POBJECT_ATTRIBUTES ObjectAttributes, +IN POBJECT_TYPE ObjectType OPTIONAL, +IN KPROCESSOR_MODE AccessMode, +IN OUT PACCESS_STATE AccessState OPTIONAL, +IN ACCESS_MASK DesiredAccess OPTIONAL, +IN OUT PVOID ParseContext OPTIONAL, +OUT PHANDLE Handle +); + +BOOL +gendrv_if_driver_destroy( +PUSB_DEV_MANAGER dev_mgr, +PUSB_DRIVER pdriver +); + +VOID +gendrv_set_cfg_completion( +PURB purb, +PVOID context +); + +BOOL +gendrv_connect( +PCONNECT_DATA param, +DEV_HANDLE dev_handle +); + +BOOL +gendrv_stop( +PUSB_DEV_MANAGER dev_mgr, +DEV_HANDLE dev_handle +); + +BOOL +gendrv_disconnect( +PUSB_DEV_MANAGER dev_mgr, +DEV_HANDLE dev_handle +); + +VOID +gendrv_startio( +IN PDEVICE_OBJECT dev_obj, +IN PIRP irp +); + +VOID +gendrv_cancel_queued_irp( +PDEVICE_OBJECT pdev_obj, +PIRP pirp +); + +VOID +gendrv_release_ext_drvr_entry( +PGENDRV_DRVR_EXTENSION pdrvr_ext, +PGENDRV_EXT_DRVR_ENTRY pentry +); + +VOID +gendrv_clean_up_queued_irps( +PDEVICE_OBJECT dev_obj +); + +PGENDRV_EXT_DRVR_ENTRY +gendrv_alloc_ext_drvr_entry( +PGENDRV_DRVR_EXTENSION pdrvr_ext +); + +PDRIVER_OBJECT +gendrv_open_ext_driver( +PUNICODE_STRING unicode_string +); + +NTSTATUS +gendrv_get_key_value( +IN HANDLE KeyHandle, +IN PWSTR ValueName, +OUT PKEY_VALUE_FULL_INFORMATION *Information +); + +NTSTATUS +gendrv_open_reg_key( +OUT PHANDLE handle, +IN HANDLE base_handle OPTIONAL, +IN PUNICODE_STRING keyname, +IN ACCESS_MASK desired_access, +IN BOOLEAN create +); + +BOOL +gendrv_do_disconnect( +PUSB_DEV_MANAGER dev_mgr, +DEV_HANDLE if_handle, +BOOL is_if +); + +BOOL +gendrv_do_stop( +PUSB_DEV_MANAGER dev_mgr, +DEV_HANDLE dev_handle, +BOOL is_if +); + +NTSTATUS +gendrv_send_pnp_msg( +ULONG msg, +PDEVICE_OBJECT pdev_obj, +PVOID pctx +); + +BOOL +gendrv_delete_device( +PUSB_DEV_MANAGER dev_mgr, +PDEVICE_OBJECT dev_obj +); + +PDEVICE_OBJECT +gendrv_create_device( +PUSB_DEV_MANAGER dev_mgr, +PUSB_DRIVER gen_drvr, +DEV_HANDLE dev_handle +); + +PDRIVER_OBJECT +gendrv_load_ext_drvr( +PGENDRV_DRVR_EXTENSION pdrvr_ext, +PUSB_DESC_HEADER pdesc +); + +PDRIVER_OBJECT +gendrv_find_drvr_by_key( +PGENDRV_DRVR_EXTENSION pdrvr_ext, +ULONG key +); + +ULONG +gendrv_make_key( +PUSB_DESC_HEADER pdesc +); + +BOOL +gendrv_driver_init( +PUSB_DEV_MANAGER dev_mgr, +PUSB_DRIVER pdriver +) +{ + PGENDRV_DRVR_EXTENSION pdrvr_ext; + + if( dev_mgr == NULL || pdriver == NULL ) + return FALSE; + + pdriver->driver_desc.flags = USB_DRIVER_FLAG_DEV_CAPABLE; + pdriver->driver_desc.vendor_id = 0xffff; // USB Vendor ID + pdriver->driver_desc.product_id = 0xffff; // USB Product ID. + pdriver->driver_desc.release_num = 0x100; // Release Number of Device + + pdriver->driver_desc.config_val = 0; // Configuration Value + pdriver->driver_desc.if_num = 0; // Interface Number + pdriver->driver_desc.if_class = 0xff; // Interface Class + pdriver->driver_desc.if_sub_class = 0xff; // Interface SubClass + pdriver->driver_desc.if_protocol = 0xff; // Interface Protocol + + pdriver->driver_desc.driver_name = "USB generic dev driver"; // Driver name for Name Registry + pdriver->driver_desc.dev_class = USB_CLASS_VENDOR_SPEC; + pdriver->driver_desc.dev_sub_class = 0; // Device Subclass + pdriver->driver_desc.dev_protocol = 0; // Protocol Info. + + pdriver->driver_ext = usb_alloc_mem( NonPagedPool, sizeof( GENDRV_DRVR_EXTENSION ) ); + pdriver->driver_ext_size = sizeof( GENDRV_DRVR_EXTENSION ); + + RtlZeroMemory( pdriver->driver_ext, pdriver->driver_ext_size ); + pdrvr_ext = ( PGENDRV_DRVR_EXTENSION )pdriver->driver_ext; + + // InitializeListHead( &pdrvr_ext->dev_list ); + InitializeListHead( &pdrvr_ext->ext_drvr_list ); + pdrvr_ext->ext_drvr_count = 0; + ExInitializeFastMutex( &pdrvr_ext->drvr_ext_mutex ); + + pdriver->disp_tbl.version = 1; + pdriver->disp_tbl.dev_connect = gendrv_connect; + pdriver->disp_tbl.dev_disconnect = gendrv_disconnect; + pdriver->disp_tbl.dev_stop = gendrv_stop; + pdriver->disp_tbl.dev_reserved = NULL; + + return TRUE; +} + +BOOL +gendrv_driver_destroy( +PUSB_DEV_MANAGER dev_mgr, +PUSB_DRIVER pdriver +) +{ + return gendrv_if_driver_destroy( dev_mgr, pdriver ); +} + +BOOL +gendrv_connect( +PCONNECT_DATA param, +DEV_HANDLE dev_handle +) +{ + PURB purb; + PUSB_CTRL_SETUP_PACKET psetup; + NTSTATUS status; + PUCHAR buf; + LONG credit, i, j, match; + PUSB_CONFIGURATION_DESC pconfig_desc; + PUSB_INTERFACE_DESC pif_desc; + PUSB_DEV_MANAGER dev_mgr; + + if( param == NULL || dev_handle == 0 ) + return FALSE; + + dev_mgr = param->dev_mgr; + + // let's set the configuration + purb = usb_alloc_mem( NonPagedPool, sizeof( URB ) ); + if( purb == NULL ) + return FALSE; + + buf = usb_alloc_mem( NonPagedPool, 512 ); + if( buf == NULL ) + { + usb_dbg_print( DBGLVL_MAXIMUM, ( "gendrv_connect(): can not alloc buf\n" ) ); + usb_free_mem( purb ); + return FALSE; + } + + // before we set the configuration, let's search to find if there + // exist interfaces we supported + psetup = ( PUSB_CTRL_SETUP_PACKET )( purb )->setup_packet; + urb_init( ( purb ) ); + purb->endp_handle = dev_handle | 0xffff; + purb->data_buffer = buf; + purb->data_length = 512; + purb->completion = NULL; // this is an immediate request, no completion required + purb->context = NULL; + purb->reference = 0; + psetup->bmRequestType = 0x80; + psetup->bRequest = USB_REQ_GET_DESCRIPTOR; + psetup->wValue = USB_DT_CONFIG << 8; + psetup->wIndex = 0; + psetup->wLength = 512; + + status = usb_submit_urb( dev_mgr, purb ); + if( status == STATUS_PENDING ) + { + TRAP(); + usb_free_mem( buf ); + usb_free_mem( purb ); + return FALSE; + } + + // check the config desc valid + pconfig_desc = ( PUSB_CONFIGURATION_DESC )buf; + if( pconfig_desc->wTotalLength > 512 ) + { + usb_free_mem( buf ); + usb_free_mem( purb ); + usb_dbg_print( DBGLVL_MAXIMUM, ( "gendrv_connect(): error, bad configuration desc\n" ) ); + return FALSE; + } + + i = pconfig_desc->bConfigurationValue; + usb_free_mem( buf ); + buf = NULL; + + //set the configuration + urb_init( purb ); + purb->endp_handle = dev_handle | 0xffff; + purb->data_buffer = NULL; + purb->data_length = 0; + purb->completion = gendrv_set_cfg_completion; + purb->context = dev_mgr; + purb->reference = ( ULONG )param->pdriver; + psetup->bmRequestType = 0; + psetup->bRequest = USB_REQ_SET_CONFIGURATION; + psetup->wValue = ( USHORT ) i; + psetup->wIndex = 0; + psetup->wLength = 0; + + usb_dbg_print( DBGLVL_MAXIMUM, ( "gendrv_connect(): start config the device, cfgval=%d\n", i ) ); + status = usb_submit_urb( dev_mgr, purb ); + + if( status != STATUS_PENDING ) + { + usb_free_mem( purb ); + + if( status == STATUS_SUCCESS ) + return TRUE; + + return FALSE; + } + + return TRUE; +} + +BOOL +gendrv_event_select_driver( +PUSB_DEV pdev, //always null. we do not use this param +ULONG event, +ULONG context, +ULONG param +) +{ + // + // try to search the registry to find one driver. + // if found, create the PDO, load the driver. + // and call its AddDevice. + // + LONG i; + NTSTATUS status; + PUSB_DRIVER pdrvr; + PGENDRV_DRVR_EXTENSION pdrvr_ext; + PGENDRV_EXT_DRVR_ENTRY pentry; + PGENDRV_DEVICE_EXTENSION pdev_ext; + PUSB_CONFIGURATION_DESC pconfig_desc; + PUSB_DEV_MANAGER dev_mgr; + + PDEVICE_OBJECT pdev_obj; + PDRIVER_OBJECT pdrvr_obj; + HANDLE handle; + PLIST_ENTRY pthis, pnext; + + USE_IRQL; + + if( pdev == NULL ) + return FALSE; + + usb_dbg_print( DBGLVL_MAXIMUM, ( "gendrv_event_select_driver(): entering...\n" ) ); + + pdrvr = ( PUSB_DRIVER )param; + pconfig_desc = ( PUSB_CONFIGURATION_DESC )pdev->desc_buf[ sizeof( USB_DEVICE_DESC ) ]; + pdrvr_ext = ( PGENDRV_DRVR_EXTENSION )pdrvr->driver_ext; + + // + // well, let's do the hard work to see if there is a class driver + // for this device. + // in the event routine, we have no need to check if the device is zomb or + // not, it must be alive there. + // + i = gendrv_make_key( ( PUSB_DESC_HEADER )pdev->pusb_dev_desc ); + if( i == -1 ) + { + return FALSE; + } + + pdrvr_obj = gendrv_find_drvr_by_key( pdrvr_ext, ( ULONG )i ); + if( !pdrvr_obj ) + { + if( ( pdrvr_obj = gendrv_load_ext_drvr( pdrvr_ext, ( PUSB_DESC_HEADER )pdev->pusb_dev_desc ) ) == NULL ) + return FALSE; + } + + dev_mgr = dev_mgr_from_dev( pdev ); + pdev_obj = gendrv_create_device( dev_mgr, pdrvr, usb_make_handle( pdev->dev_id, 0, 0 ) ); + if( pdev_obj == NULL ) + { + goto ERROR_OUT; + } + + lock_dev( pdev, FALSE ); + if( dev_state( pdev ) == USB_DEV_STATE_ZOMB || + dev_mgr_set_driver( dev_mgr, usb_make_handle( pdev->dev_id, 0, 0 ), pdrvr, pdev ) == FALSE ) + { + unlock_dev( pdev, FALSE ); + gendrv_delete_device( dev_mgr, pdev_obj ); + goto ERROR_OUT; + } + + if( pdev->usb_config ) + { + pdev->dev_obj = pdev_obj; + } + + unlock_dev( pdev, FALSE ); + + pdev_ext = ( PGENDRV_DEVICE_EXTENSION )pdev_obj->DeviceExtension; + pdev_ext->desc_buf = usb_alloc_mem( NonPagedPool, 512 ); + RtlCopyMemory( pdev_ext->desc_buf, pconfig_desc, 512 ); + + // insert the device to the dev_list + ExAcquireFastMutex( &pdrvr_ext->drvr_ext_mutex ); + ListFirst( &pdrvr_ext->ext_drvr_list, pthis ); + pentry = NULL; + while( pthis ) + { + pentry = ( PGENDRV_EXT_DRVR_ENTRY )pthis; + if( pentry->pext_drvr == pdrvr_obj ) + break; + ListNext( &pdrvr_ext->ext_drvr_list, pthis, pnext ); + pthis = pnext; + pentry = NULL; + } + ASSERT( pentry ); + InsertTailList( &pentry->dev_list, &pdev_ext->dev_obj_link ); + pdev_ext->ext_drvr_entry = pentry; + pentry->ref_count++; + ExReleaseFastMutex( &pdrvr_ext->drvr_ext_mutex ); + + // notify the class driver, some device comes + gendrv_send_pnp_msg( GENDRV_MSG_ADDDEVICE, pdev_obj, pdrvr_obj ); + usb_unlock_dev( pdev ); + return TRUE; + +ERROR_OUT: + + usb_unlock_dev( pdev ); + return FALSE; +} + +VOID +gendrv_set_cfg_completion( +PURB purb, +PVOID context +) +{ + DEV_HANDLE dev_handle; + PUSB_DEV_MANAGER dev_mgr; + PWORK_QUEUE_ITEM pwork_item; + PUSB_DRIVER pdriver; + NTSTATUS status; + PUSB_DEV pdev; + PUSB_EVENT pevent; + USE_IRQL; + + if( purb == NULL || context == NULL ) + return; + + dev_handle = purb->endp_handle & ~0xffff; + dev_mgr = ( PUSB_DEV_MANAGER ) context; + pdriver = ( PUSB_DRIVER )purb->reference; + + if( purb->status != STATUS_SUCCESS ) + { + usb_free_mem( purb ); + return; + } + + usb_free_mem( purb ); + purb = NULL; + + // set the dev state + status = usb_query_and_lock_dev( dev_mgr, purb->endp_handle, &pdev ); + if( status != STATUS_SUCCESS ) + { + usb_unlock_dev( pdev ); + return; + } + usb_unlock_dev( pdev ); // safe to release the pdev ref since we are in urb completion + + + KeAcquireSpinLockAtDpcLevel( &dev_mgr->event_list_lock ); + lock_dev( pdev, TRUE ); + + if( dev_state( pdev ) >= USB_DEV_STATE_BEFORE_ZOMB ) + { + unlock_dev( pdev, TRUE ); + KeReleaseSpinLockFromDpcLevel( &dev_mgr->event_list_lock ); + return; + } + + if( dev_mgr_set_driver( dev_mgr, dev_handle, pdriver, pdev ) == FALSE ) + { + unlock_dev( pdev, TRUE ); + KeReleaseSpinLockFromDpcLevel( &dev_mgr->event_list_lock ); + return; + } + + //transit the state to configured + pdev->flags &= ~USB_DEV_STATE_MASK; + pdev->flags |= USB_DEV_STATE_CONFIGURED; + + pevent = alloc_event( &dev_mgr->event_pool, 1 ); + if( pevent == NULL ) + { + unlock_dev( pdev, TRUE ); + KeReleaseSpinLockFromDpcLevel( &dev_mgr->event_list_lock ); + } + + pevent->flags = USB_EVENT_FLAG_ACTIVE; + pevent->event = USB_EVENT_DEFAULT; + pevent->pdev = pdev; + pevent->context = 0; + pevent->param = ( ULONG )pdriver; + pevent->pnext = 0; //vertical queue for serialized operation + pevent->process_event = gendrv_event_select_driver; + pevent->process_queue = event_list_default_process_queue; + + InsertTailList( &dev_mgr->event_list, &pevent->event_link ); + KeSetEvent( &dev_mgr->wake_up_event, 0, FALSE ); + unlock_dev( pdev, TRUE ); + KeReleaseSpinLockFromDpcLevel( &dev_mgr->event_list_lock ); + + return; +} + + +BOOL +gendrv_stop( +PUSB_DEV_MANAGER dev_mgr, +DEV_HANDLE dev_handle +) +{ + if( dev_mgr == NULL ) + return FALSE; + return gendrv_do_stop( dev_mgr, dev_handle, FALSE ); +} + +BOOL +gendrv_disconnect( +PUSB_DEV_MANAGER dev_mgr, +DEV_HANDLE dev_handle +) +{ + if( dev_mgr == NULL ) + return FALSE; + return gendrv_do_disconnect( dev_mgr, dev_handle, FALSE ); +} + +BOOL +gendrv_build_reg_string( +PUSB_DESC_HEADER pdesc, +PUNICODE_STRING pus +) +{ + + CHAR desc_str[ 128 ]; + STRING atemp; + + if( pdesc == NULL || pus == NULL ) + return FALSE; + + if( pdesc->bDescriptorType == USB_DT_DEVICE ) + { + PUSB_DEVICE_DESC pdev_desc; + pdev_desc = ( PUSB_DEVICE_DESC )pdesc; + sprintf( desc_str, "%sv_%04x&p_%04x", + "\\Registry\\Machine\\System\\CurrentControlSet\\Services\\ehci\\device\\", + pdev_desc->idVendor, + pdev_desc->idProduct ); + } + else if( pdesc->bDescriptorType == USB_DT_INTERFACE ) + { + PUSB_INTERFACE_DESC pif_desc; + pif_desc = ( PUSB_INTERFACE_DESC )pdesc; + sprintf( desc_str, "%sc_%04x&s_%04x&p_%04x", + "\\Registry\\Machine\\System\\CurrentControlSet\\Services\\ehci\\interface\\", + pif_desc->bInterfaceClass, + pif_desc->bInterfaceSubClass, + pif_desc->bInterfaceProtocol ); + } + else + return FALSE; + + RtlInitString( &atemp, desc_str ); + RtlAnsiStringToUnicodeString( pus, &atemp, TRUE ); + return TRUE; +} + +ULONG +gendrv_make_key( +PUSB_DESC_HEADER pdesc +) +{ + PUSB_DEVICE_DESC pdev_desc; + PUSB_INTERFACE_DESC pif_desc; + + if( pdesc == NULL ) + return ( ULONG )-1; + if( pdesc->bDescriptorType == USB_DT_DEVICE ) + { + pdev_desc = ( PUSB_DEVICE_DESC )pdesc; + return ( ( ( ( ULONG )pdev_desc->idVendor ) << 16 ) | pdev_desc->idProduct ); + } + else if( pdesc->bDescriptorType == USB_DT_INTERFACE ) + { + pif_desc = ( PUSB_INTERFACE_DESC )pdesc; + return ( ( ( ( ULONG )pif_desc->bInterfaceClass ) << 16 ) | \ + ( ( ( ULONG )pif_desc->bInterfaceSubClass ) << 8 ) | \ + ( ( ULONG )pif_desc->bInterfaceProtocol ) ); + } + return ( ULONG )-1; +} + +PDRIVER_OBJECT +gendrv_find_drvr_by_key( +PGENDRV_DRVR_EXTENSION pdrvr_ext, +ULONG key +) +{ + PGENDRV_EXT_DRVR_ENTRY pentry; + PLIST_ENTRY pthis, pnext; + if( pdrvr_ext == NULL || key == ( ULONG )-1 ) + return NULL; + + ExAcquireFastMutex( &pdrvr_ext->drvr_ext_mutex ); + ListFirst( &pdrvr_ext->ext_drvr_list, pthis ); + while( pthis ) + { + pentry = ( PGENDRV_EXT_DRVR_ENTRY )pthis; + if( pentry->drvr_key == key ) + { + ExReleaseFastMutex( &pdrvr_ext->drvr_ext_mutex ); + return pentry->pext_drvr; + } + ListNext( &pdrvr_ext->ext_drvr_list, pthis, pnext ); + pthis = pnext; + } + ExReleaseFastMutex( &pdrvr_ext->drvr_ext_mutex ); + + return NULL; +} + +PDRIVER_OBJECT +gendrv_load_ext_drvr( +PGENDRV_DRVR_EXTENSION pdrvr_ext, +PUSB_DESC_HEADER pdesc +) +{ + PDRIVER_OBJECT pdrvr_obj; + PGENDRV_EXT_DRVR_ENTRY pentry; + UNICODE_STRING usz, svc_name, svc_key, utemp; + PKEY_VALUE_FULL_INFORMATION val_info; + PWCHAR val_buf; + HANDLE handle; + NTSTATUS status; + + if( pdrvr_ext == NULL || pdesc == NULL ) + return NULL; + + // try to search and load driver from outside + handle = NULL; + RtlZeroMemory( &svc_key, sizeof( svc_key ) ); + val_info = NULL; + RtlInitUnicodeString( &usz, L"" ); + gendrv_build_reg_string( pdesc, &usz ); + if( gendrv_open_reg_key( &handle, NULL, &usz, KEY_READ, FALSE ) != STATUS_SUCCESS ) + { + goto ERROR_OUT; + } + if( gendrv_get_key_value( handle, L"service", &val_info ) != STATUS_SUCCESS ) + { + goto ERROR_OUT; + } + + if( val_info->DataLength > 32 ) + goto ERROR_OUT; + + val_buf = ( PWCHAR )( ( ( PBYTE )val_info ) + val_info->DataOffset ); + svc_key.Length = 0, svc_key.MaximumLength = 255; + svc_key.Buffer = usb_alloc_mem( NonPagedPool, 256 ); + + RtlInitUnicodeString( &utemp, L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\" ); + RtlAppendUnicodeStringToString( &svc_key, &utemp); + RtlInitUnicodeString( &svc_name, val_buf ); + RtlAppendUnicodeStringToString( &svc_key, &svc_name ); + + status = ZwLoadDriver( &svc_key ); + if( status != STATUS_SUCCESS ) + goto ERROR_OUT; + + svc_key.Length = 0; + RtlZeroMemory( svc_key.Buffer, 128 ); + RtlInitUnicodeString( &svc_key, L"\\Driver\\" ); + RtlAppendUnicodeStringToString( &svc_key, &svc_name ); + pdrvr_obj = gendrv_open_ext_driver( &svc_key ); + if( pdrvr_obj == NULL ) + goto ERROR_OUT; + + ExAcquireFastMutex( &pdrvr_ext->drvr_ext_mutex ); + + // insert the driver to the drvr list + pentry = gendrv_alloc_ext_drvr_entry( pdrvr_ext ); + if( pentry == NULL ) + { + ExReleaseFastMutex( &pdrvr_ext->drvr_ext_mutex ); + ObDereferenceObject( pdrvr_obj ); + goto ERROR_OUT; + } + pentry->pext_drvr = pdrvr_obj; + InsertTailList( &pdrvr_ext->ext_drvr_list, &pentry->drvr_link ); + pdrvr_ext->ext_drvr_count++; + ExReleaseFastMutex( &pdrvr_ext->drvr_ext_mutex ); + ZwClose( handle ); + return pdrvr_obj; + +ERROR_OUT: + RtlFreeUnicodeString( &usz ); + if( val_info != NULL ) + { + usb_free_mem( val_info ); + val_info = NULL; + } + if( svc_key.Buffer ) + usb_free_mem( svc_key.Buffer ); + + if( handle ) + ZwClose( handle ); + + return NULL; +} + +VOID +gendrv_release_drvr( +PGENDRV_DRVR_EXTENSION pdrvr_ext, +PDRIVER_OBJECT pdrvr_obj +) +{ + PLIST_ENTRY pthis, pnext; + PGENDRV_EXT_DRVR_ENTRY pentry; + + if( pdrvr_ext == NULL || pdrvr_obj == NULL ) + return; + ExAcquireFastMutex( &pdrvr_ext->drvr_ext_mutex ); + ListFirst( &pdrvr_ext->ext_drvr_list, pthis ); + while( pthis ) + { + pentry = ( PGENDRV_EXT_DRVR_ENTRY )pthis; + if( pentry->pext_drvr == pdrvr_obj ) + { + ASSERT( pentry->ref_count ); + ExReleaseFastMutex( &pdrvr_ext->drvr_ext_mutex ); + return; + } + ListNext( &pdrvr_ext->ext_drvr_list, pthis, pnext ); + pthis = pnext; + } + ExReleaseFastMutex( &pdrvr_ext->drvr_ext_mutex ); +} + +NTSTATUS +gendrv_send_pnp_msg( +ULONG msg, +PDEVICE_OBJECT pdev_obj, +PVOID pctx +) +{ + PDRIVER_EXTENSION pwin_drvr_ext; + if( pdev_obj == NULL ) + return STATUS_INVALID_PARAMETER; + + switch( msg ) + { + case GENDRV_MSG_ADDDEVICE: + { + PDRIVER_OBJECT pdrvr_obj; + if( pctx == NULL ) + return STATUS_INVALID_PARAMETER; + pdrvr_obj = (PDRIVER_OBJECT )pctx; + if( pdrvr_obj->DriverExtension ) + { + return pdrvr_obj->DriverExtension->AddDevice( pdrvr_obj, &pdev_obj ); + } + return STATUS_IO_DEVICE_ERROR; + } + case GENDRV_MSG_STOPDEVICE: + case GENDRV_MSG_DISCDEVICE: + { + NTSTATUS status; + IO_STACK_LOCATION *irpstack; + IRP *irp; + // IRP_MJ_PNP_POWER + irp = IoAllocateIrp( 2, FALSE ); + if( irp == NULL ) + return STATUS_NO_MEMORY; + + irpstack = IoGetNextIrpStackLocation(irp); + irpstack->MajorFunction = IRP_MJ_PNP_POWER; + irpstack->MinorFunction = + ( msg == GENDRV_MSG_STOPDEVICE ) ? IRP_MN_STOP_DEVICE : IRP_MN_REMOVE_DEVICE; + status = IoCallDriver( pdev_obj, irp ); + ASSERT( status != STATUS_PENDING ); + status = irp->IoStatus.Status; + IoFreeIrp( irp ); + return STATUS_MORE_PROCESSING_REQUIRED; + } + } + return STATUS_INVALID_PARAMETER; +} + + +BOOL +gendrv_if_connect( +PCONNECT_DATA params, +DEV_HANDLE if_handle +) +{ + // + // try to search the registry to find one driver. + // if found, create the PDO, load the driver. + // and call its AddDevice. + // + LONG if_idx, i; + NTSTATUS status; + PUSB_DEV pdev; + PUSB_DRIVER pdrvr; + PUSB_INTERFACE_DESC pif_desc; + PGENDRV_DEVICE_EXTENSION pdev_ext; + PUSB_CONFIGURATION_DESC pconfig_desc; + PUSB_DEV_MANAGER dev_mgr; + PGENDRV_DRVR_EXTENSION pdrvr_ext; + PGENDRV_EXT_DRVR_ENTRY pentry; + + PDEVICE_OBJECT pdev_obj; + PDRIVER_OBJECT pdrvr_obj; + HANDLE handle; + PLIST_ENTRY pthis, pnext; + USE_IRQL; + + pdev = NULL; + usb_dbg_print( DBGLVL_MAXIMUM, ( "gendrv_if_connect(): entering...\n" ) ); + + if( params == NULL ) + return FALSE; + + dev_mgr = params->dev_mgr; + pdrvr = params->pdriver; + pdrvr_ext = ( PGENDRV_DRVR_EXTENSION )pdrvr->driver_ext; + + status = usb_query_and_lock_dev( dev_mgr, if_handle, &pdev ); + if( status != STATUS_SUCCESS ) + { + goto ERROR_OUT; + } + // obtain the pointer to the config desc, the dev won't go away in this routine + pconfig_desc = pdev->usb_config->pusb_config_desc; // + usb_unlock_dev( pdev ); + pdev = NULL; + + if_idx = if_idx_from_handle( if_handle ); + pif_desc = ( PUSB_INTERFACE_DESC )( &pconfig_desc[ 1 ] ); + + for( i = 0; i < if_idx; i++ ) + { + //search for our if + if( usb_skip_if_and_altif( ( PUCHAR* )&pif_desc ) == FALSE ) + break; + } + if( pif_desc == NULL ) + return FALSE; + + // + // well, let's do the hard work to see if there is a class driver + // for this device. + // + i = gendrv_make_key( ( PUSB_DESC_HEADER )pif_desc ); + if( i == -1 ) + { + return FALSE; + } + + pdrvr_obj = gendrv_find_drvr_by_key( pdrvr_ext, ( ULONG )i ); + if( !pdrvr_obj ) + { + if( ( pdrvr_obj = gendrv_load_ext_drvr( pdrvr_ext, ( PUSB_DESC_HEADER )pif_desc ) ) == NULL ) + return FALSE; + } + + + pdev_obj = gendrv_create_device( dev_mgr, pdrvr, if_handle ); + if( pdev_obj == NULL ) + { + goto ERROR_OUT; + } + + lock_dev( pdev, FALSE ); + if( dev_state( pdev ) == USB_DEV_STATE_ZOMB || + dev_mgr_set_if_driver( dev_mgr, if_handle, pdrvr, pdev ) == FALSE ) + { + unlock_dev( pdev, FALSE ); + gendrv_delete_device( dev_mgr, pdev_obj ); + goto ERROR_OUT; + } + + if( pdev->usb_config ) + { + pdev->usb_config->interf[ if_idx ].if_ext = pdev_obj; + pdev->usb_config->interf[ if_idx ].if_ext_size = 0; + } + + unlock_dev( pdev, FALSE ); + + pdev_ext = ( PGENDRV_DEVICE_EXTENSION )pdev_obj->DeviceExtension; + pdev_ext->desc_buf = usb_alloc_mem( NonPagedPool, 512 ); + RtlCopyMemory( pdev_ext->desc_buf, pconfig_desc, 512 ); + pdev_ext->if_ctx.pif_desc = ( PUSB_INTERFACE_DESC )&pdev_ext->desc_buf[ ( PBYTE )pif_desc - ( PBYTE )pconfig_desc ]; + + // insert the device to the dev_list + ExAcquireFastMutex( &pdrvr_ext->drvr_ext_mutex ); + ListFirst( &pdrvr_ext->ext_drvr_list, pthis ); + pentry = NULL; + while( pthis ) + { + pentry = ( PGENDRV_EXT_DRVR_ENTRY )pthis; + if( pentry->pext_drvr == pdrvr_obj ) + break; + ListNext( &pdrvr_ext->ext_drvr_list, pthis, pnext ); + pthis = pnext; + pentry = NULL; + } + ASSERT( pentry ); + InsertTailList( &pentry->dev_list, &pdev_ext->dev_obj_link ); + pdev_ext->ext_drvr_entry = pentry; + pentry->ref_count++; + ExReleaseFastMutex( &pdrvr_ext->drvr_ext_mutex ); + + // notify the class driver, some device comes + gendrv_send_pnp_msg( GENDRV_MSG_ADDDEVICE, pdev_obj, pdrvr_obj ); + usb_unlock_dev( pdev ); + return TRUE; + +ERROR_OUT: + + usb_unlock_dev( pdev ); + return FALSE; +} + +BOOL +gendrv_do_stop( +PUSB_DEV_MANAGER dev_mgr, +DEV_HANDLE dev_handle, +BOOL is_if +) +{ + PUSB_DEV pdev; + PDEVICE_OBJECT pdev_obj; + ULONG if_idx; + + if( dev_mgr == NULL ) + return FALSE; + + // clean up the irps + if_idx = if_idx_from_handle( dev_handle ); + if( usb_query_and_lock_dev( dev_mgr, dev_handle, &pdev ) != STATUS_SUCCESS ) + { + return FALSE; + } + if( is_if && pdev->usb_config ) + pdev_obj = ( PDEVICE_OBJECT )pdev->usb_config->interf[ if_idx ].if_ext; + else + pdev_obj = pdev->dev_obj; + + gendrv_clean_up_queued_irps( pdev_obj ); + usb_unlock_dev( pdev ); + + // send message to class drivers. + gendrv_send_pnp_msg( GENDRV_MSG_STOPDEVICE, pdev_obj, NULL ); + + return TRUE; +} + +BOOL +gendrv_if_stop( +PUSB_DEV_MANAGER dev_mgr, +DEV_HANDLE dev_handle +) +{ + if( dev_mgr == NULL ) + return FALSE; + + return gendrv_do_stop( dev_mgr, dev_handle, TRUE ); +} + +BOOL +gendrv_do_disconnect( +PUSB_DEV_MANAGER dev_mgr, +DEV_HANDLE if_handle, +BOOL is_if +) +{ + PUSB_DEV pdev; + PDEVICE_OBJECT dev_obj; + NTSTATUS status; + PUSB_DRIVER pdrvr; + PGENDRV_DRVR_EXTENSION pdrvr_ext; + PGENDRV_EXT_DRVR_ENTRY pentry; + PLIST_ENTRY pthis; + PGENDRV_DEVICE_EXTENSION pdev_ext; + ULONG if_idx; + + status = usb_query_and_lock_dev( dev_mgr, if_handle, &pdev ); + if( pdev == NULL ) + { + return FALSE; + } + if( status == STATUS_SUCCESS ) + { + // must be a bug + TRAP(); + } + if_idx = if_idx_from_handle( if_handle ); + if( pdev->usb_config ) + { + if( is_if ) + { + pdrvr = pdev->usb_config->interf[ if_idx ].pif_drv; + dev_obj = ( PDEVICE_OBJECT )pdev->usb_config->interf[ if_idx ].if_ext; + } + else + { + pdrvr = pdev->dev_driver; + dev_obj = pdev->dev_obj; + } + pdrvr_ext = ( PGENDRV_DRVR_EXTENSION )pdrvr->driver_ext; + pdev_ext = ( PGENDRV_DEVICE_EXTENSION ) dev_obj->DeviceExtension; + } + else + TRAP(); + pdev = NULL; + + // remove the device from the list + ExAcquireFastMutex( &pdrvr_ext->drvr_ext_mutex ); + RemoveEntryList( &pdev_ext->dev_obj_link ); + pdev_ext->ext_drvr_entry->ref_count--; + ExReleaseFastMutex( &pdrvr_ext->drvr_ext_mutex ); + + // send message to class driver + gendrv_send_pnp_msg( GENDRV_MSG_DISCDEVICE, dev_obj, NULL ); + // delete the device object + gendrv_delete_device( dev_mgr, dev_obj ); + return TRUE; +} + +BOOL +gendrv_if_disconnect( +PUSB_DEV_MANAGER dev_mgr, +DEV_HANDLE if_handle +) +{ + return gendrv_do_disconnect( dev_mgr, if_handle, TRUE ); +} + +BOOL +gendrv_if_driver_init( +PUSB_DEV_MANAGER dev_mgr, +PUSB_DRIVER pdriver +) +{ + PGENDRV_DRVR_EXTENSION pdrvr_ext; + if( dev_mgr == NULL || pdriver == NULL ) + return FALSE; + + pdriver->driver_desc.flags = USB_DRIVER_FLAG_IF_CAPABLE; + pdriver->driver_desc.vendor_id = 0x0000; // USB Vendor ID + pdriver->driver_desc.product_id = 0x0000; // USB Product ID. + pdriver->driver_desc.release_num = 0x100; // Release Number of Device + + pdriver->driver_desc.config_val = 0; // Configuration Value + pdriver->driver_desc.if_num = 0; // Interface Number + pdriver->driver_desc.if_class = 0x0; // Interface Class + pdriver->driver_desc.if_sub_class = 0x0; // Interface SubClass + pdriver->driver_desc.if_protocol = 0x0; // Interface Protocol + + pdriver->driver_desc.driver_name = "USB generic interface driver"; // Driver name for Name Registry + pdriver->driver_desc.dev_class = 0; + pdriver->driver_desc.dev_sub_class = 0; // Device Subclass + pdriver->driver_desc.dev_protocol = 0; // Protocol Info. + + //we have no extra data sturcture currently + + pdriver->disp_tbl.version = 1; + pdriver->disp_tbl.dev_connect = gendrv_if_connect; + pdriver->disp_tbl.dev_disconnect = gendrv_if_disconnect; + pdriver->disp_tbl.dev_stop = gendrv_if_stop; + pdriver->disp_tbl.dev_reserved = NULL; + + pdriver->driver_ext = usb_alloc_mem( NonPagedPool, sizeof( GENDRV_DRVR_EXTENSION ) ); + pdriver->driver_ext_size = sizeof( GENDRV_DRVR_EXTENSION ); + + RtlZeroMemory( pdriver->driver_ext, pdriver->driver_ext_size ); + pdrvr_ext = ( PGENDRV_DRVR_EXTENSION )pdriver->driver_ext; + + // InitializeListHead( &pdrvr_ext->dev_list ); + InitializeListHead( &pdrvr_ext->ext_drvr_list ); + pdrvr_ext->ext_drvr_count = 0; + ExInitializeFastMutex( &pdrvr_ext->drvr_ext_mutex ); + + return TRUE; +} + +BOOL +gendrv_if_driver_destroy( +PUSB_DEV_MANAGER dev_mgr, +PUSB_DRIVER pdriver +) +{ + PGENDRV_DRVR_EXTENSION pdrvr_ext; + LONG i; + PLIST_ENTRY pthis, pnext; + PGENDRV_EXT_DRVR_ENTRY pentry; + if( dev_mgr == NULL || pdriver == NULL ) + return FALSE; + + if( pdriver->driver_ext ) + { + // should we lock it? + // ExAcquireFastMutex( &pdrvr_ext->drvr_ext_mutex ); + pdrvr_ext = ( PGENDRV_DRVR_EXTENSION )pdriver->driver_ext; + if( pdrvr_ext->ext_drvr_count ) + { + while( IsListEmpty( &pdrvr_ext->ext_drvr_list ) ) + { + pthis = RemoveHeadList( &pdrvr_ext->ext_drvr_list ); + pentry = ( PGENDRV_EXT_DRVR_ENTRY )pthis; + if( pentry->pext_drvr ) + { + if( pentry->ref_count ) + { + // FIXME: really fail? + continue; + } + ObDereferenceObject( pentry->pext_drvr ); + gendrv_release_ext_drvr_entry( pdrvr_ext, pentry ); + } + } + pdrvr_ext->ext_drvr_count = 0; + } + + usb_free_mem( pdriver->driver_ext ); + pdriver->driver_ext = NULL; + pdriver->driver_ext_size = 0; + } + return TRUE; +} + +PDRIVER_OBJECT +gendrv_open_ext_driver( +PUNICODE_STRING unicode_string +) +{ + NTSTATUS status; + OBJECT_ATTRIBUTES oa; + HANDLE drvr_handle; + UNICODE_STRING oname; + PDRIVER_OBJECT pdrvr; + + RtlZeroMemory( &oa, sizeof( oa ) ); + oa.Length = sizeof( oa ); + oa.ObjectName = &oname; + oa.Attributes = OBJ_CASE_INSENSITIVE; + RtlInitUnicodeString( &oname, L"" ); + RtlAppendUnicodeStringToString( &oname, unicode_string ); + + status = ObOpenObjectByName( &oa, + *IoDriverObjectType, // object type + KernelMode, // access mode + NULL, // access state + FILE_READ_DATA, // STANDARD_RIGHTS_READ, access right + NULL, + &drvr_handle ); + + if( status != STATUS_SUCCESS ) + { + return NULL; + } + ObReferenceObjectByHandle( drvr_handle, + FILE_READ_DATA, + *IoDriverObjectType, + KernelMode, + &pdrvr, + NULL // OUT POBJECT_HANDLE_INFORMATION HandleInformation OPTIONAL + ); + ZwClose( drvr_handle ); + return pdrvr; +} + +BOOL +gendrv_close_ext_driver( PDRIVER_OBJECT pdrvr ) +{ + if( pdrvr == NULL ) + return FALSE; + ObDereferenceObject( pdrvr ); + return TRUE; +} + +NTSTATUS +gendrv_dispatch( +PDEVICE_OBJECT dev_obj, +PIRP irp +) +{ + IO_STACK_LOCATION *irpstack; + PUSB_DEV_MANAGER dev_mgr; + PDEVEXT_HEADER ext_hdr; + NTSTATUS status; + + if( dev_obj == NULL || irp == NULL ) + return STATUS_INVALID_PARAMETER; + + ext_hdr = dev_obj->DeviceExtension; + dev_mgr = ext_hdr->dev_mgr; + + irpstack = IoGetNextIrpStackLocation(irp); + switch( irpstack->MajorFunction ) + { + case IRP_MJ_PNP_POWER: + { + irp->IoStatus.Information = 0; + GENDRV_EXIT_DISPATCH( dev_obj, STATUS_SUCCESS, irp ); + } + case IRP_MJ_INTERNAL_DEVICE_CONTROL: + { + status = STATUS_NOT_SUPPORTED; + if( irpstack->MinorFunction == IOCTL_SUBMIT_URB_RD || + irpstack->MinorFunction == IOCTL_SUBMIT_URB_WR || + irpstack->MinorFunction == IOCTL_SUBMIT_URB_NOIO ) + { + PURB purb; + DEV_HANDLE endp_handle; + PGENDRV_DEVICE_EXTENSION pdev_ext; + + pdev_ext = dev_obj->DeviceExtension; + if( irpstack->Parameters.DeviceIoControl.InputBufferLength < sizeof( URB ) ) + { + GENDRV_EXIT_DISPATCH( dev_obj, STATUS_INVALID_PARAMETER, irp ); + } + + purb = ( PURB )irp->AssociatedIrp.SystemBuffer; + endp_handle = purb->endp_handle; + if( purb->data_buffer == NULL || purb->data_length == 0 ) + { + if( irpstack->MinorFunction != IOCTL_SUBMIT_URB_NOIO ) + { + GENDRV_EXIT_DISPATCH( dev_obj, STATUS_INVALID_PARAMETER, irp ); + } + } + if( !default_endp_handle( endp_handle ) ) + { + //no permit to other interface if interface dev + if( if_dev( dev_obj ) && + if_idx_from_handle( endp_handle ) != pdev_ext->if_ctx.if_idx ) + GENDRV_EXIT_DISPATCH( dev_obj, STATUS_INVALID_PARAMETER, irp ); + } + + GENDRV_EXIT_DISPATCH( dev_obj, STATUS_PENDING, irp ); + } + else if( irpstack->MinorFunction == IOCTL_GET_DEV_DESC ) + { + // this is a synchronous call, route to dev_mgr_dispatch + return dev_mgr_dispatch( dev_mgr, irp ); + } + else if( irpstack->MinorFunction == IOCTL_GET_DEV_HANDLE ) + { + PUCHAR user_buffer; + ULONG user_buffer_length; + PGENDRV_DEVICE_EXTENSION pdev_ext; + pdev_ext = dev_obj->DeviceExtension; + if( irpstack->Parameters.DeviceIoControl.OutputBufferLength < sizeof( LONG ) ) + GENDRV_EXIT_DISPATCH( dev_obj, STATUS_INVALID_PARAMETER, irp ); + + *( ( PLONG )irp->AssociatedIrp.SystemBuffer ) = pdev_ext->dev_handle; + irp->IoStatus.Information = sizeof( LONG ); + GENDRV_EXIT_DISPATCH( dev_obj, STATUS_SUCCESS, irp ); + } + GENDRV_EXIT_DISPATCH( dev_obj, STATUS_NOT_SUPPORTED, irp ); + } + case IRP_MJ_DEVICE_CONTROL: + { + status = STATUS_NOT_SUPPORTED; + if( irpstack->MinorFunction == IOCTL_SUBMIT_URB_RD || + irpstack->MinorFunction == IOCTL_SUBMIT_URB_WR || + irpstack->MinorFunction == IOCTL_SUBMIT_URB_NOIO ) + { + PURB purb; + DEV_HANDLE endp_handle; + PGENDRV_DEVICE_EXTENSION pdev_ext; + + pdev_ext = dev_obj->DeviceExtension; + if( irpstack->Parameters.DeviceIoControl.InputBufferLength < sizeof( URB ) ) + { + GENDRV_EXIT_DISPATCH( dev_obj, STATUS_INVALID_PARAMETER, irp ); + } + + purb = ( PURB )irp->AssociatedIrp.SystemBuffer; + endp_handle = purb->endp_handle; + if( !default_endp_handle( endp_handle ) ) + { + //no permit to other interface if interface dev + if( if_dev( dev_obj ) && + if_idx_from_handle( endp_handle ) != pdev_ext->if_ctx.if_idx ) + GENDRV_EXIT_DISPATCH( dev_obj, STATUS_INVALID_PARAMETER, irp ); + } + + GENDRV_EXIT_DISPATCH( dev_obj, STATUS_PENDING, irp ); + } + else if( irpstack->MinorFunction == IOCTL_GET_DEV_DESC ) + { + // this is a synchronous call, route to dev_mgr_dispatch + return dev_mgr_dispatch( dev_mgr, irp ); + } + else if( irpstack->MinorFunction == IOCTL_GET_DEV_HANDLE ) + { + PUCHAR user_buffer; + ULONG user_buffer_length; + PGENDRV_DEVICE_EXTENSION pdev_ext; + pdev_ext = dev_obj->DeviceExtension; + if( irpstack->Parameters.DeviceIoControl.OutputBufferLength < sizeof( LONG ) ) + GENDRV_EXIT_DISPATCH( dev_obj, STATUS_INVALID_PARAMETER, irp ); + + *( ( PLONG )irp->AssociatedIrp.SystemBuffer ) = pdev_ext->dev_handle; + irp->IoStatus.Information = sizeof( LONG ); + GENDRV_EXIT_DISPATCH( dev_obj, STATUS_SUCCESS, irp ); + } + GENDRV_EXIT_DISPATCH( dev_obj, STATUS_NOT_SUPPORTED, irp ); + } + } + irp->IoStatus.Status = STATUS_NOT_SUPPORTED; + irp->IoStatus.Information = 0; + IoCompleteRequest( irp, IO_NO_INCREMENT ); + + return STATUS_NOT_SUPPORTED; +} +BOOL +gendrv_init_dev_ext_hdr( +PDEVICE_OBJECT dev_obj, +PUSB_DEV_MANAGER dev_mgr +) +{ + PDEVEXT_HEADER dev_hdr; + if( dev_obj == NULL || dev_mgr == NULL ) + return FALSE; + + dev_hdr = ( PDEVEXT_HEADER )dev_obj->DeviceExtension; + dev_hdr->type = NTDEV_TYPE_CLIENT_DEV; + dev_hdr->dispatch = gendrv_dispatch; + dev_hdr->start_io = gendrv_startio; + return TRUE; +} + +PDEVICE_OBJECT +gendrv_create_device( +PUSB_DEV_MANAGER dev_mgr, +PUSB_DRIVER gen_drvr, +DEV_HANDLE dev_handle +) +{ + BOOL is_if; + PDEVICE_OBJECT pdev; + PGENDRV_DEVICE_EXTENSION pdev_ext; + ULONG dev_id; + PGENDRV_DRVR_EXTENSION pdrvr_ext; + CHAR dev_name[ 64 ]; + STRING string; + UNICODE_STRING name_string, symb_link; + NTSTATUS status; + + if( dev_mgr == NULL || gen_drvr == NULL || dev_handle == 0 ) + return NULL; + + is_if = ( gen_drvr->driver_desc.flags & USB_DRIVER_FLAG_IF_CAPABLE ) ? 1 : 0; + usb_dbg_print( DBGLVL_MAXIMUM, ( "gendrv_create_device(): entering...\n" ) ); + pdrvr_ext = ( PGENDRV_DRVR_EXTENSION )gen_drvr->driver_ext; + dev_id = ( UCHAR )dev_id_from_handle( dev_handle ); + + if( is_if == FALSE ) + sprintf( dev_name, "\\Device\\gendev_%d", ( int )dev_id ); + else + sprintf( dev_name, "\\Device\\genifdev_%d", ( int )dev_id ); + + RtlInitString( &string, dev_name ); + RtlAnsiStringToUnicodeString( &name_string, &string, TRUE ); + pdev = NULL; + + status = IoCreateDevice( + dev_mgr->usb_driver_obj, + sizeof( GENDRV_DEVICE_EXTENSION ), + &name_string, + FILE_USB_DEV_TYPE, + 0, + TRUE, + &pdev); + + if( status == STATUS_SUCCESS ) + { + // + // We do direct io + // + pdev->Flags |= DO_DIRECT_IO; + + pdev->Flags &= ~DO_DEVICE_INITIALIZING; + pdev->StackSize = 2; //one for fdo, one for file device obj + + pdev_ext = ( PGENDRV_DEVICE_EXTENSION )pdev->DeviceExtension; + + //may be accessed by other thread + + gendrv_init_dev_ext_hdr( pdev, dev_mgr ); + + pdev_ext->dev_id = ( UCHAR )dev_id; + pdev_ext->pdo = pdev; + pdev_ext->dev_handle = dev_handle; + pdev_ext->dev_mgr = dev_mgr; + pdev_ext->pdriver = gen_drvr; + + if( is_if == FALSE ) + sprintf( dev_name, "\\DosDevices\\gendev%d", ( int )dev_id ); + else + sprintf( dev_name, "\\DosDevices\\genifdev%d", ( int )dev_id ); + + RtlInitString( &string, dev_name ); + RtlAnsiStringToUnicodeString( &symb_link, &string, TRUE ); + IoCreateSymbolicLink( &symb_link, &name_string ); + RtlFreeUnicodeString( &symb_link ); + KeInitializeEvent( &pdev_ext->sync_event, SynchronizationEvent, FALSE ); + KeInitializeSpinLock( &pdev_ext->dev_lock ); + + } + RtlFreeUnicodeString( &name_string ); + return pdev; +} + + + +VOID +gendrv_deferred_delete_device( +PVOID context +) +{ + PDEVICE_OBJECT dev_obj; + PGENDRV_DEVICE_EXTENSION pdev_ext; + PGENDRV_DRVR_EXTENSION pdrvr_ext; + LARGE_INTEGER interval; + + if( context == NULL ) + return; + + dev_obj = ( PDEVICE_OBJECT )context; + pdev_ext = dev_obj->DeviceExtension; + pdrvr_ext = ( PGENDRV_DRVR_EXTENSION )pdev_ext->pdriver->driver_ext; + + interval.QuadPart = -20000; //two ms + + for( ; ; ) + { + if( dev_obj->ReferenceCount ) + KeDelayExecutionThread( KernelMode, TRUE, &interval ); + else + { + KeDelayExecutionThread( KernelMode, TRUE, &interval ); + if( dev_obj->ReferenceCount == 0 ) + break; + } + } + usb_dbg_print( DBGLVL_MAXIMUM, ( "gendrv_deferred_delete_device(): delete device, 0x%x\n", dev_obj ) ); + + ExAcquireFastMutex( &pdrvr_ext->drvr_ext_mutex ); + RemoveEntryList( &pdev_ext->dev_obj_link ); + pdev_ext->ext_drvr_entry->ref_count--; + ExReleaseFastMutex( &pdrvr_ext->drvr_ext_mutex ); + + IoDeleteDevice( dev_obj ); + return; +} + +BOOL +gendrv_delete_device( +PUSB_DEV_MANAGER dev_mgr, +PDEVICE_OBJECT dev_obj +) +{ + BOOL is_if; + PUSB_DRIVER pdrvr; + PGENDRV_DEVICE_EXTENSION pdev_ext; + UCHAR dev_name[ 64 ]; + STRING string; + UNICODE_STRING symb_link; + NTSTATUS status; + PGENDRV_DRVR_EXTENSION pdrvr_ext; + + if( dev_mgr == NULL || dev_obj == 0 ) + return FALSE; + + pdev_ext = ( PGENDRV_DEVICE_EXTENSION )dev_obj->DeviceExtension; + pdrvr = pdev_ext->pdriver; + pdrvr_ext = ( PGENDRV_DRVR_EXTENSION )pdrvr->driver_ext; + is_if = if_dev( dev_obj ); + if( is_if == FALSE ) + sprintf( dev_name, "\\DosDevices\\gendev%d", ( int )pdev_ext->dev_id ); + else + sprintf( dev_name, "\\DosDevices\\genifdev%d", ( int )pdev_ext->dev_id ); + + RtlInitString( &string, dev_name ); + RtlAnsiStringToUnicodeString( &symb_link, &string, TRUE ); + IoDeleteSymbolicLink( &symb_link ); + RtlFreeUnicodeString( &symb_link ); + + if( pdev_ext->desc_buf ) + { + usb_dbg_print( DBGLVL_MAXIMUM, ( "gendrv_delete_device(): delete desc_buf\n" ) ); + usb_free_mem( pdev_ext->desc_buf ); + pdev_ext->desc_buf = NULL; + + } + + if( dev_obj->ReferenceCount == 0 ) + { + ExAcquireFastMutex( &pdrvr_ext->drvr_ext_mutex ); + RemoveEntryList( &pdev_ext->dev_obj_link ); + pdev_ext->ext_drvr_entry->ref_count--; //the ext_drvr_entry is actually in pdrvr_ext, so lock it. + ExReleaseFastMutex( &pdrvr_ext->drvr_ext_mutex ); + + IoDeleteDevice( dev_obj ); + return TRUE; + } + + // borrow from umss's work routine + return umss_schedule_workitem( dev_obj, gendrv_deferred_delete_device, NULL, 0 ); +} + +// must have the drvr_ext_mutex acquired. +PGENDRV_EXT_DRVR_ENTRY +gendrv_alloc_ext_drvr_entry( +PGENDRV_DRVR_EXTENSION pdrvr_ext +) +{ + LONG i; + if( pdrvr_ext == NULL ) + return NULL; + if( pdrvr_ext->ext_drvr_count == GENDRV_MAX_EXT_DRVR ) + return NULL; + for( i = 0; i < GENDRV_MAX_EXT_DRVR; i++ ) + { + if( pdrvr_ext->ext_drvr_array[ i ].drvr_link.Flink == NULL && + pdrvr_ext->ext_drvr_array[ i ].drvr_link.Blink == NULL ) + { + return &pdrvr_ext->ext_drvr_array[ i ]; + } + } + return NULL; +} + +// must have the drvr_ext_mutex acquired. +VOID +gendrv_release_ext_drvr_entry( +PGENDRV_DRVR_EXTENSION pdrvr_ext, +PGENDRV_EXT_DRVR_ENTRY pentry +) +{ + if( pdrvr_ext == NULL || pentry == NULL ) + return; + RtlZeroMemory( pentry, sizeof( GENDRV_EXT_DRVR_ENTRY ) ); + InitializeListHead( &pentry->dev_list ); + return; +} + +NTSTATUS +gendrv_open_reg_key( +OUT PHANDLE handle, +IN HANDLE base_handle OPTIONAL, +IN PUNICODE_STRING keyname, +IN ACCESS_MASK desired_access, +IN BOOLEAN create +) + +/*++ + +Routine Description: + + Opens or creates a VOLATILE registry key using the name passed in based + at the BaseHandle node. + +Arguments: + + Handle - Pointer to the handle which will contain the registry key that + was opened. + + BaseHandle - Handle to the base path from which the key must be opened. + + KeyName - Name of the Key that must be opened/created. + + DesiredAccess - Specifies the desired access that the caller needs to + the key. + + Create - Determines if the key is to be created if it does not exist. + +Return Value: + + The function value is the final status of the operation. + +--*/ + +{ + OBJECT_ATTRIBUTES object_attr; + ULONG disposition; + + // + // Initialize the object for the key. + // + + InitializeObjectAttributes( &object_attr, + keyname, + OBJ_CASE_INSENSITIVE, + base_handle, + (PSECURITY_DESCRIPTOR) NULL ); + + // + // Create the key or open it, as appropriate based on the caller's + // wishes. + // + + if (create) + { + return ZwCreateKey( handle, + desired_access, + &object_attr, + 0, + (PUNICODE_STRING) NULL, + REG_OPTION_VOLATILE, + &disposition ); + } + else + { + return ZwOpenKey( handle, + desired_access, + &object_attr ); + } + return STATUS_INVALID_PARAMETER; +} + +NTSTATUS +gendrv_get_key_value( +IN HANDLE KeyHandle, +IN PWSTR ValueName, +OUT PKEY_VALUE_FULL_INFORMATION *Information +) + +/*++ + +Routine Description: + + This routine is invoked to retrieve the data for a registry key's value. + This is done by querying the value of the key with a zero-length buffer + to determine the size of the value, and then allocating a buffer and + actually querying the value into the buffer. + + It is the responsibility of the caller to free the buffer. + +Arguments: + + KeyHandle - Supplies the key handle whose value is to be queried + + ValueName - Supplies the null-terminated Unicode name of the value. + + Information - Returns a pointer to the allocated data buffer. + +Return Value: + + The function value is the final status of the query operation. + +--*/ + +{ + UNICODE_STRING unicodeString; + NTSTATUS status; + PKEY_VALUE_FULL_INFORMATION infoBuffer; + ULONG keyValueLength; + + PAGED_CODE(); + + RtlInitUnicodeString( &unicodeString, ValueName ); + + // + // Figure out how big the data value is so that a buffer of the + // appropriate size can be allocated. + // + + status = ZwQueryValueKey( KeyHandle, + &unicodeString, + KeyValueFullInformation, + (PVOID) NULL, + 0, + &keyValueLength ); + if (status != STATUS_BUFFER_OVERFLOW && + status != STATUS_BUFFER_TOO_SMALL) { + return status; + } + + // + // Allocate a buffer large enough to contain the entire key data value. + // + + infoBuffer = usb_alloc_mem( NonPagedPool, keyValueLength ); + if (!infoBuffer) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // Query the data for the key value. + // + + status = ZwQueryValueKey( KeyHandle, + &unicodeString, + KeyValueFullInformation, + infoBuffer, + keyValueLength, + &keyValueLength ); + if (!NT_SUCCESS( status )) { + usb_free_mem( infoBuffer ); + return status; + } + + // + // Everything worked, so simply return the address of the allocated + // buffer to the caller, who is now responsible for freeing it. + // + + *Information = infoBuffer; + return STATUS_SUCCESS; +} + +VOID +gendrv_startio( +IN PDEVICE_OBJECT dev_obj, +IN PIRP irp +) +{ + PIO_STACK_LOCATION irp_stack; + ULONG ctrl_code; + PUSB_DEV_MANAGER dev_mgr; + USE_IRQL; + + if( dev_obj == NULL || irp == NULL ) + return; + + // standard process from walter oney + IoAcquireCancelSpinLock(&old_irql); + if (irp != dev_obj->CurrentIrp || irp->Cancel) + { + // already move on to other irp + IoReleaseCancelSpinLock( old_irql ); + return; + } + else + { + IoSetCancelRoutine( irp, NULL ); + } + IoReleaseCancelSpinLock( old_irql ); + + irp->IoStatus.Information = 0; + + irp_stack = IoGetCurrentIrpStackLocation( irp ); + ctrl_code = irp_stack->Parameters.DeviceIoControl.IoControlCode; + dev_mgr = ( ( PDEVEXT_HEADER )dev_obj->DeviceExtension )->dev_mgr; + + if( irp_stack->MajorFunction != IRP_MJ_DEVICE_CONTROL && + irp_stack->MajorFunction != IRP_MJ_INTERNAL_DEVICE_CONTROL ) + { + GENDRV_COMPLETE_START_IO( dev_obj, STATUS_INVALID_DEVICE_REQUEST, irp ); + } + + switch( ctrl_code ) + { + case IOCTL_SUBMIT_URB_RD: + case IOCTL_SUBMIT_URB_NOIO: + case IOCTL_SUBMIT_URB_WR: + { + LONG buf_size; + PURB purb; + KIRQL old_irql; + ULONG endp_idx, if_idx, user_buffer_length; + PUCHAR user_buffer; + PUSB_DEV pdev; + DEV_HANDLE endp_handle; + PUSB_ENDPOINT pendp; + + NTSTATUS status; + PUSB_CTRL_SETUP_PACKET psetup; + + if( irp_stack->Parameters.DeviceIoControl.InputBufferLength < sizeof( URB ) ) + { + GENDRV_COMPLETE_START_IO( dev_obj, STATUS_INVALID_PARAMETER, irp ); + } + + purb = ( PURB )irp->AssociatedIrp.SystemBuffer; + if( ctrl_code == IOCTL_SUBMIT_URB_RD || ctrl_code == IOCTL_SUBMIT_URB_WR ) + { + if( irp_stack->MajorFunction == IRP_MJ_DEVICE_CONTROL ) + { + user_buffer = MmGetSystemAddressForMdl( irp->MdlAddress ); + user_buffer_length = irp_stack->Parameters.DeviceIoControl.OutputBufferLength; + if( user_buffer_length == 0 ) + GENDRV_COMPLETE_START_IO( dev_obj, STATUS_INVALID_PARAMETER, irp ); + } + else + { + if( purb->data_buffer == NULL || purb->data_length == 0 ) + GENDRV_COMPLETE_START_IO( dev_obj, STATUS_INVALID_PARAMETER, irp ); + user_buffer = purb->data_buffer; + user_buffer_length = purb->data_length; + } + } + + purb->reference = 0; + endp_handle = purb->endp_handle; + + if( usb_query_and_lock_dev( dev_mgr, endp_handle & ~0xffff, &pdev ) != STATUS_SUCCESS ) + { + GENDRV_COMPLETE_START_IO( dev_obj, 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 > 16 ) + { + 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 ) + { + 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 register irp before the urb is scheduled to + // avoid race condition. + // + ASSERT( dev_mgr_register_irp( dev_mgr, irp, purb ) ); + // + // the irp can not be canceled at this point, since it is + // now the current irp and not in any urb queue. dev_mgr_cancel_irp + // can not find it and simply return. + // + // FIXME: there is a time window that the irp is registered and + // the urb is not queued. In the meantime, the cancel + // request may come and cause the irp removed from the irp + // queue while fail to cancel due to urb not in any urb queue . + // Thus from that point on, the irp can not be canceled till it + // is completed or hanging there forever. + // + status = usb_submit_urb( dev_mgr, purb ); + if( status != STATUS_PENDING ) + { + // unmark the pending bit + 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; + GENDRV_COMPLETE_START_IO( dev_obj, status, irp ); + } + return; + +ERROR_OUT1: + unlock_dev( pdev, FALSE ); + usb_unlock_dev( pdev ); + irp->IoStatus.Information = 0; + GENDRV_COMPLETE_START_IO( dev_obj, status, irp ); + } + } + GENDRV_COMPLETE_START_IO( dev_obj, STATUS_INVALID_DEVICE_REQUEST, irp ); +} + +VOID +gendrv_clean_up_queued_irps( +PDEVICE_OBJECT dev_obj +) +{ + // called when device may not function or about to be removed, need cleanup + KIRQL cancelIrql; + PIRP irp, cur_irp; + PKDEVICE_QUEUE_ENTRY packet; + LIST_ENTRY cancel_irps, *pthis, *pnext; + PDEVEXT_HEADER dev_hdr; + + // + // cancel all the irps in the queue + // + if( dev_obj == NULL ) + return; + + InitializeListHead( &cancel_irps ); + + // remove the irps from device queue + IoAcquireCancelSpinLock( &cancelIrql ); + cur_irp = dev_obj->CurrentIrp; + while( packet = KeRemoveDeviceQueue( &dev_obj->DeviceQueue ) ) + { + irp = struct_ptr( packet, IRP, Tail.Overlay.DeviceQueueEntry ); + InsertTailList( &cancel_irps, &irp->Tail.Overlay.DeviceQueueEntry.DeviceListEntry ); + } + IoReleaseCancelSpinLock( cancelIrql ); + + // cancel the irps in process + // we did not cancel the current irp, it will be done by hcd when + // disconnect is detected. + // remove_irp_from_list( &dev_mgr->irp_list, cur_irp, dev_mgr ); + + while( IsListEmpty( &cancel_irps ) == FALSE ) + { + pthis = RemoveHeadList( &cancel_irps ); + irp = struct_ptr( pthis, IRP, Tail.Overlay.DeviceQueueEntry.DeviceListEntry ); + irp->IoStatus.Information = 0; + irp->IoStatus.Status = STATUS_CANCELLED; + IoCompleteRequest( irp, IO_NO_INCREMENT ); + } + return; +} + +VOID +gendrv_cancel_queued_irp( +PDEVICE_OBJECT dev_obj, +PIRP pirp +) +{ + // cancel routine for irps queued in the device queue + PUSB_DEV_MANAGER dev_mgr; + PDEVEXT_HEADER pdev_ext_hdr; + ULONG i; + + pdev_ext_hdr = ( PDEVEXT_HEADER )dev_obj->DeviceExtension; + dev_mgr = pdev_ext_hdr->dev_mgr; + + if( dev_obj->CurrentIrp == pirp ) + { + // just before start_io set the cancel routine to null + IoReleaseCancelSpinLock( pirp->CancelIrql ); + // we did not IoStartNextPacket, leave it for dev_mgr_cancel_irp, that + // is user have to call CancelIo again. + return; + } + + KeRemoveEntryDeviceQueue( &dev_obj->DeviceQueue, &pirp->Tail.Overlay.DeviceQueueEntry ); + IoReleaseCancelSpinLock( pirp->CancelIrql ); + + pirp->IoStatus.Information = 0; + pirp->IoStatus.Status = STATUS_CANCELLED; + IoCompleteRequest( pirp, IO_NO_INCREMENT ); + // the device queue is moved on, no need to call IoStartNextPacket + return; +} + diff --git a/reactos/drivers/usb/nt4compat/usbdriver/gendrv.h b/reactos/drivers/usb/nt4compat/usbdriver/gendrv.h new file mode 100644 index 00000000000..1ef8039a8df --- /dev/null +++ b/reactos/drivers/usb/nt4compat/usbdriver/gendrv.h @@ -0,0 +1,65 @@ +#ifndef __GENDRV_H__ +#define __GENDRV_H__ + +#include "td.h" +#include "hub.h" + +#define GENDRV_MAX_EXT_DRVR 2 +#define GENDRV_DRVR_FLAG_IF_DRVR 0x01 + +#define GENDRV_MSG_ADDDEVICE 0x01 +#define GENDRV_MSG_STOPDEVICE 0x02 +#define GENDRV_MSG_DISCDEVICE 0x03 + +typedef struct _GENDRV_EXT_DRVR_ENTRY +{ + LIST_ENTRY drvr_link; // used for dynamic load/unload driver + ULONG drvr_key; // used to find the driver loaded, the key + // is ( vendor_id << 16 )|( product_id ) or + // ( class_id << 16 ) | ( sub_class_id << 8 ) | ( protocol_id )) + PDRIVER_OBJECT pext_drvr; // the external driver count + ULONG ref_count; // number of devices attached + LIST_HEAD dev_list; // link the devices by deviceExtension->dev_obj_link + +} GENDRV_EXT_DRVR_ENTRY, *PGENDRV_EXT_DRVR_ENTRY; + +typedef struct _GENDRV_DRVR_EXTENSION +{ + FAST_MUTEX drvr_ext_mutex; + ULONG ext_drvr_count; // loaded driver count + LIST_HEAD ext_drvr_list; + GENDRV_EXT_DRVR_ENTRY ext_drvr_array[ GENDRV_MAX_EXT_DRVR ]; + +}GENDRV_DRVR_EXTENSION, *PGENDRV_DRVR_EXTENSION; + +typedef struct _GENDRV_IF_CTX +{ + UCHAR if_idx; // valid for if device + PUSB_INTERFACE_DESC pif_desc; + +} GENDRV_IF_CTX, *PGENDRV_IF_IDX; + +typedef struct _GENDRV_DEVICE_EXTENSION +{ + //this structure is the device extension for dev_obj + //created for the device. + DEVEXT_HEADER dev_ext_hdr; + ULONG flags; + LIST_ENTRY dev_obj_link; // this link is used by the driver object to track the existing dev_objs + PGENDRV_EXT_DRVR_ENTRY ext_drvr_entry; + + PDEVICE_OBJECT pdo; // Our device object + DEV_HANDLE dev_handle; // handle to the usb_dev under + PUCHAR desc_buf; + UCHAR dev_id; // used to build symbolic link + GENDRV_IF_CTX if_ctx; + + KEVENT sync_event; + KSPIN_LOCK dev_lock; + + PUSB_DRIVER pdriver; + PUSB_DEV_MANAGER dev_mgr; + +} GENDRV_DEVICE_EXTENSION, *PGENDRV_DEVICE_EXTENSION; + +#endif diff --git a/reactos/drivers/usb/nt4compat/usbdriver/hcd.h b/reactos/drivers/usb/nt4compat/usbdriver/hcd.h new file mode 100644 index 00000000000..102bb693688 --- /dev/null +++ b/reactos/drivers/usb/nt4compat/usbdriver/hcd.h @@ -0,0 +1,71 @@ +#ifndef __HCD_H__ +#define __HCD_H__ + +#include "ntddk.h" + +#define HCD_TYPE_MASK 0xf0 +#define HCD_TYPE_UHCI 0x10 +#define HCD_TYPE_OHCI 0x20 +#define HCD_TYPE_EHCI 0x30 + +#define hcd_type( hCD ) ( ( ( hCD )->flags ) & HCD_TYPE_MASK ) +#define usb2( hCD ) ( hcd_type( hCD ) == HCD_TYPE_EHCI ) + +#define HCD_ID_MASK 0xf + +#ifndef BOOL +#define BOOL ULONG +#endif + +#define HCD_DISP_READ_PORT_COUNT 1 // the param is a pointer to UCHAR +#define HCD_DISP_READ_RH_DEV_CHANGE 2 // the param is a buffer to hold conn change on all the port + // must have the rh dev_lock acquired + +typedef VOID ( *PHCD_SET_DEV_MGR )( struct _HCD* hcd, struct _USB_DEV_MANAGER *dev_mgr ); +typedef struct _USB_DEV_MANAGER* ( *PHCD_GET_DEV_MGR )( struct _HCD* hcd ); +typedef ULONG ( *PHCD_GET_TYPE )( struct _HCD* hcd ); +typedef VOID ( *PHCD_SET_ID )( struct _HCD* hcd, UCHAR id ); +typedef UCHAR ( *PHCD_GET_ID )( struct _HCD* hcd ); +typedef UCHAR ( *PHCD_ALLOC_ADDR )( struct _HCD* hcd ); +typedef VOID ( *PHCD_FREE_ADDR )( struct _HCD* hcd, UCHAR addr ); +typedef NTSTATUS ( *PHCD_SUBMIT_URB )( struct _HCD* hcd, struct _USB_DEV *pdev, struct _USB_ENDPOINT *pendp, struct _URB *purb ); +typedef VOID ( *PHCD_GENERIC_URB_COMPLETION )( struct _URB *purb, PVOID context ); //we can get te hcd from purb +typedef struct _USB_DEV* ( *PHCD_GET_ROOT_HUB )( struct _HCD* hcd ); +typedef VOID ( *PHCD_SET_ROOT_HUB )( struct _HCD* hcd, struct _USB_DEV *root_hub ); +typedef BOOL ( *PHCD_REMOVE_DEVICE )( struct _HCD* hcd, struct _USB_DEV *pdev ); +typedef BOOL ( *PHCD_RH_RESET_PORT )( struct _HCD* hcd, UCHAR port_idx ); //must have the rh dev_lock acquired +typedef BOOL ( *PHCD_RELEASE )( struct _HCD* hcd ); //must have the rh dev_lock acquired +typedef NTSTATUS( *PHCD_CANCEL_URB)( struct _HCD* hcd, struct _USB_DEV *pdev, struct _USB_ENDPOINT* pendp, struct _URB *purb ); +typedef BOOL ( *PHCD_START )( struct _HCD* hcd ); //must have the rh dev_lock acquired +typedef NTSTATUS ( *PHCD_DISPATCH )( struct _HCD* hcd, LONG disp_code, PVOID param ); // locking depends on type of code + +typedef struct _HCD +{ + PHCD_SET_DEV_MGR hcd_set_dev_mgr; + PHCD_GET_DEV_MGR hcd_get_dev_mgr; + PHCD_GET_TYPE hcd_get_type; + PHCD_SET_ID hcd_set_id; + PHCD_GET_ID hcd_get_id; + PHCD_ALLOC_ADDR hcd_alloc_addr; + PHCD_FREE_ADDR hcd_free_addr; + PHCD_SUBMIT_URB hcd_submit_urb; + PHCD_GENERIC_URB_COMPLETION hcd_generic_urb_completion; + PHCD_GET_ROOT_HUB hcd_get_root_hub; + PHCD_SET_ROOT_HUB hcd_set_root_hub; + PHCD_REMOVE_DEVICE hcd_remove_device; + PHCD_RH_RESET_PORT hcd_rh_reset_port; + PHCD_RELEASE hcd_release; + PHCD_CANCEL_URB hcd_cancel_urb; + PHCD_START hcd_start; + PHCD_DISPATCH hcd_dispatch; + + //interfaces for all the host controller + ULONG flags; //hcd types | hcd id + ULONG conn_count; //statics for connection activities + struct _USB_DEV_MANAGER *dev_mgr; //pointer manager + UCHAR dev_addr_map[ 128 / 8 ]; //bitmap for the device addrs + struct _DEVICE_EXTENSION *pdev_ext; + +} HCD, *PHCD; + +#endif diff --git a/reactos/drivers/usb/nt4compat/usbdriver/hub.c b/reactos/drivers/usb/nt4compat/usbdriver/hub.c new file mode 100644 index 00000000000..a01608e64c2 --- /dev/null +++ b/reactos/drivers/usb/nt4compat/usbdriver/hub.c @@ -0,0 +1,5184 @@ +/** + * hub.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 "hub.h" +#include "td.h" +#include "debug.h" +#include "umss.h" +//---------------------------------------------------------- +//event pool routines +#define crash_machine() \ +{ ( ( PUSB_DEV ) 0 )->flags = 0x12345; } + +#define hub_if_from_dev( pdEV, pIF ) \ +{\ + int i;\ + for( i = 0; i < pdEV->usb_config->if_count; i ++ )\ + {\ + if( pdEV->usb_config->interf[ i ].pusb_if_desc->bInterfaceClass\ + == USB_CLASS_HUB )\ + {\ + break;\ + }\ + }\ +\ + if( i < pdEV->usb_config->if_count )\ + pIF = &pdev->usb_config->interf[ i ];\ + else\ + pIF = NULL;\ +\ +} + +#define hub_ext_from_dev( pdEV ) ( ( PHUB2_EXTENSION )pdEV->dev_ext ) + +#define realloc_buf( pdEV, puRB ) \ +{\ + PBYTE data_buf;\ + int i;\ + data_buf = usb_alloc_mem( NonPagedPool, ( pdEV )->desc_buf_size += 1024 );\ + RtlZeroMemory( data_buf, ( pdEV )->desc_buf_size );\ + for( i = 0; i < ( LONG )( puRB )->context; i++ )\ + {\ + data_buf[ i ] = ( pdEV )->desc_buf[ i ];\ + }\ + usb_free_mem( ( pdEV )->desc_buf );\ + ( pdEV )->desc_buf = data_buf;\ + ( pdEV )->pusb_dev_desc = ( PUSB_DEVICE_DESC )( pdEV )->desc_buf;\ + ( puRB )->data_buffer = &data_buf[ ( LONG ) ( puRB )->context ];\ +} + + +BOOL +dev_mgr_set_if_driver( +PUSB_DEV_MANAGER dev_mgr, +DEV_HANDLE if_handle, +PUSB_DRIVER pdriver, +PUSB_DEV pdev //if pdev != NULL, we use pdev instead if_handle, and must have dev_lock acquired. +) +{ + ULONG i; + USE_IRQL; + + if( dev_mgr == NULL || if_handle == 0 || pdriver == NULL ) + return FALSE; + + i = if_idx_from_handle( if_handle ); + if( pdev != NULL ) + { + if( dev_state( pdev ) < USB_DEV_STATE_BEFORE_ZOMB ) + { + pdev->usb_config->interf[ i ].pif_drv = pdriver; + return TRUE; + } + return FALSE; + } + + if( usb_query_and_lock_dev( dev_mgr, if_handle, &pdev ) != STATUS_SUCCESS ) + return FALSE; + + lock_dev( pdev, TRUE ); + if( dev_state( pdev ) != USB_DEV_STATE_ZOMB ) + { + pdev->usb_config->interf[ i ].pif_drv = pdriver; + } + unlock_dev( pdev, TRUE ); + usb_unlock_dev( pdev ); + return TRUE; +} + +BOOL +dev_mgr_set_driver( +PUSB_DEV_MANAGER dev_mgr, +DEV_HANDLE dev_handle, +PUSB_DRIVER pdriver, +PUSB_DEV pdev //if pdev != NULL, we use pdev instead if_handle +) +{ + USE_IRQL; + + if( dev_mgr == NULL || dev_handle == 0 || pdriver == NULL ) + return FALSE; + + if( pdev != NULL ) + { + if( dev_state( pdev ) < USB_DEV_STATE_BEFORE_ZOMB ) + { + pdev->dev_driver = pdriver; + return TRUE; + } + return FALSE; + } + + if( usb_query_and_lock_dev( dev_mgr, dev_handle, &pdev ) != STATUS_SUCCESS ) + return FALSE; + + lock_dev( pdev, FALSE ); + if( dev_state( pdev ) < USB_DEV_STATE_BEFORE_ZOMB ) + { + pdev->dev_driver = pdriver; + } + unlock_dev( pdev, FALSE ); + usb_unlock_dev( pdev ); + + return TRUE; + +} + +USB_DRIVER g_driver_list[ DEVMGR_MAX_DRIVERS ]; +USB_DEV_MANAGER g_dev_mgr; +extern ULONG cpu_clock_freq; + +BOOL +hub_check_reset_port_status( +PUSB_DEV pdev, +LONG port_idx +); + +BOOL +hub_start_next_reset_port( +PUSB_DEV_MANAGER dev_mgr, +BOOL from_dpc +); + +VOID +hub_reexamine_port_status_queue( +PUSB_DEV hub_dev, +ULONG port_idx, +BOOL from_dpc +); + +void +hub_int_completion( +PURB purb, +PVOID pcontext +); + +VOID +hub_get_port_status_completion( +PURB purb, +PVOID context +); + +VOID +hub_clear_port_feature_completion( +PURB purb, +PVOID context +); + +VOID +hub_event_examine_status_que( +PUSB_DEV pdev, +ULONG event, +ULONG context, //hub_ext +ULONG param //port_idx +); + +VOID +hub_timer_wait_dev_stable( +PUSB_DEV pdev, +PVOID context //port-index +); + +VOID +hub_event_dev_stable( +PUSB_DEV pdev, +ULONG event, +ULONG context, //hub_ext +ULONG param //port_idx +); + +VOID +hub_post_esq_event( +PUSB_DEV pdev, +BYTE port_idx, +PROCESS_EVENT pe +); + +void +hub_set_cfg_completion( +PURB purb, +PVOID pcontext +); + +void +hub_get_hub_desc_completion( +PURB purb, +PVOID pcontext +); + +NTSTATUS +hub_start_int_request( +PUSB_DEV pdev +); + +BOOL +hub_remove_reset_event( +PUSB_DEV pdev, +ULONG port_idx, +BOOL from_dpc +); + +BOOL +hub_driver_init( +PUSB_DEV_MANAGER dev_mgr, +PUSB_DRIVER pdriver +); + +BOOL +hub_driver_destroy( +PUSB_DEV_MANAGER dev_mgr, +PUSB_DRIVER pdriver +); + +BOOL +hub_connect( +PCONNECT_DATA init_param, +DEV_HANDLE dev_handle +); + +BOOL +hub_disconnect( +PUSB_DEV_MANAGER dev_mgr, +DEV_HANDLE dev_handle +); + +BOOL +hub_stop( +PUSB_DEV_MANAGER dev_mgr, +DEV_HANDLE dev_handle +); + +NTSTATUS +hub_disable_port_request( +PUSB_DEV pdev, +UCHAR port_idx +); + +VOID +hub_start_reset_port_completion( +PURB purb, +PVOID context +); + +BOOL +dev_mgr_start_config_dev( +PUSB_DEV pdev +); + +BOOL +dev_mgr_event_init( +PUSB_DEV dev, //always null. we do not use this param +ULONG event, +ULONG context, +ULONG param +); + +VOID +dev_mgr_get_desc_completion( +PURB purb, +PVOID context +); + +VOID +dev_mgr_event_select_driver( +PUSB_DEV pdev, +ULONG event, +ULONG context, +ULONG param +); + +LONG +dev_mgr_score_driver_for_dev( +PUSB_DEV_MANAGER dev_mgr, +PUSB_DRIVER pdriver, +PUSB_DEVICE_DESC pdev_desc +); + +NTSTATUS +dev_mgr_destroy_usb_config( +PUSB_CONFIGURATION pcfg +); + +BOOL +dev_mgr_start_select_driver( +PUSB_DEV pdev +); + +VOID +dev_mgr_cancel_irp( +PDEVICE_OBJECT pdev_obj, +PIRP pirp +); + +BOOL +init_event_pool( +PUSB_EVENT_POOL pool +) +{ + int i; + + if( pool == NULL ) + return FALSE; + + if( ( pool->event_array = usb_alloc_mem( + NonPagedPool, + sizeof( USB_EVENT ) * MAX_EVENTS ) ) + == NULL) + return FALSE; + + InitializeListHead( &pool->free_que ); + KeInitializeSpinLock( &pool->pool_lock ); + pool->total_count = MAX_EVENTS; + pool->free_count = 0; + + for( i = 0; i < MAX_EVENTS; i++ ) + { + free_event( pool, &pool->event_array[ i ] ); + } + + return TRUE; +} + +BOOL +free_event( +PUSB_EVENT_POOL pool, +PUSB_EVENT pevent +) +{ + if( pool == NULL || pevent == NULL ) + { + return FALSE; + } + + RtlZeroMemory( pevent, sizeof( USB_EVENT ) ); + InsertTailList( &pool->free_que, (PLIST_ENTRY) pevent ); + pool->free_count++; + usb_dbg_print( DBGLVL_MAXIMUM + 1, ( "free_event(): alloced=0x%x, addr=0x%x\n", MAX_EVENTS - pool->free_count, pevent ) ); + + return TRUE; +} + +PUSB_EVENT +alloc_event( +PUSB_EVENT_POOL pool, +LONG count +) //null if failed +{ + PUSB_EVENT new; + if( pool == NULL || count != 1) + return NULL; + + if( pool->free_count == 0 ) + return NULL; + + new = ( PUSB_EVENT )RemoveHeadList( &pool->free_que ); + pool->free_count --; + + usb_dbg_print( DBGLVL_MAXIMUM + 1, ( "alloc_event(): alloced=0x%x, addr=0x%x\n", MAX_EVENTS - pool->free_count, new ) ); + return new; +} + +BOOL +destroy_event_pool( +PUSB_EVENT_POOL pool +) +{ + if( pool == NULL ) + return FALSE; + + InitializeListHead( &pool->free_que ); + pool->free_count = pool->total_count = 0; + usb_free_mem( pool->event_array ); + pool->event_array = NULL; + + return TRUE; +} + +VOID +event_list_default_process_event( +PUSB_DEV pdev, +ULONG event, +ULONG context, +ULONG param +) +{} + +//---------------------------------------------------------- +//timer_svc pool routines + +BOOL +init_timer_svc_pool( +PTIMER_SVC_POOL pool +) +{ + int i; + if( pool == NULL ) + return FALSE; + + pool->timer_svc_array = usb_alloc_mem( NonPagedPool, sizeof( TIMER_SVC ) * MAX_TIMER_SVCS ); + InitializeListHead( &pool->free_que ); + pool->free_count = 0; + pool->total_count = MAX_TIMER_SVCS; + KeInitializeSpinLock( &pool->pool_lock ); + + for( i = 0; i < MAX_TIMER_SVCS; i++ ) + { + free_timer_svc( pool, &pool->timer_svc_array[ i ] ); + } + + return TRUE; +} + +BOOL +free_timer_svc( +PTIMER_SVC_POOL pool, +PTIMER_SVC ptimer +) +{ + if( pool == NULL || ptimer == NULL ) + return FALSE; + + RtlZeroMemory( ptimer, sizeof( TIMER_SVC ) ); + InsertTailList( &pool->free_que, ( PLIST_ENTRY )&ptimer->timer_svc_link ); + pool->free_count ++; + + return TRUE; +} + +PTIMER_SVC +alloc_timer_svc( +PTIMER_SVC_POOL pool, +LONG count +) //null if failed +{ + PTIMER_SVC new; + if( pool == NULL || count != 1 ) + return NULL; + + if( pool->free_count <= 0 ) + return NULL; + + new = ( PTIMER_SVC )RemoveHeadList( &pool->free_que ); + pool->free_count --; + return new; + +} + +BOOL +destroy_timer_svc_pool( +PTIMER_SVC_POOL pool +) +{ + if( pool == NULL ) + return FALSE; + + usb_free_mem( pool->timer_svc_array ); + pool->timer_svc_array = NULL; + InitializeListHead( &pool->free_que ); + pool->free_count = 0; + pool->total_count = 0; + + return TRUE; +} + +BOOL +dev_mgr_post_event( +PUSB_DEV_MANAGER dev_mgr, +PUSB_EVENT event +) +{ + KIRQL old_irql; + + if( dev_mgr == NULL || event == NULL ) + return FALSE; + + KeAcquireSpinLock( &dev_mgr->event_list_lock, &old_irql ); + InsertTailList( &dev_mgr->event_list, &event->event_link ); + KeReleaseSpinLock( &dev_mgr->event_list_lock, old_irql ); + + KeSetEvent( &dev_mgr->wake_up_event, 0, FALSE ); + return TRUE; +} + + +VOID +event_list_default_process_queue( +PLIST_HEAD event_list, +PUSB_EVENT_POOL event_pool, +PUSB_EVENT usb_event, +PUSB_EVENT out_event +) +{ + //remove the first event from the event list, and copy it to + //out_event + + if( event_list == NULL || event_pool == NULL || usb_event == NULL || out_event == NULL ) + return; + + RemoveEntryList( &usb_event->event_link ); + RtlCopyMemory( out_event, usb_event, sizeof( USB_EVENT ) ); + free_event( event_pool, usb_event ); + return; +} + +BOOL +psq_enqueue( +PPORT_STATUS_QUEUE psq, +ULONG status +) +{ + if( psq == NULL ) + return FALSE; + + if( psq_is_full( psq ) ) + return FALSE; + + psq->port_status[ psq->status_count ].wPortChange = HIWORD( status ); + psq->port_status[ psq->status_count ].wPortStatus = LOWORD( status ); + + psq->status_count++; + + usb_dbg_print( DBGLVL_MAXIMUM, ("psq_enqueue(): last status=0x%x, status count=0x%x, port_flag=0x%x\n", \ + status, \ + psq->status_count, \ + psq->port_flags ) ); + return TRUE; + +} + +VOID +psq_init( +PPORT_STATUS_QUEUE psq +) +{ + RtlZeroMemory( psq, sizeof( PORT_STATUS_QUEUE ) ); + psq->port_flags = STATE_IDLE | USB_PORT_FLAG_DISABLE; +} + +ULONG +psq_outqueue( +PPORT_STATUS_QUEUE psq +) //return 0xffffffff if no element +{ + ULONG status; + + if( psq == NULL ) + return 0; + + if( psq_is_empty( psq ) ) + return 0; + + status = ( ( PULONG )&psq->port_status )[ 0 ]; + psq->port_status[ 0 ] = psq->port_status[ 1 ]; + psq->port_status[ 1 ] = psq->port_status[ 2 ]; + psq->port_status[ 2 ] = psq->port_status[ 3 ]; + psq->status_count--; + + return status; +} + +BOOL +psq_push( +PPORT_STATUS_QUEUE psq, +ULONG status +) +{ + if( psq == NULL ) + return FALSE; + + status = ( ( PULONG )&psq->port_status )[ 0 ]; + psq->port_status[ 3 ] = psq->port_status[ 2 ]; + psq->port_status[ 2 ] = psq->port_status[ 1 ]; + psq->port_status[ 1 ] = psq->port_status[ 0 ]; + + ( ( PULONG )&psq->port_status )[ 0 ]= status; + + psq->status_count++; + psq->status_count = ( ( 4 > psq->status_count ) ? psq->status_count : 4 ); + + return TRUE; +} + +VOID +dev_mgr_driver_entry_init( +PUSB_DEV_MANAGER dev_mgr, +PUSB_DRIVER pdrvr +) +{ + // Device Info + + RtlZeroMemory( pdrvr, sizeof( USB_DRIVER ) * DEVMGR_MAX_DRIVERS ); + + pdrvr[ RH_DRIVER_IDX ].driver_init = rh_driver_init; // in fact, this routine will init the rh device rather that the driver struct. + pdrvr[ RH_DRIVER_IDX ].driver_destroy = rh_driver_destroy; // we do not need rh to destroy currently, since that may means fatal hardware failure + + pdrvr[ HUB_DRIVER_IDX ].driver_init = hub_driver_init; //no need, since dev_mgr is also a hub driver + pdrvr[ HUB_DRIVER_IDX ].driver_destroy = hub_driver_destroy; + + pdrvr[ UMSS_DRIVER_IDX ].driver_init = umss_if_driver_init; + pdrvr[ UMSS_DRIVER_IDX ].driver_destroy = umss_if_driver_destroy; + + pdrvr[ COMP_DRIVER_IDX ].driver_init = compdev_driver_init; + pdrvr[ COMP_DRIVER_IDX ].driver_destroy = compdev_driver_destroy; + + pdrvr[ GEN_DRIVER_IDX ].driver_init = gendrv_driver_init; + pdrvr[ GEN_DRIVER_IDX ].driver_destroy = gendrv_driver_destroy; + + pdrvr[ GEN_IF_DRIVER_IDX ].driver_init = gendrv_if_driver_init; + pdrvr[ GEN_IF_DRIVER_IDX ].driver_destroy = gendrv_if_driver_destroy; +} + +BOOL +dev_mgr_strobe( +PUSB_DEV_MANAGER dev_mgr +) +{ + PUSB_EVENT pevent; + HANDLE thread_handle; + + if( dev_mgr == NULL ) + return FALSE; + if( dev_mgr->hcd_count == 0 ) + return FALSE; + + dev_mgr->term_flag = FALSE; + + if( dev_mgr->hcd_count == 0 ) + return FALSE; + + KeInitializeSpinLock( &dev_mgr->event_list_lock ); + InitializeListHead( &dev_mgr->event_list ); + init_event_pool( &dev_mgr->event_pool ); + + pevent = alloc_event( &dev_mgr->event_pool, 1 ); + if( pevent == NULL ) + { + destroy_event_pool( &dev_mgr->event_pool ); + return FALSE; + } + + pevent->flags = USB_EVENT_FLAG_ACTIVE; + pevent->event = USB_EVENT_INIT_DEV_MGR; + + pevent->process_queue = event_list_default_process_queue; + pevent->process_event = dev_mgr_event_init; + + pevent->context = ( ULONG )dev_mgr; + + KeInitializeEvent( &dev_mgr->wake_up_event, + SynchronizationEvent, + FALSE ); + + InsertTailList( &dev_mgr->event_list, &pevent->event_link ); + + if( PsCreateSystemThread( + &thread_handle, + 0, + NULL, + NULL, + NULL, + dev_mgr_thread, + dev_mgr ) + != STATUS_SUCCESS ) + { + destroy_event_pool( &dev_mgr->event_pool ); + return FALSE; + } + + ObReferenceObjectByHandle( + thread_handle, + THREAD_ALL_ACCESS, + NULL, + KernelMode, + (PVOID*) &dev_mgr->pthread, + NULL); + + ZwClose( thread_handle ); + + return TRUE; +} + +BOOL +dev_mgr_event_init( +PUSB_DEV pdev, //always null. we do not use this param +ULONG event, +ULONG context, +ULONG param +) +{ + LARGE_INTEGER due_time; + PUSB_DEV_MANAGER dev_mgr; + LONG i; + + usb_dbg_print( DBGLVL_MAXIMUM, ( "dev_mgr_event_init(): dev_mgr=0x%x, event=0x%x\n", context, event ) ); + dev_mgr = ( PUSB_DEV_MANAGER ) context; + if( dev_mgr == NULL ) + return FALSE; + + if( event != USB_EVENT_INIT_DEV_MGR ) + return FALSE; + + //dev_mgr->root_hub = NULL; + KeInitializeTimer( &dev_mgr->dev_mgr_timer ); + + KeInitializeDpc( &dev_mgr->dev_mgr_timer_dpc, + dev_mgr_timer_dpc_callback, + ( PVOID )dev_mgr ); + + KeInitializeSpinLock( &dev_mgr->timer_svc_list_lock ); + InitializeListHead( &dev_mgr->timer_svc_list ); + init_timer_svc_pool( &dev_mgr->timer_svc_pool ); + dev_mgr->timer_click = 0; + + init_irp_list( &dev_mgr->irp_list ); + + KeInitializeSpinLock( &dev_mgr->dev_list_lock ); + InitializeListHead( &dev_mgr->dev_list ); + + dev_mgr->hub_count = 0; + InitializeListHead( &dev_mgr->hub_list ); + + dev_mgr->conn_count = 0; + dev_mgr->driver_list = g_driver_list; + + dev_mgr_driver_entry_init( dev_mgr, dev_mgr->driver_list ); + + for( i = 0; i < DEVMGR_MAX_DRIVERS; i++ ) + { + if( dev_mgr->driver_list[ i ].driver_init == NULL ) + continue; + + if( dev_mgr->driver_list[ i ].driver_init( dev_mgr, &dev_mgr->driver_list[ i ] ) == FALSE ) + break; + } + if( i == DEVMGR_MAX_DRIVERS ) + { + due_time.QuadPart = -( DEV_MGR_TIMER_INTERVAL_NS - 10 ); + + KeSetTimerEx( &dev_mgr->dev_mgr_timer, + due_time, + DEV_MGR_TIMER_INTERVAL_MS, + &dev_mgr->dev_mgr_timer_dpc ); + + return TRUE; + } + + i--; + + for( ; i >= 0; i-- ) + { + if( dev_mgr->driver_list[ i ].driver_destroy ) + dev_mgr->driver_list[ i ].driver_destroy( dev_mgr, &dev_mgr->driver_list[ i ] ); + } + + KeCancelTimer( &dev_mgr->dev_mgr_timer ); + KeRemoveQueueDpc( &dev_mgr->dev_mgr_timer_dpc ); + return FALSE; + +} + +VOID dev_mgr_destroy( +PUSB_DEV_MANAGER dev_mgr +) +{ + LONG i; + // oops... + KeCancelTimer ( &dev_mgr->dev_mgr_timer ); + KeRemoveQueueDpc( &dev_mgr->dev_mgr_timer_dpc ); + + for( i = DEVMGR_MAX_DRIVERS - 1; i >= 0; i-- ) + dev_mgr->driver_list[ i ].driver_destroy( dev_mgr, &dev_mgr->driver_list[ i ]); + + destroy_irp_list( &dev_mgr->irp_list ); + destroy_timer_svc_pool( &dev_mgr->timer_svc_pool ); + destroy_event_pool( &dev_mgr->event_pool ); + +} + +VOID +dev_mgr_thread( +PVOID context +) +{ + PUSB_DEV_MANAGER dev_mgr; + PUSB_EVENT pevent; + PLIST_ENTRY pthis, pnext; + USB_EVENT usb_event; + LARGE_INTEGER time_out; + NTSTATUS status; + BOOL dev_mgr_inited; + KIRQL old_irql; + LONG i; + + dev_mgr = ( PUSB_DEV_MANAGER )context; + dev_mgr_inited = FALSE; + usb_cal_cpu_freq(); + time_out.u.LowPart = ( 10 * 1000 * 1000 ) * 100 - 1; //1 minutes + time_out.u.HighPart = 0; + time_out.QuadPart = -time_out.QuadPart; + + //usb_dbg_print( DBGLVL_MAXIMUM + 1, ( "dev_mgr_thread(): current uhci status=0x%x\n", uhci_status( dev_mgr->pdev_ext->uhci ) ) ); + + while( dev_mgr->term_flag == FALSE ) + { + KeAcquireSpinLock( &dev_mgr->event_list_lock, &old_irql ); + if( IsListEmpty( &dev_mgr->event_list ) == TRUE ) + { + KeReleaseSpinLock( &dev_mgr->event_list_lock, old_irql ); + status = KeWaitForSingleObject( + &dev_mgr->wake_up_event, + Executive, + KernelMode, + TRUE, + &time_out + ); + continue; + } + + // usb_dbg_print( DBGLVL_MAXIMUM, ( "dev_mgr_thread(): current element in event list is 0x%x\n", \ + // dbg_count_list( &dev_mgr->event_list ) ) ); + + dev_mgr_inited = TRUE; //since we have post one event, if this statement is executed, dev_mgr_event_init must be called sometime later or earlier + + ListFirst( &dev_mgr->event_list, pthis ); + pevent = struct_ptr( pthis, USB_EVENT, event_link ); + + while( pevent && ( ( pevent->flags & USB_EVENT_FLAG_ACTIVE ) == 0 ) ) + { + //skip inactive ones + ListNext( &dev_mgr->event_list, &pevent->event_link, pnext ); + pevent = struct_ptr( pnext, USB_EVENT, event_link ); + } + + if( pevent != NULL ) + { + if( pevent->process_queue == NULL ) + pevent->process_queue = event_list_default_process_queue; + + pevent->process_queue( &dev_mgr->event_list, + &dev_mgr->event_pool, + pevent, + &usb_event ); + } + else + { + //no active event + KeReleaseSpinLock( &dev_mgr->event_list_lock, old_irql ); + status = KeWaitForSingleObject( + &dev_mgr->wake_up_event, + Executive, + KernelMode, + TRUE, + &time_out // 10 minutes + ); + + usb_dbg_print( DBGLVL_MAXIMUM, ("dev_mgr_thread(): wake up, reason=0x%x\n", status ) ); + continue; + } + + KeReleaseSpinLock( &dev_mgr->event_list_lock, old_irql ); + + if( usb_event.process_event ) + { + usb_event.process_event( usb_event.pdev, + usb_event.event, + usb_event.context, + usb_event.param); + } + else + { + event_list_default_process_event( usb_event.pdev, + usb_event.event, + usb_event.context, + usb_event.param); + } + } + + if( dev_mgr_inited ) + { + for( i = 0; i < dev_mgr->hcd_count; i++ ) + dev_mgr_disconnect_dev( dev_mgr->hcd_array[ i ]->hcd_get_root_hub( dev_mgr->hcd_array[ i ] ) ); + dev_mgr_destroy( dev_mgr ); + } + PsTerminateSystemThread( 0 ); +} + +VOID +dev_mgr_timer_dpc_callback( +PKDPC Dpc, +PVOID context, +PVOID SystemArgument1, +PVOID SystemArgument2 +) +{ + PUSB_DEV_MANAGER dev_mgr; + LIST_HEAD templist; + PLIST_ENTRY pthis, pnext; + static ULONG ticks = 0; + + ticks++; + dev_mgr = ( PUSB_DEV_MANAGER ) context; + if( dev_mgr == NULL ) + return; + + dev_mgr->timer_click ++; + InitializeListHead( &templist ); + + KeAcquireSpinLockAtDpcLevel( &dev_mgr->timer_svc_list_lock ); + if( IsListEmpty( &dev_mgr->timer_svc_list ) == TRUE ) + { + KeReleaseSpinLockFromDpcLevel( &dev_mgr->timer_svc_list_lock ); + return; + } + + ListFirst( &dev_mgr->timer_svc_list, pthis ); + while( pthis ) + { + ( ( PTIMER_SVC )pthis )->counter++; + ListNext( &dev_mgr->timer_svc_list, pthis, pnext ); + if( ( ( PTIMER_SVC )pthis )->counter >= ( ( PTIMER_SVC )pthis )->threshold ) + { + RemoveEntryList( pthis ); + InsertTailList( &templist, pthis ); + } + pthis = pnext; + } + + KeReleaseSpinLockFromDpcLevel( &dev_mgr->timer_svc_list_lock ); + + + while( IsListEmpty( &templist ) == FALSE ) + { + pthis = RemoveHeadList( &templist ); + ( ( PTIMER_SVC )pthis )->func( ( ( PTIMER_SVC )pthis )->pdev, ( PVOID )( ( PTIMER_SVC )pthis )->context ); + KeAcquireSpinLockAtDpcLevel( &dev_mgr->timer_svc_list_lock ); + free_timer_svc( &dev_mgr->timer_svc_pool, ( PTIMER_SVC )pthis ); + KeReleaseSpinLockFromDpcLevel( &dev_mgr->timer_svc_list_lock ); + } + +} + +BOOL +dev_mgr_request_timer_svc( +PUSB_DEV_MANAGER dev_mgr, +PUSB_DEV pdev, +ULONG context, +ULONG due_time, +TIMER_SVC_HANDLER handler +) +{ + PTIMER_SVC timer_svc; + KIRQL old_irql; + + if( dev_mgr == NULL || pdev == NULL || due_time == 0 || handler == NULL ) + return FALSE; + + KeAcquireSpinLock( &dev_mgr->timer_svc_list_lock, &old_irql ); + timer_svc = alloc_timer_svc( &dev_mgr->timer_svc_pool, 1 ); + if( timer_svc == NULL ) + { + KeReleaseSpinLock( &dev_mgr->timer_svc_list_lock, old_irql ); + return FALSE; + } + timer_svc->pdev = pdev; + timer_svc->threshold = due_time; + timer_svc->func = handler; + timer_svc->counter = 0; + + InsertTailList( &dev_mgr->timer_svc_list, &timer_svc->timer_svc_link ); + KeReleaseSpinLock( &dev_mgr->timer_svc_list_lock, old_irql ); + return TRUE; +} + +BYTE +dev_mgr_alloc_addr( +PUSB_DEV_MANAGER dev_mgr, +PHCD hcd +) +{ + // alloc a usb addr for the device within 1-128 + ULONG i; + if( dev_mgr == NULL || hcd == NULL ) + return 0xff; + + return hcd->hcd_alloc_addr( hcd ); +} + +BOOL +dev_mgr_free_addr( +PUSB_DEV_MANAGER dev_mgr, +PUSB_DEV pdev, +BYTE addr +) +{ + PHCD hcd; + if( addr & 0x80 ) + return FALSE; + + if( dev_mgr == NULL || pdev == NULL ) + return FALSE; + + hcd = pdev->hcd; + if( hcd == NULL ) + return FALSE; + hcd->hcd_free_addr( hcd, addr ); + return TRUE; +} + +PUSB_DEV +dev_mgr_alloc_device( +PUSB_DEV_MANAGER dev_mgr, +PHCD hcd +) +{ + BYTE addr; + PUSB_DEV pdev; + + if( ( addr = dev_mgr_alloc_addr( dev_mgr, hcd ) ) == 0xff ) + return NULL; + + pdev = usb_alloc_mem( NonPagedPool, sizeof( USB_DEV ) ); + if( pdev == NULL ) + return NULL; + + RtlZeroMemory( pdev, sizeof( USB_DEV ) ); + + KeInitializeSpinLock( &pdev->dev_lock ); + dev_mgr->conn_count++; + + pdev->flags = USB_DEV_STATE_RESET; //class | cur_state | low speed + pdev->ref_count = 0; + pdev->dev_addr = addr; + + pdev->hcd = hcd; + + pdev->dev_id = dev_mgr->conn_count; //will be used to compose dev_handle + + InitializeListHead( &pdev->default_endp.urb_list ); + pdev->default_endp.pusb_if = ( PUSB_INTERFACE )pdev; + pdev->default_endp.flags = USB_ENDP_FLAG_DEFAULT_ENDP; //toggle | busy-count | stall | default-endp + + return pdev; +} + +VOID +dev_mgr_free_device( +PUSB_DEV_MANAGER dev_mgr, +PUSB_DEV pdev +) +{ + if( pdev == NULL || dev_mgr == NULL ) + return; + + dev_mgr_free_addr( dev_mgr, pdev, pdev->dev_addr ); + if( pdev->usb_config && pdev != pdev->hcd->hcd_get_root_hub( pdev->hcd ) ) + { + //root hub has its config and desc buf allocated together, + //so no usb_config allocated seperately + dev_mgr_destroy_usb_config( pdev->usb_config ); + pdev->usb_config = NULL; + } + if( pdev->desc_buf ) + { + usb_free_mem( pdev->desc_buf ); + pdev->desc_buf = NULL; + } + usb_free_mem( pdev ); + pdev = NULL; + return; +} + +BOOL +rh_driver_destroy( +PUSB_DEV_MANAGER dev_mgr, +PUSB_DRIVER pdriver +) +{ + LONG i; + PHCD hcd; + + if( dev_mgr == NULL ) + return FALSE; + + for( i = 0; i < dev_mgr->hcd_count; i++ ) + { + hcd = dev_mgr->hcd_array[ i ]; + // if( hcd->hcd_get_type( hcd ) != HCD_TYPE_UHCI ) + // continue; + rh_destroy( hcd->hcd_get_root_hub( hcd ) ); + } + return TRUE; +} + +BOOL +rh_driver_init( +PUSB_DEV_MANAGER dev_mgr, +PUSB_DRIVER pdriver +) +{ + + PUSB_DEV rh; + PUSB_CONFIGURATION_DESC pconfig_desc; + PUSB_INTERFACE_DESC pif_desc; + PUSB_ENDPOINT_DESC pendp_desc; + PUSB_CONFIGURATION pconfig; + PUSB_INTERFACE pif; + PUSB_ENDPOINT pendp; + PHUB2_EXTENSION phub_ext; + PTIMER_SVC ptimer; + PURB purb; + NTSTATUS status; + PHCD hcd; + LONG i; + + if( dev_mgr == NULL || pdriver == NULL ) + return FALSE; + + //init driver structure, no PNP table functions + pdriver->driver_desc.flags = USB_DRIVER_FLAG_DEV_CAPABLE; + pdriver->driver_desc.vendor_id = 0xffff; // USB Vendor ID + pdriver->driver_desc.product_id = 0xffff; // USB Product ID. + pdriver->driver_desc.release_num = 0xffff; // Release Number of Device + + pdriver->driver_desc.config_val = 0; // Configuration Value + pdriver->driver_desc.if_num = 0; // Interface Number + pdriver->driver_desc.if_class = USB_CLASS_HUB; // Interface Class + pdriver->driver_desc.if_sub_class = 0; // Interface SubClass + pdriver->driver_desc.if_protocol = 0; // Interface Protocol + + pdriver->driver_desc.driver_name = "USB root hub"; // Driver name for Name Registry + pdriver->driver_desc.dev_class = USB_CLASS_HUB; + pdriver->driver_desc.dev_sub_class = 0; // Device Subclass + pdriver->driver_desc.dev_protocol = 0; // Protocol Info. + + //pdriver->driver_init = rh_driver_init; // initialized in dev_mgr_init_driver + //pdriver->driver_destroy = rh_driver_destroy; + pdriver->disp_tbl.version = 1; // other fields of the dispatch table is not used since rh needs no pnp + + pdriver->driver_ext = 0; + pdriver->driver_ext_size = 0; + + for( i = 0; i < dev_mgr->hcd_count; i++ ) + { + hcd = dev_mgr->hcd_array[ i ]; + //if( hcd->hcd_get_type( hcd ) != HCD_TYPE_UHCI ) + // continue; + + if( ( rh = dev_mgr_alloc_device( dev_mgr, hcd ) )== NULL ) + return FALSE; + + rh->parent_dev = NULL; + rh->port_idx = 0; + rh->hcd = hcd; + rh->flags = USB_DEV_CLASS_ROOT_HUB | USB_DEV_STATE_CONFIGURED; + + if( usb2( hcd ) ) + rh->flags |= USB_DEV_FLAG_HIGH_SPEED; + + rh->dev_driver = pdriver; + + rh->desc_buf_size = sizeof( USB_DEVICE_DESC ) + + sizeof( USB_CONFIGURATION_DESC ) + + sizeof( USB_INTERFACE_DESC ) + + sizeof( USB_ENDPOINT_DESC ) + + sizeof( USB_CONFIGURATION ) + + sizeof( HUB2_EXTENSION ); + + rh->desc_buf = usb_alloc_mem( NonPagedPool, rh->desc_buf_size ); + + if( rh->desc_buf == NULL ) + { + return FALSE; + } + else + RtlZeroMemory( rh->desc_buf, rh->desc_buf_size ); + + rh->pusb_dev_desc = ( PUSB_DEVICE_DESC )rh->desc_buf; + + rh->pusb_dev_desc->bLength = sizeof( USB_DEVICE_DESC ); + rh->pusb_dev_desc->bDescriptorType = USB_DT_DEVICE; + rh->pusb_dev_desc->bcdUSB = 0x110; + if( usb2( hcd ) ) + rh->pusb_dev_desc->bcdUSB = 0x200; + rh->pusb_dev_desc->bDeviceClass = USB_CLASS_HUB; + rh->pusb_dev_desc->bDeviceSubClass = 0; + rh->pusb_dev_desc->bDeviceProtocol = 0; + rh->pusb_dev_desc->bMaxPacketSize0 = 8; + if( usb2( hcd ) ) + { + rh->pusb_dev_desc->bDeviceProtocol = 1; + rh->pusb_dev_desc->bMaxPacketSize0 = 64; + } + rh->pusb_dev_desc->idVendor = 0; + rh->pusb_dev_desc->idProduct = 0; + rh->pusb_dev_desc->bcdDevice = 0x100; + rh->pusb_dev_desc->iManufacturer = 0; + rh->pusb_dev_desc->iProduct = 0; + rh->pusb_dev_desc->iSerialNumber = 0; + rh->pusb_dev_desc->bNumConfigurations = 1; + + pconfig_desc = ( PUSB_CONFIGURATION_DESC )&rh->desc_buf[ sizeof( USB_DEVICE_DESC ) ]; + pif_desc = ( PUSB_INTERFACE_DESC ) &pconfig_desc[ 1 ]; + pendp_desc = ( PUSB_ENDPOINT_DESC ) &pif_desc[ 1 ]; + + pconfig_desc->bLength = sizeof( USB_CONFIGURATION_DESC ); + pconfig_desc->bDescriptorType = USB_DT_CONFIG; + + pconfig_desc->wTotalLength = sizeof( USB_CONFIGURATION_DESC ) + + sizeof( USB_INTERFACE_DESC ) + + sizeof( USB_ENDPOINT_DESC ); + + pconfig_desc->bNumInterfaces = 1; + pconfig_desc->bConfigurationValue = 1; + pconfig_desc->iConfiguration = 0; + pconfig_desc->bmAttributes = 0Xe0; //self-powered and support remoke wakeup + pconfig_desc->MaxPower = 0; + + pif_desc->bLength = sizeof( USB_INTERFACE_DESC ); + pif_desc->bDescriptorType = USB_DT_INTERFACE; + pif_desc->bInterfaceNumber = 0; + pif_desc->bAlternateSetting = 0; + pif_desc->bNumEndpoints = 1; + pif_desc->bInterfaceClass = USB_CLASS_HUB; + pif_desc->bInterfaceSubClass = 0; + pif_desc->bInterfaceProtocol = 0; + pif_desc->iInterface = 0; + + pendp_desc->bLength = sizeof( USB_ENDPOINT_DESC ); + pendp_desc->bDescriptorType = USB_DT_ENDPOINT; + pendp_desc->bEndpointAddress = 0x81; + pendp_desc->bmAttributes = 0x03; + pendp_desc->wMaxPacketSize = 8; + pendp_desc->bInterval = USB_HUB_INTERVAL; + if( usb2( hcd ) ) + pendp_desc->bInterval = 0x0c; + + pconfig = rh->usb_config = ( PUSB_CONFIGURATION )&pendp_desc[ 1 ]; + rh->active_config_idx = 0; + pconfig->pusb_config_desc = pconfig_desc; + pconfig->if_count = 1; + pconfig->pusb_dev = rh; + pif = &pconfig->interf[ 0 ]; + + pif->endp_count = 1; + pendp = &pif->endp[ 0 ]; + pif->pusb_config = pconfig;; + pif->pusb_if_desc = pif_desc; + + pif->if_ext_size = 0; + pif->if_ext = NULL; + + phub_ext = ( PHUB2_EXTENSION )&pconfig[ 1 ]; + phub_ext->port_count = 2; + + if( usb2( hcd ) ) + { + // port count is configurable in usb2 + hcd->hcd_dispatch( hcd, HCD_DISP_READ_PORT_COUNT, &phub_ext->port_count ); + } + + { + int j; + for( j = 0; j < phub_ext->port_count; j++ ) + { + psq_init( &phub_ext->port_status_queue[ j ] ); + phub_ext->child_dev[ j ] = NULL; + usb_dbg_print( DBGLVL_MAXIMUM, ( "rh_driver_init(): port[ %d ].flag=0x%x\n", \ + j, phub_ext->port_status_queue[ j ].port_flags ) ); + } + } + + phub_ext->pif = pif; + phub_ext->hub_desc.bLength = sizeof( USB_HUB_DESCRIPTOR ); + phub_ext->hub_desc.bDescriptorType = USB_DT_HUB; + phub_ext->hub_desc.bNbrPorts = ( UCHAR )phub_ext->port_count; + phub_ext->hub_desc.wHubCharacteristics = 0; + phub_ext->hub_desc.bPwrOn2PwrGood = 0; + phub_ext->hub_desc.bHubContrCurrent = 50; + + rh->dev_ext = ( PBYTE )phub_ext; + rh->dev_ext_size = sizeof( HUB2_EXTENSION ); + + rh->default_endp.flags = USB_ENDP_FLAG_DEFAULT_ENDP; + InitializeListHead( &rh->default_endp.urb_list ); + rh->default_endp.pusb_if = ( PUSB_INTERFACE )rh; + rh->default_endp.pusb_endp_desc = NULL; //??? + rh->time_out_count = 0; + rh->error_count = 0; + + InitializeListHead( &pendp->urb_list ); + pendp->flags = 0; + pendp->pusb_endp_desc = pendp_desc; + pendp->pusb_if = pif; + + //add to device list + InsertTailList( &dev_mgr->dev_list, &rh->dev_link ); + hcd->hcd_set_root_hub( hcd, rh ); + status = hub_start_int_request( rh ); + pdriver->driver_ext = 0; + } + return TRUE; +} + +BOOL +rh_destroy( +PUSB_DEV pdev +) +//to be the reverse of what init does, we assume that the timer is now killed +//int is disconnected and the hub thread will not process event anymore +{ + PUSB_DEV rh; + PLIST_ENTRY pthis, pnext; + PUSB_DEV_MANAGER dev_mgr; + + if( pdev == NULL ) + return FALSE; + + dev_mgr = dev_mgr_from_dev( pdev ); + + //??? + rh = pdev->hcd->hcd_get_root_hub( pdev->hcd ); + if( rh == pdev ) + { + //free all the buf + dev_mgr_free_device(dev_mgr, rh ); + //dev_mgr->root_hub = NULL; + } + + return TRUE; +} + +VOID +rh_timer_svc_int_completion( +PUSB_DEV pdev, +PVOID context +) +{ + PUSB_EVENT pevent; + PURB purb; + ULONG status, i; + PHCD hcd; + USE_IRQL; + + if( pdev == NULL || context == NULL ) + return; + + purb = ( PURB )context; + + lock_dev( pdev, TRUE ); + + if( dev_state( pdev ) == USB_DEV_STATE_ZOMB ) + { + pdev->ref_count -= 2; // one for timer_svc and one for urb, for those rh requests + unlock_dev( pdev, TRUE ); + usb_free_mem( purb ); + usb_dbg_print( DBGLVL_MAXIMUM, ( "rh_timer_svc_int_completion(): the dev is zomb, 0x%x\n", pdev ) ); + return; + } + + hcd = pdev->hcd; + if( purb->data_length < 1 ) + { + purb->status = STATUS_INVALID_PARAMETER; + unlock_dev( pdev, TRUE ); + goto LBL_OUT; + } + + pdev->hcd->hcd_dispatch( pdev->hcd, HCD_DISP_READ_RH_DEV_CHANGE, purb->data_buffer ); + purb->status = STATUS_SUCCESS; + unlock_dev( pdev, TRUE ); + +LBL_OUT: + hcd->hcd_generic_urb_completion( purb, purb->context ); + + lock_dev( pdev, TRUE ); + pdev->ref_count -= 2; // one for timer_svc and one for urb, for those rh requests + // that completed immediately, the ref_count of the dev for + // that urb won't increment and for normal hub request + // completion, hcd_generic_urb_completion will be called + // by the xhci_dpc_callback, and the ref_count for the urb + // is maintained there. So only rh's timer-svc cares refcount + // when hcd_generic_urb_completion is called. + usb_dbg_print( DBGLVL_MAXIMUM, ( "rh_timer_svc_int_completion(): rh's ref_count=0x%x\n", pdev->ref_count ) ); + unlock_dev( pdev, TRUE ); + usb_dbg_print( DBGLVL_MAXIMUM, ( "rh_timer_svc_int_completion(): exitiing...\n" ) ); + return; +} + +VOID +rh_timer_svc_reset_port_completion( +PUSB_DEV pdev, +PVOID context +) +{ + PURB purb; + ULONG i; + USHORT port_num; + PHUB2_EXTENSION hub_ext; + PLIST_ENTRY pthis, pnext; + PUSB_DEV_MANAGER dev_mgr; + PUSB_CTRL_SETUP_PACKET psetup; + + USE_IRQL; + + if( pdev == NULL || context == NULL ) + return; + + dev_mgr = dev_mgr_from_dev( pdev ); //readonly and hold ref_count + + //block the rh polling + KeAcquireSpinLockAtDpcLevel( &dev_mgr->timer_svc_list_lock ); + if( IsListEmpty( &dev_mgr->timer_svc_list ) == FALSE ) + { + ListFirst( &dev_mgr->timer_svc_list, pthis ); + while( pthis ) + { + if( ( ( PTIMER_SVC )pthis )->pdev == pdev && \ + ( ( PTIMER_SVC )pthis )->threshold == RH_INTERVAL ) + { + ( ( PTIMER_SVC )pthis )->threshold = RH_INTERVAL + 0x800000; + break; + } + + ListNext( &dev_mgr->timer_svc_list, pthis, pnext ); + pthis = pnext; + } + } + KeReleaseSpinLockFromDpcLevel( &dev_mgr->timer_svc_list_lock ); + + purb = ( PURB )context; + psetup = ( PUSB_CTRL_SETUP_PACKET )purb->setup_packet; + + lock_dev( pdev, TRUE ); + if( dev_state( pdev ) == USB_DEV_STATE_ZOMB ) + { + //purb->status = STATUS_ERROR; + //pdev->hcd->hcd_generic_urb_completion( purb, purb->context ); + + pdev->ref_count -= 2; + unlock_dev( pdev, TRUE ); + usb_free_mem( purb ); + return; + } + + i = pdev->hcd->hcd_rh_reset_port( pdev->hcd, ( UCHAR )psetup->wIndex ); + + hub_ext = hub_ext_from_dev( pdev ); + + { + USHORT temp; + PUCHAR pbuf; + if( psetup->wIndex < 16 ) + { + temp = 1 << psetup->wIndex; + pbuf = ( PUCHAR )&temp; + if( temp > 128 ) + pbuf++; + hub_ext->int_data_buf[ psetup->wIndex / 8 ] |= *pbuf; + if( i == TRUE ) + hub_ext->rh_port_status[ psetup->wIndex ].wPortChange |= USB_PORT_STAT_C_RESET; + else // notify that is not a high speed device, will lost definitely + hub_ext->rh_port_status[ psetup->wIndex ].wPortChange |= USB_PORT_STAT_C_CONNECTION; + } + } + + //???how to construct port status map + // decrease the timer_svc ref-count + pdev->ref_count --; + unlock_dev( pdev, TRUE ); + + purb->status = STATUS_SUCCESS; + //we delegate the completion to the rh_timer_svc_int_completion. + //this function is equivalent to hub_start_reset_port_completion + + usb_free_mem( purb ); + + //expire the rh polling timer + KeAcquireSpinLockAtDpcLevel( &dev_mgr->timer_svc_list_lock ); + if( IsListEmpty( &dev_mgr->timer_svc_list ) == FALSE ) + { + ListFirst( &dev_mgr->timer_svc_list, pthis ); + while( pthis ) + { + if( ( ( PTIMER_SVC )pthis )->pdev == pdev && \ + ( ( PTIMER_SVC )pthis )->threshold == RH_INTERVAL + 0x800000 ) + { + ( ( PTIMER_SVC )pthis )->counter = RH_INTERVAL; + ( ( PTIMER_SVC )pthis )->threshold = RH_INTERVAL; + break; + } + + ListNext( &dev_mgr->timer_svc_list, pthis, pnext ); + pthis = pnext; + } + } + KeReleaseSpinLockFromDpcLevel( &dev_mgr->timer_svc_list_lock ); + + lock_dev( pdev, TRUE ); + pdev->ref_count--; + unlock_dev( pdev, TRUE ); + return; +} + +VOID +dev_mgr_disconnect_dev( +PUSB_DEV pdev +) +//called when a disconnect is detected on the port +{ + PLIST_ENTRY pthis, pnext; + PHUB2_EXTENSION phub_ext; + PUSB_CONFIGURATION pconfig; + PUSB_INTERFACE pif; + PUSB_DEV_MANAGER dev_mgr; + PHCD hcd; + BOOL is_hub, found; + ULONG dev_id; + + int i; + USE_IRQL; + + if( pdev == NULL ) + return; + + found = FALSE; + + usb_dbg_print( DBGLVL_MAXIMUM, ( "dev_mgr_disconnect_dev(): entering, pdev=0x%x\n", pdev ) ); + lock_dev( pdev, FALSE ); + pdev->flags &= ~ USB_DEV_STATE_MASK; + pdev->flags |= USB_DEV_STATE_BEFORE_ZOMB; + dev_mgr = dev_mgr_from_dev( pdev ); + unlock_dev( pdev, FALSE ); + + // notify dev_driver that the dev stops function before any operations + if( pdev->dev_driver && pdev->dev_driver->disp_tbl.dev_stop ) + pdev->dev_driver->disp_tbl.dev_stop( dev_mgr, dev_handle_from_dev( pdev ) ); + + //safe to use the dev pointer in this function. + lock_dev( pdev, FALSE ); + pdev->flags &= ~ USB_DEV_STATE_MASK; + pdev->flags |= USB_DEV_STATE_ZOMB; + hcd = pdev->hcd; + dev_id = pdev->dev_id; + unlock_dev( pdev, FALSE ); + + if( dev_mgr == NULL ) + return; + + hcd->hcd_remove_device( hcd, pdev ); + + //disconnect its children + if( ( pdev->flags & USB_DEV_CLASS_MASK ) == USB_DEV_CLASS_HUB || \ + ( pdev->flags & USB_DEV_CLASS_MASK ) == USB_DEV_CLASS_ROOT_HUB ) + { + phub_ext = hub_ext_from_dev( pdev ); + if( phub_ext ) + { + for( i = 1; i <= phub_ext->port_count; i++ ) + { + if( phub_ext->child_dev[ i ] ) + { + dev_mgr_disconnect_dev( phub_ext->child_dev[ i ] ); + phub_ext->child_dev[ i ] = NULL; + } + } + } + } + + pconfig = pdev->usb_config; + + //remove event belong to the dev + is_hub = ( ( pdev->flags & USB_DEV_CLASS_MASK ) == USB_DEV_CLASS_HUB ); + + if( phub_ext && is_hub ) + { + for( i = 1; i <= phub_ext->port_count; i++ ) + { + found = hub_remove_reset_event( pdev, i, FALSE ); + if( found ) + break; + } + } + + //free event of the dev from the event list + KeAcquireSpinLock( &dev_mgr->event_list_lock, &old_irql ); + ListFirst( &dev_mgr->event_list, pthis ); + while( pthis ) + { + ListNext( &dev_mgr->event_list, pthis, pnext ); + if( ( ( PUSB_EVENT )pthis )->pdev == pdev ) + { + PLIST_ENTRY p1; + RemoveEntryList( pthis ); + if( ( ( ( PUSB_EVENT )pthis )->flags & USB_EVENT_FLAG_QUE_TYPE ) + != USB_EVENT_FLAG_NOQUE ) + { + //has a queue, re-insert the queue + if( p1 = ( PLIST_ENTRY )( ( PUSB_EVENT )pthis )->pnext ) + { + InsertHeadList( &dev_mgr->event_list, p1 ); + free_event( &dev_mgr->event_pool, struct_ptr( pthis, USB_EVENT, event_link ) ); + pthis = p1; + //note: this queue will be examined again in the next loop + //to find the matched dev in the queue + continue; + } + } + free_event( &dev_mgr->event_pool, struct_ptr( pthis, USB_EVENT, event_link ) ); + } + else if( ( ( ( ( PUSB_EVENT )pthis )->flags & USB_EVENT_FLAG_QUE_TYPE ) + != USB_EVENT_FLAG_NOQUE ) && ( ( PUSB_EVENT )pthis )->pnext ) + { + //has a queue, examine the queue + PUSB_EVENT p1, p2; + p1 = ( PUSB_EVENT )pthis; + p2 = p1->pnext; + while( p2 ) + { + if( p2->pdev == pdev ) + { + p1->pnext = p2->pnext; + p2->pnext = NULL; + free_event( &dev_mgr->event_pool, p2 ); + p2 = p1->pnext; + } + else + { + p1 = p2; + p2 = p2->pnext; + } + } + } + pthis = pnext; + } + KeReleaseSpinLock( &dev_mgr->event_list_lock, old_irql ); + + // found indicates the reset event on one of the dev's port in process + if( found ) + hub_start_next_reset_port( dev_mgr_from_dev( pdev ), FALSE ); + + // remove timer-svc belonging to the dev + KeAcquireSpinLock( &dev_mgr->timer_svc_list_lock, &old_irql ); + ListFirst( &dev_mgr->timer_svc_list, pthis ); + i = 0; + while( pthis ) + { + ListNext( &dev_mgr->timer_svc_list, pthis, pnext ); + if( ( ( PUSB_EVENT )pthis )->pdev == pdev ) + { + RemoveEntryList( pthis ); + free_timer_svc( &dev_mgr->timer_svc_pool, struct_ptr( pthis, TIMER_SVC, timer_svc_link ) ); + i++; + } + pthis = pnext; + } + KeReleaseSpinLock( &dev_mgr->timer_svc_list_lock, old_irql ); + + // release the refcount + if( i ) + { + lock_dev( pdev, FALSE ); + pdev->ref_count -= i; + unlock_dev( pdev, FALSE ); + } + + // wait for all the reference count be released + for( ; ; ) + { + LARGE_INTEGER interval; + + lock_dev( pdev, FALSE ); + if( pdev->ref_count == 0 ) + { + unlock_dev( pdev, FALSE ); + break; + } + unlock_dev( pdev, FALSE ); + // Wait two ms. + interval.QuadPart = -20000; + KeDelayExecutionThread( KernelMode, FALSE, &interval ); + } + + if( pdev->dev_driver && pdev->dev_driver->disp_tbl.dev_disconnect ) + pdev->dev_driver->disp_tbl.dev_disconnect( dev_mgr, dev_handle_from_dev( pdev ) ); + + // we put it here to let handle valid before disconnect + KeAcquireSpinLock( &dev_mgr->dev_list_lock, &old_irql ); + ListFirst( &dev_mgr->dev_list, pthis ); + while( pthis ) + { + if( ( ( PUSB_DEV )pthis ) == pdev ) + { + RemoveEntryList( pthis ); + break; + } + ListNext( &dev_mgr->dev_list, pthis, pnext ); + pthis = pnext; + } + KeReleaseSpinLock( &dev_mgr->dev_list_lock, old_irql ); + + + if( pdev != pdev->hcd->hcd_get_root_hub( pdev->hcd ) ) + { + dev_mgr_free_device( dev_mgr, pdev ); + } + else + { + //rh_destroy( pdev ); + //TRAP(); + //destroy it in dev_mgr_destroy + } + + return; +} + +BOOL +hub_driver_init( +PUSB_DEV_MANAGER dev_mgr, +PUSB_DRIVER pdriver +) +{ + //init driver structure, no PNP table functions + pdriver->driver_desc.flags = USB_DRIVER_FLAG_DEV_CAPABLE; + pdriver->driver_desc.vendor_id = 0xffff; // USB Vendor ID + pdriver->driver_desc.product_id = 0xffff; // USB Product ID. + pdriver->driver_desc.release_num = 0xffff; // Release Number of Device + + pdriver->driver_desc.config_val = 0; // Configuration Value + pdriver->driver_desc.if_num = 0; // Interface Number + pdriver->driver_desc.if_class = USB_CLASS_HUB; // Interface Class + pdriver->driver_desc.if_sub_class = 0; // Interface SubClass + pdriver->driver_desc.if_protocol = 0; // Interface Protocol + + pdriver->driver_desc.driver_name = "USB hub"; // Driver name for Name Registry + pdriver->driver_desc.dev_class = USB_CLASS_HUB; + pdriver->driver_desc.dev_sub_class = 0; // Device Subclass + pdriver->driver_desc.dev_protocol = 0; // Protocol Info. + + //pdriver->driver_init = hub_driver_init; // initialized in dev_mgr_init_driver + //pdriver->driver_destroy = hub_driver_destroy; + + pdriver->driver_ext = 0; + pdriver->driver_ext_size = 0; + + pdriver->disp_tbl.version = 1; + pdriver->disp_tbl.dev_connect = hub_connect; + pdriver->disp_tbl.dev_disconnect = hub_disconnect; + pdriver->disp_tbl.dev_stop = hub_stop; + pdriver->disp_tbl.dev_reserved = NULL; + + return TRUE; +} + +BOOL +hub_driver_destroy( +PUSB_DEV_MANAGER dev_mgr, +PUSB_DRIVER pdriver +) +{ + pdriver->driver_ext = NULL; + return TRUE; +} +void + +hub_reset_pipe_completion( +PURB purb, //only for reference, can not be released +PVOID context +) +{ + PUSB_DEV pdev; + PUSB_ENDPOINT pendp; + NTSTATUS status; + + USE_IRQL; + + if( purb == NULL ) + { + return; + } + + pdev = purb->pdev; + pendp = purb->pendp; + + lock_dev( pdev, TRUE ); + if( dev_state( pdev ) == USB_DEV_STATE_ZOMB ) + { + unlock_dev( pdev, TRUE ); + return; + } + + if( usb_error( purb->status ) ) + { + //simply retry it + unlock_dev( pdev, TRUE ); + //usb_free_mem( purb ); + return; + } + unlock_dev( pdev, TRUE ); + + pdev = purb->pdev; + hub_start_int_request( pdev ); + return; +} + +NTSTATUS +hub_start_int_request( +PUSB_DEV pdev +) +{ + PURB purb; + PUSB_INTERFACE pif; + PHUB2_EXTENSION hub_ext; + NTSTATUS status; + PHCD hcd; + USE_IRQL; + + if( pdev == NULL ) + return STATUS_INVALID_PARAMETER; + + lock_dev( pdev, FALSE ); + if( dev_state( pdev ) == USB_DEV_STATE_ZOMB ) + { + unlock_dev( pdev, FALSE ); + return STATUS_DEVICE_DOES_NOT_EXIST; + } + purb = usb_alloc_mem( NonPagedPool, sizeof( URB ) ); + RtlZeroMemory( purb, sizeof( URB ) ); + + if( purb == NULL ) + { + unlock_dev( pdev, FALSE ); + return STATUS_NO_MEMORY; + } + + purb->flags = 0; + purb->status = STATUS_SUCCESS; + hub_ext = hub_ext_from_dev( pdev ); + purb->data_buffer = hub_ext->int_data_buf; + purb->data_length = ( hub_ext->port_count + 7 ) / 8; + + hub_if_from_dev( pdev, pif ); + usb_dbg_print( DBGLVL_MAXIMUM, ( "hub_start_int_request(): pdev=0x%x, pif=0x%x\n", pdev, pif ) ); + purb->pendp = &pif->endp[ 0 ]; + purb->pdev = pdev; + + purb->completion = hub_int_completion; + purb->context = hub_ext; + + purb->pirp = NULL; + purb->reference = 0; + hcd = pdev->hcd; + unlock_dev( pdev, FALSE ); + + status = hcd->hcd_submit_urb( hcd, pdev, purb->pendp, purb ); + if( status != STATUS_PENDING ) + { + usb_free_mem( purb ); + purb = NULL; + } + + return status; +} + +void +hub_int_completion( +PURB purb, +PVOID pcontext +) +{ + + PUSB_DEV pdev; + PHUB2_EXTENSION hub_ext; + ULONG port_idx; + PUSB_CTRL_SETUP_PACKET psetup; + NTSTATUS status; + LONG i; + PHCD hcd; + + USE_IRQL; + + if( purb == NULL ) + return; + + if( pcontext == NULL ) + { + usb_free_mem( purb ); + return; + } + + usb_dbg_print( DBGLVL_MAXIMUM, ("hub_int_completion(): entering...\n" ) ); + + pdev = purb->pdev; + hub_ext = pcontext; + + lock_dev( pdev, TRUE ); + if( dev_state( pdev ) == USB_DEV_STATE_ZOMB ) + { + unlock_dev( pdev, TRUE ); + usb_free_mem( purb ); + return; + } + + hcd = pdev->hcd; + + if( purb->status == STATUS_SUCCESS ) + { + + for( i = 1; i <= hub_ext->port_count; i++ ) + { + if( hub_ext->int_data_buf[ i >> 3 ] & ( 1 << i ) ) + { + break; + } + } + if( i > hub_ext->port_count ) + { + //no status change, re-initialize the int request + unlock_dev( pdev, TRUE ); + usb_free_mem( purb ); + hub_start_int_request( pdev ); + return; + } + + port_idx = ( ULONG )i; + + //re-use the urb to get port status + purb->pendp = &pdev->default_endp; + purb->data_buffer = ( PUCHAR )&hub_ext->port_status; + + purb->data_length = sizeof( USB_PORT_STATUS ); + purb->pdev = pdev; + + purb->context = hub_ext; + purb->pdev = pdev; + purb->completion = hub_get_port_status_completion; + purb->reference = port_idx; + + psetup = ( PUSB_CTRL_SETUP_PACKET )purb->setup_packet; + + psetup->bmRequestType = 0xa3; //host-device class other recepient + psetup->bRequest = USB_REQ_GET_STATUS; + psetup->wValue = 0; + psetup->wIndex = ( USHORT )port_idx; + psetup->wLength = 4; + + purb->pirp = NULL; + unlock_dev( pdev, TRUE ); + + status = hcd->hcd_submit_urb( hcd, pdev, purb->pendp, purb ); + if( usb_error( status ) ) + { + usb_free_mem( purb ); + purb = NULL; + } + else if( status == STATUS_SUCCESS ) + { + // this is for root hub + hcd->hcd_generic_urb_completion( purb, purb->context ); + } + return; + } + else + { + unlock_dev( pdev, TRUE ); + if( usb_halted( purb->status ) ) + { + //let's reset pipe + usb_reset_pipe( pdev, purb->pendp, hub_reset_pipe_completion, NULL ); + } + //unexpected error + usb_free_mem( purb ); + purb = NULL; + } + return; +} + +VOID +hub_get_port_status_completion( +PURB purb, +PVOID context +) +{ + PUSB_DEV pdev; + PUSB_ENDPOINT pendp; + BYTE port_idx; + PHUB2_EXTENSION hub_ext; + PUSB_CTRL_SETUP_PACKET psetup; + NTSTATUS status; + PHCD hcd; + + USE_IRQL; + + if( purb == NULL || context == NULL ) + return; + + usb_dbg_print( DBGLVL_MAXIMUM, ("hub_get_port_feature_completion(): entering...\n" ) ); + + pdev = purb->pdev; + pendp = purb->pendp; + + lock_dev( pdev, TRUE ); + if( dev_state( pdev ) == USB_DEV_STATE_ZOMB ) + { + unlock_dev( pdev, TRUE ); + usb_free_mem( purb ); + return; + } + + hcd = pdev->hcd; + if( usb_error( purb->status ) ) + { + unlock_dev( pdev, TRUE ); + + purb->status = 0; + //simply retry the request refer to item 55 in document + status = hcd->hcd_submit_urb( hcd, pdev, pendp, purb ); + if( status != STATUS_PENDING ) + { + if( status == STATUS_SUCCESS ) + { + hcd->hcd_generic_urb_completion( purb, purb->context ); + + } + else + { + // + // must be fatal error + // FIXME: better to pass it to the completion for further + // processing? + // + usb_free_mem( purb ); + } + } + return; + } + + hub_ext = hub_ext_from_dev( pdev ); + port_idx = ( BYTE )purb->reference; + + usb_dbg_print( DBGLVL_MAXIMUM, ("hub_get_port_stataus_completion(): port_idx=0x%x, hcd =0x%x, \ + pdev=0x%x, purb=0x%x, hub_ext=0x%x, portsc=0x%x \n", \ + port_idx, \ + pdev->hcd, \ + pdev, \ + purb, \ + hub_ext,\ + *( ( PULONG ) purb->data_buffer ) ) ); + + psq_enqueue( &hub_ext->port_status_queue[ port_idx ], + *( ( PULONG )purb->data_buffer ) ); + + //reuse the urb to clear the feature + RtlZeroMemory( purb, sizeof( URB ) ); + + purb->data_buffer = NULL; + purb->data_length = 0; + purb->pendp = &pdev->default_endp; + purb->pdev = pdev; + + purb->context = ( PVOID )&hub_ext->port_status ; + purb->pdev = pdev; + purb->completion = hub_clear_port_feature_completion; + purb->reference = port_idx; + + psetup = ( PUSB_CTRL_SETUP_PACKET )purb->setup_packet; + + psetup->bmRequestType = 0x23; //host-device class port recepient + psetup->bRequest = USB_REQ_CLEAR_FEATURE; + psetup->wIndex = port_idx; + psetup->wLength = 0; + purb->pirp = NULL; + + if( hub_ext->port_status.wPortChange & USB_PORT_STAT_C_CONNECTION ) + { + psetup->wValue = USB_PORT_FEAT_C_CONNECTION; + } + else if( hub_ext->port_status.wPortChange & USB_PORT_STAT_C_ENABLE ) + { + psetup->wValue = USB_PORT_FEAT_C_ENABLE; + } + else if( hub_ext->port_status.wPortChange & USB_PORT_STAT_C_SUSPEND ) + { + psetup->wValue = USB_PORT_FEAT_C_SUSPEND; + } + else if( hub_ext->port_status.wPortChange & USB_PORT_STAT_C_OVERCURRENT ) + { + psetup->wValue = USB_PORT_FEAT_C_OVER_CURRENT; + } + else if( hub_ext->port_status.wPortChange & USB_PORT_STAT_C_RESET ) + { + psetup->wValue = USB_PORT_FEAT_C_RESET; + } + unlock_dev( pdev, TRUE ); + + status = hcd->hcd_submit_urb( hcd, pdev, pendp, purb ); + + // if( status != STATUS_SUCCESS ) + if( status != STATUS_PENDING ) + { + hcd->hcd_generic_urb_completion( purb, purb->context ); + } + /*else if( usb_error( status ) ) + { + usb_free_mem( purb ); + return; + }*/ + return; + +} + +VOID +hub_clear_port_feature_completion( +PURB purb, +PVOID context +) +{ + BYTE port_idx; + LONG i; + BOOL bReset, event_post, brh; + ULONG pc; + PHCD hcd; + NTSTATUS status; + PUSB_DEV pdev, pdev2; + PUSB_EVENT pevent; + PUSB_ENDPOINT pendp; + PUSB_INTERFACE pif; + PHUB2_EXTENSION hub_ext; + PUSB_DEV_MANAGER dev_mgr; + + PUSB_CTRL_SETUP_PACKET psetup; + + USE_IRQL; + + if( purb == NULL ) + return; + + if( context == NULL ) + { + usb_free_mem( purb ); + return; + } + + usb_dbg_print( DBGLVL_MAXIMUM, ("hub_clear_port_feature_completion(): entering...\n" ) ); + + pdev = purb->pdev; + port_idx = ( BYTE )purb->reference; + + lock_dev( pdev, TRUE ); + dev_mgr = dev_mgr_from_dev( pdev ); + hcd = pdev->hcd; + brh = ( dev_class( pdev ) == USB_DEV_CLASS_ROOT_HUB ); + + if( usb_error( purb->status ) ) + { + unlock_dev( pdev, TRUE ); + + purb->status = 0; + + // retry the request + status = hcd->hcd_submit_urb( hcd, purb->pdev, purb->pendp, purb ); + if( status != STATUS_PENDING ) + { + if( status == STATUS_SUCCESS ) + { + hcd->hcd_generic_urb_completion( purb, purb->context ); + } + else + { + // + // FIXME: should we pass the error to the completion directly + // instead of forstall it here? + // + // do not think the device is workable, no requests to it any more. + // including the int polling + // + // usb_free_mem( purb ); + // + goto LBL_SCAN_PORT_STAT; + } + } + return; + } + + if( dev_state( pdev ) == USB_DEV_STATE_ZOMB ) + { + unlock_dev( pdev, TRUE ); + usb_free_mem( purb ); + return; + } + + pc = ( ( PUSB_PORT_STATUS ) context )->wPortChange; + + if( pc ) + { + // the bits are tested in ascending order + if( pc & USB_PORT_STAT_C_CONNECTION ) + { + pc &= ~USB_PORT_STAT_C_CONNECTION; + } + else if( pc & USB_PORT_STAT_C_ENABLE ) + { + pc &= ~USB_PORT_STAT_C_ENABLE; + } + else if( pc & USB_PORT_STAT_C_SUSPEND ) + { + pc &= ~USB_PORT_STAT_C_SUSPEND; + } + else if( pc & USB_PORT_STAT_C_OVERCURRENT ) + { + pc &= ~USB_PORT_STAT_C_OVERCURRENT; + } + else if( pc & USB_PORT_STAT_C_RESET ) + { + pc &= ~USB_PORT_STAT_C_RESET; + } + } + ( ( PUSB_PORT_STATUS ) context )->wPortChange = ( USHORT )pc; + + hub_ext = hub_ext_from_dev( pdev ); + + if( pc ) + { + //some other status change on the port still active + psetup = ( PUSB_CTRL_SETUP_PACKET )purb->setup_packet; + + if( hub_ext->port_status.wPortChange & USB_PORT_STAT_C_CONNECTION ) + { + psetup->wValue = USB_PORT_FEAT_C_CONNECTION; + } + else if( hub_ext->port_status.wPortChange & USB_PORT_STAT_C_ENABLE ) + { + psetup->wValue = USB_PORT_FEAT_C_ENABLE; + } + else if( hub_ext->port_status.wPortChange & USB_PORT_STAT_C_SUSPEND ) + { + psetup->wValue = USB_PORT_FEAT_C_SUSPEND; + } + else if( hub_ext->port_status.wPortChange & USB_PORT_STAT_C_OVERCURRENT ) + { + psetup->wValue = USB_PORT_FEAT_C_OVER_CURRENT; + } + else if( hub_ext->port_status.wPortChange & USB_PORT_STAT_C_RESET ) + { + psetup->wValue = USB_PORT_FEAT_C_RESET; + } + unlock_dev( pdev, TRUE ); + + status = hcd->hcd_submit_urb( hcd, pdev, purb->pendp, purb ); + if( status != STATUS_PENDING ) + { + if( status == STATUS_SUCCESS ) + { + usb_dbg_print( DBGLVL_MAXIMUM, ("hub_clear_port_stataus_completion(): port_idx=0x%x, hcd=0x%x, \ + pdev=0x%x, purb=0x%x, hub_ext=0x%x, wPortChange=0x%x \n", \ + port_idx, \ + pdev->hcd, \ + pdev, \ + purb, \ + hub_ext,\ + pc ) ); + + hcd->hcd_generic_urb_completion( purb, purb->context ); + } + else + { + usb_dbg_print( DBGLVL_MAXIMUM, (" hub_clear_port_feature_completion(): \ + error=0x%x\n", status ) ); + + // usb_free_mem( purb ); + goto LBL_SCAN_PORT_STAT; + } + } + return; + } + + for( i = 1; i <= hub_ext->port_count; i++ ) + { + if( hub_ext->int_data_buf[ i >> 3 ] & ( 1 << i ) ) + { + break; + } + } + + //clear the port-change map, we have get port i's status. + hub_ext->int_data_buf[ i >> 3 ] &= ~( 1 << i ); + + //rescan to find some other port that has status change + for( i = 1; i <= hub_ext->port_count; i++ ) + { + if( hub_ext->int_data_buf[ i >> 3 ] & ( 1 << i ) ) + { + break; + } + } + + if( i <= hub_ext->port_count ) + { + //still has port-change pending, get the port status change + port_idx = ( UCHAR )i; + + //re-use the urb + purb->data_buffer = ( PUCHAR )&hub_ext->port_status; + purb->data_length = sizeof( USB_PORT_STATUS ); + purb->pendp = &pdev->default_endp; + purb->pdev = pdev; + + purb->context = hub_ext; + purb->pdev = pdev; + purb->completion = hub_get_port_status_completion; + purb->reference = port_idx; + + psetup = ( PUSB_CTRL_SETUP_PACKET )purb->setup_packet; + + psetup->bmRequestType = 0xa3; //host-device class other recepient + psetup->bRequest = USB_REQ_GET_STATUS; + psetup->wValue = 0; + psetup->wIndex = port_idx; + psetup->wLength = 4; + + purb->pirp = NULL; + + unlock_dev( pdev, TRUE ); + + status = hcd->hcd_submit_urb( hcd, pdev, purb->pendp, purb ); + if( status != STATUS_PENDING ) + { + if( status == STATUS_SUCCESS ) + { + hcd->hcd_generic_urb_completion( purb, purb->context ); + } + else + { //must be fatal error + // usb_free_mem( purb ); + goto LBL_SCAN_PORT_STAT; + } + } + return; + } + + unlock_dev( pdev, TRUE ); + +LBL_SCAN_PORT_STAT: + + //all status changes are cleared + if( purb ) + usb_free_mem( purb ); + + purb = NULL; + + KeAcquireSpinLockAtDpcLevel( &dev_mgr->event_list_lock ); + lock_dev( pdev, TRUE ); + + if( dev_state( pdev ) == USB_DEV_STATE_ZOMB ) + { + // + // if reset is in process, the dev_mgr_disconnect_dev will continue + // the following resets + // + unlock_dev( pdev, TRUE ); + KeReleaseSpinLockFromDpcLevel( &dev_mgr->event_list_lock ); + return; + } + + //at last we wake up thread if some port have status change to process + port_idx = 0; + for( i = 1, event_post = FALSE; i <= hub_ext->port_count; i++ ) + { + if( psq_is_empty( &hub_ext->port_status_queue[ i ]) == FALSE ) + { + if( port_state( hub_ext->port_status_queue[ i ].port_flags ) == STATE_IDLE || + port_state( hub_ext->port_status_queue[ i ].port_flags ) == STATE_WAIT_ADDRESSED ) + { + // have status in the queue pending + // STATE_WAIT_ADDRESSED is added to avoid some bad mannered + // hub to disturb the reset process + hub_post_esq_event( pdev, ( BYTE )i, hub_event_examine_status_que ); + } + else if( port_state( hub_ext->port_status_queue[ i ].port_flags ) == STATE_WAIT_RESET_COMPLETE ) + { + //there is only one reset at one time + port_idx = ( BYTE )i; + } + } + } + + unlock_dev( pdev, TRUE ); + KeReleaseSpinLockFromDpcLevel( &dev_mgr->event_list_lock ); + + + if( port_idx ) + hub_check_reset_port_status( + pdev, + port_idx ); + + //reinitialize the int request, here to reduce some uncertainty of concurrency + hub_start_int_request( pdev ); + + return; +} + +VOID +hub_event_examine_status_que( +PUSB_DEV pdev, +ULONG event, +ULONG context, //hub_ext +ULONG param //port_idx +) +{ + PHUB2_EXTENSION hub_ext; + USB_PORT_STATUS ps; + PUSB_DEV pchild_dev; + PTIMER_SVC ptimer; + PUSB_DEV_MANAGER dev_mgr; + + USE_IRQL; + + if( pdev == NULL || context == 0 || param == 0 ) + return; + + while( TRUE ) + { + lock_dev( pdev, FALSE ); + if( dev_state( pdev ) == USB_DEV_STATE_ZOMB ) + { + unlock_dev( pdev, FALSE ); + break; + } + + dev_mgr = dev_mgr_from_dev( pdev ); + hub_ext = hub_ext_from_dev( pdev ); + + if( psq_is_empty( &hub_ext->port_status_queue[ param ] ) ) + { + set_port_state( hub_ext->port_status_queue[ param ].port_flags, + STATE_IDLE ); + unlock_dev( pdev, FALSE ); + break; + } + + *( ( ULONG* )&ps ) = psq_outqueue( &hub_ext->port_status_queue[ param ] ); + + + pchild_dev = hub_ext->child_dev[ param ]; + hub_ext->child_dev[ param ] = 0; + + usb_dbg_print( DBGLVL_MAXIMUM, ( "hub_event_examine_status_queue(): dev_addr=0x%x, port=0x%x, wPortChange=0x%x, wPortStatus=0x%x\n", \ + pdev->dev_addr, \ + param, + ps.wPortChange, \ + ps.wPortStatus ) ); + + unlock_dev( pdev, FALSE ); + + if( pchild_dev != NULL ) + dev_mgr_disconnect_dev( pchild_dev ); + + if( ( ( ps.wPortChange & USB_PORT_STAT_C_ENABLE ) && + ( ( pdev->flags & USB_DEV_CLASS_MASK ) != USB_DEV_CLASS_ROOT_HUB ) ) + || ( ps.wPortChange & USB_PORT_STAT_C_OVERCURRENT ) + || ( ps.wPortChange & USB_PORT_STAT_C_RESET ) + || ( ( ps.wPortChange & USB_PORT_STAT_C_CONNECTION ) && + !( ps.wPortStatus & USB_PORT_STAT_CONNECTION ) ) ) + { + usb_dbg_print( DBGLVL_MAXIMUM, ("hub_event_examine_status_queue(): error occured, portc=0x%x, ports=0x%x\n", \ + ps.wPortChange,\ + ps.wPortStatus ) ); + + lock_dev( pdev, FALSE ); + if( dev_state( pdev ) == USB_DEV_STATE_ZOMB ) + { + unlock_dev( pdev, FALSE ); + break; + } + if( psq_is_empty( &hub_ext->port_status_queue[ param ] ) ) + { + set_port_state( hub_ext->port_status_queue[ param ].port_flags, + STATE_IDLE ); + } + else + { + set_port_state( hub_ext->port_status_queue[ param ].port_flags, + STATE_EXAMINE_STATUS_QUE ); + } + unlock_dev( pdev, FALSE ); + continue; + + } + else if( ( ps.wPortChange & USB_PORT_STAT_C_CONNECTION ) + && ( ps.wPortStatus & USB_PORT_STAT_CONNECTION ) + && psq_is_empty( &hub_ext->port_status_queue[ param ] ) ) + { + KeAcquireSpinLock( &dev_mgr->timer_svc_list_lock, &old_irql ); + lock_dev( pdev, TRUE ); + if( dev_state( pdev ) == USB_DEV_STATE_ZOMB ) + { + unlock_dev( pdev, TRUE ); + KeReleaseSpinLock( &dev_mgr->timer_svc_list_lock, old_irql ); + usb_dbg_print( DBGLVL_MAXIMUM, ("hub_event_examine_status_queue(): dev lost\n" ) ); + break; + } + ptimer = alloc_timer_svc( &dev_mgr->timer_svc_pool, 1 ); + if( ptimer == NULL ) + { + unlock_dev( pdev, TRUE ); + KeReleaseSpinLock( &dev_mgr->timer_svc_list_lock, old_irql ); + usb_dbg_print( DBGLVL_MAXIMUM, ("hub_event_examine_status_queue(): timer can not allocated\n" ) ); + break; + } + + //a new connection + usb_dbg_print( DBGLVL_MAXIMUM, ("hub_event_examine_status_queue(): new connection comes\n" ) ); + + ptimer->counter = 0; + ptimer->threshold = 21; //100 ms + + if( ps.wPortStatus & USB_PORT_STAT_LOW_SPEED ) + ptimer->threshold = 51; //500 ms + + ptimer->context = param; + ptimer->pdev = pdev; + ptimer->func = hub_timer_wait_dev_stable; + InsertTailList( &dev_mgr->timer_svc_list, &ptimer->timer_svc_link ); + pdev->ref_count ++; + set_port_state( hub_ext->port_status_queue[ param ].port_flags, + STATE_WAIT_STABLE ); + unlock_dev( pdev, TRUE ); + KeReleaseSpinLock( &dev_mgr->timer_svc_list_lock, old_irql ); + break; + + } + else + { + usb_dbg_print( DBGLVL_MAXIMUM, ("hub_event_examine_status_queue(): unknown error\n" ) ); + continue; + } + } + return; +} + +VOID +hub_timer_wait_dev_stable( +PUSB_DEV pdev, +PVOID context //port-index +) +{ + + PHUB2_EXTENSION hub_ext; + PUSB_INTERFACE pif; + PUSB_EVENT pevent; + ULONG param; + PUSB_DEV_MANAGER dev_mgr; + + USE_IRQL; + + if( pdev == NULL || context == 0 ) + return; + + dev_mgr = dev_mgr_from_dev( pdev ); + param = ( ULONG ) context; + KeAcquireSpinLockAtDpcLevel( &dev_mgr->event_list_lock ); + lock_dev( pdev, TRUE ); + + pdev->ref_count--; + + if( dev_state( pdev ) == USB_DEV_STATE_ZOMB ) + { + goto LBL_OUT; + } + + hub_ext = hub_ext_from_dev( pdev ); + + if( !psq_is_empty( &hub_ext->port_status_queue[ param ] ) ) + { + //error occured, normally we should not receive event here + set_port_state( hub_ext->port_status_queue[ param ].port_flags, + STATE_EXAMINE_STATUS_QUE ); + + hub_post_esq_event( pdev, ( BYTE )param, hub_event_examine_status_que ); + } + else + { + set_port_state( hub_ext->port_status_queue[ param ].port_flags, + STATE_WAIT_RESET ); + + hub_post_esq_event( pdev, ( BYTE )param, hub_event_dev_stable ); + + } + + LBL_OUT: + unlock_dev( pdev, TRUE ); + KeReleaseSpinLockFromDpcLevel( &dev_mgr->event_list_lock ); + return; +} + +VOID +hub_event_dev_stable( +PUSB_DEV pdev, +ULONG event, +ULONG context, //hub_ext +ULONG param //port_idx +) +{ + + PHUB2_EXTENSION hub_ext; + PUSB_EVENT pevent, pevent1; + PLIST_ENTRY pthis, pnext; + BOOL que_exist; + PHCD hcd; + PUSB_DEV_MANAGER dev_mgr; + NTSTATUS status; + PURB purb; + PUSB_CTRL_SETUP_PACKET psetup; + + USE_IRQL; + + if( pdev == NULL || context == 0 || param == 0 ) + return; + + dev_mgr = dev_mgr_from_dev( pdev ); + KeAcquireSpinLock( &dev_mgr->event_list_lock, &old_irql ); + lock_dev( pdev, TRUE ); + + if( dev_state( pdev ) == USB_DEV_STATE_ZOMB ) + goto LBL_OUT; + + hub_ext = hub_ext_from_dev( pdev ); + hcd = pdev->hcd; + + pevent = alloc_event( &dev_mgr->event_pool, 1 ); + if( pevent == NULL ) + goto LBL_OUT; + + pevent->event = USB_EVENT_WAIT_RESET_PORT; + pevent->pdev = pdev; + pevent->context = ( ULONG )hub_ext; + pevent->param = param; + pevent->flags = USB_EVENT_FLAG_QUE_RESET; + pevent->process_event = NULL; //hub_event_reset_port_complete; + pevent->process_queue = NULL; //hub_event_reset_process_queue; + pevent->pnext = NULL; + + ListFirst( &dev_mgr->event_list, pthis ); + que_exist = FALSE; + + while( pthis ) + { + //insert the event in to the wait-queue + pevent1 = ( PUSB_EVENT ) pthis; + if( pevent1->event == USB_EVENT_WAIT_RESET_PORT ) + { + while( pevent1->pnext ) + pevent1 = pevent1->pnext; + + pevent1->pnext = pevent; + que_exist = TRUE; + break; + } + ListNext( &dev_mgr->event_list, pthis, pnext ); + pthis = pnext; + } + + if( !que_exist ) + { + //Let's start a reset port request + InsertHeadList( &dev_mgr->event_list, &pevent->event_link ); + purb = usb_alloc_mem( NonPagedPool, sizeof( URB ) ); + RtlZeroMemory( purb, sizeof( URB ) ); + + purb->data_buffer = NULL; + purb->data_length = 0; + purb->pendp = &pdev->default_endp; + + purb->context = hub_ext; + purb->pdev = pdev; + purb->completion = hub_start_reset_port_completion; //hub_int_completion; + purb->reference = param; + + psetup = ( PUSB_CTRL_SETUP_PACKET )purb->setup_packet; + + psetup->bmRequestType = 0x23; //host-device other recepient + psetup->bRequest = USB_REQ_SET_FEATURE; + psetup->wValue = USB_PORT_FEAT_RESET; + psetup->wIndex = ( USHORT )param; + psetup->wLength = 0; + + purb->pirp = NULL; + //enter another state + set_port_state( hub_ext->port_status_queue[ param ].port_flags, STATE_WAIT_RESET_COMPLETE ); + + unlock_dev( pdev, TRUE ); + KeReleaseSpinLock( &dev_mgr->event_list_lock, old_irql ); + + status = hcd->hcd_submit_urb( hcd, pdev, purb->pendp, purb ) ; + if( status != STATUS_PENDING ) + { + //must be fatal error + usb_free_mem( purb ); + hub_reexamine_port_status_queue( pdev, param, FALSE ); + if( hub_remove_reset_event( pdev, param, FALSE ) ) + hub_start_next_reset_port( dev_mgr, FALSE ); + } + return; + } + + LBL_OUT: + unlock_dev( pdev, TRUE ); + KeReleaseSpinLock( &dev_mgr->event_list_lock, old_irql ); + return; +} + +VOID +hub_start_reset_port_completion( +PURB purb, +PVOID context +) +{ + PUSB_DEV pdev; + PUSB_ENDPOINT pendp; + PUSB_DEV_MANAGER dev_mgr; + NTSTATUS status; + ULONG port_idx; + PHCD hcd; + + USE_IRQL; + if( purb == NULL ) + return; + + if( context == NULL ) + { + //fatal error no retry. + usb_free_mem( purb ); + return; + } + + pdev = purb->pdev; + pendp = purb->pendp; + port_idx = purb->reference; + + lock_dev( pdev, TRUE ); + + if( dev_state( pdev ) == USB_DEV_STATE_ZOMB ) + { + unlock_dev( pdev, TRUE ); + usb_free_mem( purb ); + goto LBL_FREE_EVENT; + } + + hcd = pdev->hcd; + dev_mgr = dev_mgr_from_dev( pdev ); + unlock_dev( pdev, TRUE ); + + status = purb->status; + usb_free_mem( purb ); + + if( !usb_error( status ) ) + { + return; + } + + LBL_FREE_EVENT: + //since we have no patient to retry the dev, we should remove the event of + //wait_reset_port on the port from the event list. and if possible, start + //another reset process. note other port on the dev still have chance to be + //reset if necessary. + hub_reexamine_port_status_queue( pdev, port_idx, TRUE ); + if( hub_remove_reset_event( pdev, port_idx, TRUE ) ) + hub_start_next_reset_port( dev_mgr, TRUE ); + return; +} + + +VOID +hub_set_address_completion( +PURB purb, +PVOID context +) +{ + PUSB_DEV pdev, hub_dev; + PUSB_ENDPOINT pendp; + PUSB_DEV_MANAGER dev_mgr; + NTSTATUS status; + ULONG port_idx; + PHCD hcd; + + USE_IRQL; + + if( purb == NULL ) + return; + + if( context == NULL ) + { + //fatal error no retry. + usb_free_mem( purb ); + return; + } + + pdev = purb->pdev; + pendp = purb->pendp; + port_idx = purb->reference; + + lock_dev( pdev, TRUE ); + + hcd = pdev->hcd; + dev_mgr = dev_mgr_from_dev( pdev ); + hub_dev = pdev->parent_dev; + port_idx = pdev->port_idx; + + if( dev_state( pdev ) == USB_DEV_STATE_ZOMB ) + { + unlock_dev( pdev, TRUE ); + usb_free_mem( purb ); + //some error occured, let's start the next reset event + goto LBL_RESET_NEXT; + } + + pdev->flags &= ~USB_DEV_STATE_MASK; + pdev->flags |= USB_DEV_STATE_ADDRESSED; + + unlock_dev( pdev, TRUE ); + status = purb->status; + + if( usb_error( status ) ) + { + //retry the urb + purb->status = 0; + hcd_dbg_print( DBGLVL_MAXIMUM, ( "hub_set_address_completion: can not set address\n" ) ); + status = hcd->hcd_submit_urb( hcd, pdev, pendp, purb ); + //some error occured, disable the port + if( status != STATUS_PENDING ) + { + usb_free_mem( purb ); + status = hub_disable_port_request( hub_dev, ( UCHAR )port_idx ); + } + return; + } + + usb_free_mem( purb ); + //let address settle + usb_wait_ms_dpc( 10 ); + + //let's config the dev + dev_mgr_start_config_dev( pdev ); + + LBL_RESET_NEXT: + + //second, remove the event in the queue + hub_reexamine_port_status_queue( hub_dev, port_idx, TRUE ); + if( hub_remove_reset_event( hub_dev, port_idx, TRUE ) ) + hub_start_next_reset_port( dev_mgr, TRUE ); + return; +}; + +VOID +hub_disable_port_completion( +PURB purb, +PVOID pcontext +) +{ + PHUB2_EXTENSION hub_ext; + PUSB_DEV pdev; + PUSB_DEV_MANAGER dev_mgr; + UCHAR port_idx; + PUSB_ENDPOINT pendp; + PUSB_CTRL_SETUP_PACKET psetup; + + if( purb == NULL ) + return; + + pdev = purb->pdev; + pendp = purb->pendp; + psetup = ( PUSB_CTRL_SETUP_PACKET )purb->setup_packet; + port_idx = ( UCHAR )psetup->wIndex; + + dev_mgr = dev_mgr_from_dev( pdev ); + + usb_free_mem( purb ); + + hub_reexamine_port_status_queue( pdev, port_idx, TRUE ); + if( hub_remove_reset_event( pdev, port_idx, TRUE ) ) + hub_start_next_reset_port( dev_mgr, TRUE ); + + return; +} + +NTSTATUS +hub_disable_port_request( +PUSB_DEV pdev, +UCHAR port_idx +) +//caller should guarantee the validity of the dev +{ + PURB purb; + PUSB_ENDPOINT pendp; + PHUB2_EXTENSION hub_ext; + PUSB_CTRL_SETUP_PACKET psetup; + NTSTATUS status; + PHCD hcd; + USE_IRQL; + + if( pdev == NULL || port_idx == 0 ) + return STATUS_INVALID_PARAMETER; + + lock_dev( pdev, FALSE ); + if( dev_state( pdev ) == USB_DEV_STATE_ZOMB ) + { + unlock_dev( pdev, FALSE ); + return STATUS_DEVICE_DOES_NOT_EXIST; + } + + purb = usb_alloc_mem( NonPagedPool, sizeof( URB ) ); + if( purb == NULL ) + { + unlock_dev( pdev, FALSE ); + return STATUS_NO_MEMORY; + } + + RtlZeroMemory( purb, sizeof( URB ) ); + + purb->flags = 0; + purb->status = STATUS_SUCCESS; + + hub_ext = hub_ext_from_dev( pdev ); + + purb->data_buffer = NULL; + purb->data_length = 0; + + pendp = purb->pendp = &pdev->default_endp; + purb->pdev = pdev; + + purb->completion = hub_disable_port_completion; + purb->context = hub_ext; + + purb->pirp = NULL; + purb->reference = 0; + + psetup = ( PUSB_CTRL_SETUP_PACKET )purb->setup_packet; + + psetup->bmRequestType = 0x23; //host-device other recepient + psetup->bRequest = USB_REQ_CLEAR_FEATURE; //clear_feature + psetup->wValue = USB_PORT_FEAT_ENABLE; + psetup->wIndex = ( USHORT )port_idx; + psetup->wLength = 0; + + purb->pirp = NULL; + //enter another state + hcd = pdev->hcd; + unlock_dev( pdev, FALSE ); + + status = hcd->hcd_submit_urb( hcd, pdev, pendp, purb ); + if( status == STATUS_PENDING ) + return status; + + usb_free_mem( purb ); + return status; +} + + + +BOOL +hub_remove_reset_event( +PUSB_DEV pdev, +ULONG port_idx, +BOOL from_dpc +) +{ + PUSB_DEV_MANAGER dev_mgr; + PLIST_ENTRY pthis, pnext; + PUSB_EVENT pevent, pnext_event; + BOOL found; + + KIRQL old_irql; + + if( pdev == NULL ) + return FALSE; + + if( port_idx == 0 ) + return FALSE; + + dev_mgr = dev_mgr_from_dev( pdev ); + found = FALSE; + + if( from_dpc ) + KeAcquireSpinLockAtDpcLevel( &dev_mgr->event_list_lock ); + else + KeAcquireSpinLock( &dev_mgr->event_list_lock, &old_irql ); + + ListFirst( &dev_mgr->event_list, pthis ); + while( pthis ) + { + pevent = ( PUSB_EVENT )pthis; + if( pevent->event == USB_EVENT_WAIT_RESET_PORT && + ( pevent->flags & USB_EVENT_FLAG_QUE_TYPE ) == USB_EVENT_FLAG_QUE_RESET ) + { + if( pevent->pdev == pdev && pevent->param == port_idx ) + { + //remove it + RemoveEntryList( &pevent->event_link ); + pnext_event = pevent->pnext; + free_event( &dev_mgr->event_pool, pevent ); + + if( pnext_event ) + InsertHeadList( &dev_mgr->event_list, &pnext_event->event_link ); + + found = TRUE; + break; + } + } + ListNext( &dev_mgr->event_list, pthis, pnext ); + pthis = pnext; + } + + if( from_dpc ) + KeReleaseSpinLockFromDpcLevel( &dev_mgr->event_list_lock ); + else + KeReleaseSpinLock( &dev_mgr->event_list_lock, old_irql ); + return found; +} + +BOOL +hub_start_next_reset_port( +PUSB_DEV_MANAGER dev_mgr, +BOOL from_dpc +) +{ + PLIST_ENTRY pthis, pnext; + PUSB_EVENT pevent, pnext_event; + PUSB_DEV pdev; + PHUB2_EXTENSION hub_ext; + BOOL bret; + PURB purb; + BOOL processed; + PUSB_CTRL_SETUP_PACKET psetup; + PHCD hcd; + + USE_IRQL; + + if( dev_mgr == NULL ) + return FALSE; + + bret = FALSE; + processed = FALSE; + + if( from_dpc ) + KeAcquireSpinLockAtDpcLevel( &dev_mgr->event_list_lock ); + else + KeAcquireSpinLock( &dev_mgr->event_list_lock, &old_irql ); + + ListFirst( &dev_mgr->event_list, pthis); + + while( pevent = ( PUSB_EVENT )pthis ) + { + while( pevent->event == USB_EVENT_WAIT_RESET_PORT && + ( pevent->flags & USB_EVENT_FLAG_QUE_TYPE ) == USB_EVENT_FLAG_QUE_RESET ) + { + + processed = TRUE; + + pdev = pevent->pdev; + lock_dev( pdev, TRUE ); + + if( dev_state( pdev ) == USB_DEV_STATE_ZOMB ) + { + unlock_dev( pdev, TRUE ); + pnext_event = pevent->pnext; + free_event( &dev_mgr->event_pool, pevent ); + pevent = pnext_event; + if( pevent == NULL ) + { + bret = FALSE; + break; + } + continue; + } + + purb = usb_alloc_mem( NonPagedPool, sizeof( URB ) ); + RtlZeroMemory( purb, sizeof( URB ) ); + + purb->data_buffer = NULL; + purb->data_length = 0; + purb->pendp = &pdev->default_endp; + + hub_ext = hub_ext_from_dev( pdev ); + purb->context = hub_ext; + purb->pdev = pdev; + purb->completion = hub_start_reset_port_completion; + purb->reference = pevent->param; + + psetup = ( PUSB_CTRL_SETUP_PACKET )purb->setup_packet; + + psetup->bmRequestType = 0x23; //host-device other recepient + psetup->bRequest = 3; //set_feature + psetup->wValue = USB_PORT_FEAT_RESET; + psetup->wIndex = ( USHORT )pevent->param; + psetup->wLength = 0; + + purb->pirp = NULL; + hcd = pdev->hcd; + set_port_state( hub_ext->port_status_queue[ pevent->param ].port_flags, STATE_WAIT_RESET_COMPLETE ); + unlock_dev( pdev, TRUE ); + + bret = TRUE; + break; + } + + if( !processed ) + { + ListNext( &dev_mgr->event_list, pthis, pnext ); + pthis = pnext; + } + else + break; + } + + if( from_dpc ) + KeReleaseSpinLockFromDpcLevel( &dev_mgr->event_list_lock ); + else + KeReleaseSpinLock( &dev_mgr->event_list_lock, old_irql ); + + if( processed && bret ) + { + if( hcd->hcd_submit_urb( hcd, pdev, purb->pendp, purb ) != STATUS_PENDING ) + { + //fatal error + usb_free_mem( purb ); + bret = FALSE; + //do not know what to do + } + } + + if( pthis == NULL ) + bret = TRUE; + + return bret; +} + +// +//must have event-list-lock and dev-lock acquired +// +VOID +hub_post_esq_event( +PUSB_DEV pdev, +BYTE port_idx, +PROCESS_EVENT pe +) +{ + PUSB_DEV_MANAGER dev_mgr; + PUSB_EVENT pevent; + + if( pdev == NULL || port_idx == 0 || pe == NULL ) + return; + + dev_mgr = dev_mgr_from_dev( pdev ); + + pevent = alloc_event( &dev_mgr->event_pool, 1 ); + pevent->event = USB_EVENT_DEFAULT; + pevent->process_queue = event_list_default_process_queue; + pevent->process_event = pe; + pevent->context = ( ULONG )hub_ext_from_dev( pdev ); + pevent->param = port_idx; + pevent->flags = USB_EVENT_FLAG_ACTIVE; + pevent->pdev = pdev; + pevent->pnext = NULL; + + InsertTailList( &dev_mgr->event_list, &pevent->event_link ); + KeSetEvent( &dev_mgr->wake_up_event, 0, FALSE ); + // usb_dbg_print( DBGLVL_MAXIMUM, ( "hub_post_esq_event(): current element in event list is 0x%x\n", \ + // dbg_count_list( &dev_mgr->event_list ) ) ); + return; + +} + +BOOL +hub_check_reset_port_status( +PUSB_DEV pdev, +LONG port_idx +) +// called only in hub_clear_port_feature_completion +{ + PUSB_DEV_MANAGER dev_mgr; + PHUB2_EXTENSION hub_ext; + BOOL bReset; + USB_PORT_STATUS port_status; + PUSB_DEV pdev2; + PURB purb2; + PHCD hcd; + + PUSB_CTRL_SETUP_PACKET psetup; + ULONG status; + LARGE_INTEGER delay; + + USE_IRQL; + + //let's check whether the status change is a reset complete + usb_dbg_print( DBGLVL_MAXIMUM, ("hub_check_reset_port_status(): entering...\n" ) ); + dev_mgr = dev_mgr_from_dev( pdev ); + KeAcquireSpinLockAtDpcLevel( &dev_mgr->dev_list_lock ); + lock_dev( pdev, TRUE ); + + dev_mgr = dev_mgr_from_dev( pdev ); + hcd = pdev->hcd; + + if( dev_state( pdev ) == USB_DEV_STATE_ZOMB ) + { + unlock_dev( pdev, TRUE ); + KeReleaseSpinLockFromDpcLevel( &dev_mgr->dev_list_lock ); + return FALSE; + } + + hub_ext = hub_ext_from_dev( pdev ); + port_status = psq_peek( &hub_ext->port_status_queue[ port_idx ], 0 ); + + bReset = FALSE; + if( port_status.wPortChange & USB_PORT_STAT_C_RESET ) + bReset = TRUE; + + pdev2 = NULL; + purb2 = NULL; + + if( bReset + && port_state( hub_ext->port_status_queue[ port_idx ].port_flags ) == STATE_WAIT_RESET_COMPLETE + && psq_count( &hub_ext->port_status_queue[ port_idx ] ) == 1 ) + { + // a port-reset complete, empty the queue, keep the state + psq_outqueue( &hub_ext->port_status_queue[ port_idx ] ); + set_port_state( hub_ext->port_status_queue[ port_idx ].port_flags, + STATE_WAIT_ADDRESSED ); + + //let's new a dev, and start the set-addr request + if( hub_ext->child_dev[ port_idx ] == 0 ) + { + pdev2 = hub_ext->child_dev[ port_idx ] + = dev_mgr_alloc_device( dev_mgr, hcd ); + if( pdev2 ) + { + purb2 = usb_alloc_mem( NonPagedPool, sizeof( URB ) ); + if( !purb2 ) + { + dev_mgr_free_device( dev_mgr, pdev2 ); + pdev2 = hub_ext->child_dev[ port_idx ] = NULL; + } + else + { + if( port_status.wPortStatus & USB_PORT_STAT_LOW_SPEED ) + { + pdev2->flags |= USB_DEV_FLAG_LOW_SPEED; + } + else if( port_status.wPortStatus & USB_PORT_STAT_HIGH_SPEED ) + pdev2->flags |= USB_DEV_FLAG_HIGH_SPEED; + + pdev2->parent_dev = pdev; + pdev2->port_idx = ( UCHAR )port_idx; + pdev2->ref_count++; + + RtlZeroMemory( purb2, sizeof( URB ) ); + + purb2->pdev = pdev2; + purb2->pendp = &pdev2->default_endp; + purb2->context = hub_ext; + purb2->completion = hub_set_address_completion; + + InitializeListHead( &purb2->trasac_list ); + purb2->reference = port_idx; + purb2->pirp = 0; + + psetup = ( PUSB_CTRL_SETUP_PACKET )purb2->setup_packet; + psetup->bmRequestType = 0; + psetup->bRequest = USB_REQ_SET_ADDRESS; + psetup->wValue = pdev2->dev_addr; + } + } + } + + if( pdev2 && purb2 ) + { + //creation success, emit the urb + //add to dev list + InsertTailList( &dev_mgr->dev_list, &pdev2->dev_link ); + + unlock_dev( pdev, TRUE ); + KeReleaseSpinLockFromDpcLevel( &dev_mgr->dev_list_lock ); + + status = hcd->hcd_submit_urb( hcd, pdev2, purb2->pendp, purb2 ); + + lock_dev( pdev2, TRUE ); + pdev2->ref_count--; + usb_dbg_print( DBGLVL_MAXIMUM, ( "hub_check_reset_port_status(): new dev ref_count=0x%x\n", pdev2->ref_count ) ); + unlock_dev( pdev2, TRUE ); + + if( status != STATUS_PENDING ) + { + usb_free_mem( purb2 ); + //??? do we need to lock it for SMP? + //dev_mgr_free_device( dev_mgr, pdev2 ), let dev_mgr_thread to clean it; + // disable the port + if( hub_disable_port_request( pdev, ( UCHAR )port_idx ) != STATUS_PENDING ) + goto LBL_RESET_FAIL; + } + + return TRUE; + } + } + else + { + usb_dbg_print( DBGLVL_MAXIMUM, ( "hub_check_reset_port_status(): not a correct reset status\n" ) ); + } + unlock_dev( pdev, TRUE ); + KeReleaseSpinLockFromDpcLevel( &dev_mgr->dev_list_lock ); + +LBL_RESET_FAIL: + //Any event other than reset cause the reset process stall and another + //pending reset-port requeset is serviced + hub_reexamine_port_status_queue( pdev, port_idx, TRUE ); + if( hub_remove_reset_event( pdev, port_idx, TRUE ) ) + hub_start_next_reset_port( dev_mgr, TRUE); + + return FALSE; +} + +VOID +hub_reexamine_port_status_queue( +PUSB_DEV hub_dev, +ULONG port_idx, +BOOL from_dpc +) +{ + + PHUB2_EXTENSION hub_ext; + PUSB_DEV_MANAGER dev_mgr; + + USE_IRQL; + + if( hub_dev == NULL || port_idx == 0 ) + return; + + dev_mgr = dev_mgr_from_dev( hub_dev ); + if( from_dpc ) + KeAcquireSpinLockAtDpcLevel( &dev_mgr->event_list_lock ); + else + KeAcquireSpinLock( &dev_mgr->event_list_lock, &old_irql ); + + lock_dev( hub_dev, TRUE ); + if( dev_state( hub_dev ) != USB_DEV_STATE_ZOMB ) + { + + hub_ext = hub_ext_from_dev( hub_dev ); + if( psq_is_empty( &hub_ext->port_status_queue[ port_idx ] ) ) + { + set_port_state( hub_ext->port_status_queue[ port_idx ].port_flags, + STATE_IDLE ); + + } + else + { + set_port_state( hub_ext->port_status_queue[ port_idx ].port_flags, + STATE_EXAMINE_STATUS_QUE ); + + hub_post_esq_event( hub_dev, ( UCHAR )port_idx, hub_event_examine_status_que ); + } + } + unlock_dev( hub_dev, TRUE ); + + if( from_dpc ) + KeReleaseSpinLockFromDpcLevel( &dev_mgr->event_list_lock ); + else + KeReleaseSpinLock( &dev_mgr->event_list_lock, old_irql ); + return; +} + +BOOL +dev_mgr_start_config_dev( +PUSB_DEV pdev +) +//called in hub_set_address_completion +{ + PUSB_DEV_MANAGER dev_mgr; + PBYTE data_buf; + PUSB_CTRL_SETUP_PACKET psetup; + PURB purb; + PHCD hcd; + + USE_IRQL; + + if( pdev == NULL ) + return FALSE; + + lock_dev( pdev, TRUE ); + if( dev_state( pdev ) == USB_DEV_STATE_ZOMB ) + { + unlock_dev( pdev, TRUE ); + return FALSE; + } + + hcd = pdev->hcd; + + //first, get device descriptor + purb = usb_alloc_mem( NonPagedPool, sizeof( URB ) ); + data_buf = usb_alloc_mem( NonPagedPool, 512 ); + if( purb == NULL ) + { + unlock_dev( pdev, TRUE ); + return FALSE; + } + + RtlZeroMemory( purb, sizeof( URB ) ); + RtlZeroMemory( data_buf, 512 ); + + psetup = ( PUSB_CTRL_SETUP_PACKET )purb->setup_packet; + + purb->data_buffer = data_buf; // user data + purb->data_length = 8; // get partial desc + + pdev->desc_buf = data_buf; + pdev->desc_buf_size = 512; + + purb->pdev = pdev; + purb->pendp = &pdev->default_endp; //pipe for current transfer + + purb->completion = dev_mgr_get_desc_completion; + purb->reference = 0; + + InitializeListHead( &purb->trasac_list ); + + psetup->bmRequestType = 0x80; + psetup->bRequest = USB_REQ_GET_DESCRIPTOR; + psetup->wValue = ( USB_DT_DEVICE << 8 ) | 0; + psetup->wIndex = 0; + psetup->wLength = 8; //sizeof( USB_DEVICE_DESC ); + unlock_dev( pdev, TRUE ); + + if( hcd->hcd_submit_urb( hcd, pdev, purb->pendp, purb ) != STATUS_PENDING ) + { + usb_free_mem( purb ); + usb_free_mem( data_buf ); + return FALSE; + } + return TRUE; +} + +VOID +dev_mgr_get_desc_completion( +PURB purb, +PVOID context +) +{ + PUSB_DEV pdev; + PUSB_CONFIGURATION_DESC pconfig_desc; + PUSB_ENDPOINT pendp; + PUSB_DEV_MANAGER dev_mgr; + NTSTATUS status; + PUSB_CTRL_SETUP_PACKET psetup; + LONG i; + PHCD hcd; + + USE_IRQL; + + if( purb == NULL ) + return; + + pdev = purb->pdev; + pendp = purb->pendp; + + if( pdev == NULL || pendp == NULL ) + { + usb_free_mem( purb ); + purb = NULL; + return; + } + + lock_dev( pdev, TRUE ); + if( dev_state( pdev ) == USB_DEV_STATE_ZOMB ) + { + unlock_dev( pdev, TRUE ); + goto LBL_OUT; + } + + pendp = &pdev->default_endp; + dev_mgr = dev_mgr_from_dev( pdev ); + hcd = pdev->hcd; + psetup = ( PUSB_CTRL_SETUP_PACKET )purb->setup_packet; + + if( usb_error( purb->status ) ) + { + unlock_dev( pdev, TRUE ); + hcd_dbg_print( DBGLVL_MAXIMUM, ( "dev_mgr_get_desc_completion: can not get dev desc ref=0x%x, status=0x%x\n", purb->reference, purb->status ) ); + goto LBL_OUT; + } + + switch( purb->reference ) + { + case 0: + { + //only partial dev_desc + //enable the dev specific default endp maxpacketsize + pdev->pusb_dev_desc = ( PUSB_DEVICE_DESC )purb->data_buffer; + + psetup = ( PUSB_CTRL_SETUP_PACKET )purb->setup_packet; + psetup->wLength = sizeof( USB_DEVICE_DESC ); + + //get the complete dev_desc + purb->reference = 1; + purb->status = 0; + purb->data_length = sizeof( USB_DEVICE_DESC ); + + unlock_dev( pdev, TRUE ); + + status = hcd->hcd_submit_urb( hcd, pdev, pendp, purb ); + if( status != STATUS_PENDING ) + { + goto LBL_OUT; + } + return; + } + case 1: + { + //let's begin to get config descriptors. + if( pdev->pusb_dev_desc->bNumConfigurations == 0 ) + { + unlock_dev( pdev, TRUE ); + goto LBL_OUT; + } + + purb->data_buffer += sizeof( USB_DEVICE_DESC ); + purb->data_length = 8; + purb->reference++; + purb->context = ( PVOID )sizeof( USB_DEVICE_DESC ); + purb->status = 0; + + psetup->wValue = ( USB_DT_CONFIG << 8 ) | 0; + psetup->wLength = 8; + unlock_dev( pdev, TRUE ); + + status = hcd->hcd_submit_urb( hcd, pdev, pendp, purb ); + + if( status != STATUS_PENDING ) + { + goto LBL_OUT; + } + return; + } + default: + { + LONG config_idx; + config_idx = ( purb->reference >> 1 ) - 1; + if( ( purb->reference & 1 ) == 0 ) + { + //partial config desc is obtained. + pconfig_desc = ( PUSB_CONFIGURATION_DESC )purb->data_buffer; + if( pconfig_desc->wTotalLength >= 1024 ) + { + //treat as an error + unlock_dev( pdev, TRUE ); + goto LBL_OUT; + + } + + if( pconfig_desc->wTotalLength > ( USHORT )( pdev->desc_buf_size - ( LONG ) purb->context ) ) + { + //rewind the 8-byte hdr + *( ( PULONG )&context ) -= 8; + realloc_buf( pdev, purb ); + } + purb->data_length = pconfig_desc->wTotalLength; + psetup->wLength = pconfig_desc->wTotalLength; + purb->reference ++; + unlock_dev( pdev, TRUE ); + status = hcd->hcd_submit_urb( hcd, pdev, pendp, purb ); + if( status != STATUS_PENDING ) + goto LBL_OUT; + + } + else + { + //complete desc is returned. + if( config_idx + 1 < pdev->pusb_dev_desc->bNumConfigurations ) + { + //still have configurations left + *( ( PULONG )&context ) += psetup->wLength; + purb->data_buffer = &pdev->desc_buf[ ( LONG ) context ]; + purb->data_length = 8; + psetup->wLength = 8; + psetup->wValue = ( ( ( USB_DT_CONFIG ) << 8 ) | ( config_idx + 1 ) ); + purb->reference ++; + purb->context = context; + + if( ( ( LONG )context ) + 8 > pdev->desc_buf_size ) + realloc_buf( pdev, purb ); + + purb->status = 0; + unlock_dev( pdev, TRUE ); + status = hcd->hcd_submit_urb( hcd, pdev, pendp, purb ); + if( status != STATUS_PENDING ) + goto LBL_OUT; + } + else + { + //config descriptors have all been fetched + unlock_dev( pdev, TRUE ); + usb_free_mem( purb ); + purb = NULL; + dev_mgr_start_select_driver( pdev ); + } + } + return; + } + } + + LBL_OUT: + usb_free_mem( purb ); + purb = NULL; + + lock_dev( pdev, TRUE ); + if( dev_state( pdev ) != USB_DEV_STATE_ZOMB ) + { + if( pdev->desc_buf ) + { + usb_free_mem( pdev->desc_buf ); + pdev->desc_buf_size = 0; + pdev->desc_buf = NULL; + pdev->pusb_dev_desc = NULL; + pdev->usb_config = NULL; + } + } + unlock_dev( pdev, TRUE ); + + return; +} + +BOOL +dev_mgr_start_select_driver( +PUSB_DEV pdev +) +{ + PUSB_DEV_MANAGER dev_mgr; + PUSB_EVENT pevent; + BOOL bret; + + USE_IRQL; + + if( pdev == NULL ) + return FALSE; + + dev_mgr = dev_mgr_from_dev( pdev ); + KeAcquireSpinLockAtDpcLevel( &dev_mgr->event_list_lock ); + lock_dev( pdev, TRUE ); + + if( dev_state( pdev ) == USB_DEV_STATE_ZOMB ) + { + bret = FALSE; + goto LBL_OUT; + } + + pevent = alloc_event( &dev_mgr->event_pool, 1 ); + if( pevent == NULL ) + { + bret = FALSE; + goto LBL_OUT; + } + pevent->flags = USB_EVENT_FLAG_ACTIVE; + pevent->event = USB_EVENT_DEFAULT; + pevent->pdev = pdev; + pevent->context = 0; + pevent->param = 0; + pevent->pnext = 0; //vertical queue for serialized operation + pevent->process_event = dev_mgr_event_select_driver; + pevent->process_queue = event_list_default_process_queue; + + InsertTailList( &dev_mgr->event_list, &pevent->event_link ); + KeSetEvent( &dev_mgr->wake_up_event, 0, FALSE ); + bret = TRUE; + + LBL_OUT: + unlock_dev( pdev, TRUE ); + KeReleaseSpinLockFromDpcLevel( &dev_mgr->event_list_lock ); + return bret; +} + +BOOL +dev_mgr_connect_to_dev( +PVOID Parameter +) +{ + PUSB_DEV pdev; + DEV_HANDLE dev_handle; + NTSTATUS status; + PUSB_DRIVER pdriver; + PCONNECT_DATA pcd = ( PCONNECT_DATA )Parameter; + PUSB_DEV_MANAGER dev_mgr; + CONNECT_DATA param; + + USE_IRQL; + + if( pcd == NULL ) + return FALSE; + dev_handle = pcd->dev_handle; + pdriver = pcd->pdriver; + dev_mgr = pcd->dev_mgr; + + param.dev_mgr = dev_mgr; + param.pdriver = pdriver; + param.dev_handle = 0; //not used + + status = usb_query_and_lock_dev( dev_mgr, dev_handle, &pdev ); + if( status != STATUS_SUCCESS ) + return FALSE; + + usb_dbg_print( DBGLVL_MAXIMUM, ( "dev_mgr_connect_to_dev(): about to call driver's dev_connect\n" ) ); + status = pdriver->disp_tbl.dev_connect( ¶m, dev_handle ); + usb_unlock_dev( pdev ); + return status; +} + +VOID +dev_mgr_event_select_driver( +PUSB_DEV pdev, +ULONG event, +ULONG context, +ULONG param +) +{ + PUSB_DEV_MANAGER dev_mgr; + PLIST_ENTRY pthis, pnext; + PUSB_DRIVER pdriver, pcand; + LONG credit, match, i; + DEV_HANDLE handle; + CONNECT_DATA cd; + + USE_IRQL; + + if( pdev == NULL ) + return; + + lock_dev( pdev, FALSE ); + if( dev_state( pdev ) == USB_DEV_STATE_ZOMB ) + { + unlock_dev( pdev, FALSE ); + return; + } + dev_mgr = dev_mgr_from_dev( pdev ); + + pcand = NULL; + match = 0; + for( i = HUB_DRIVER_IDX; i < DEVMGR_MAX_DRIVERS; i++ ) + { + //bypass root-hub driver with idx zero + pdriver = ( PUSB_DRIVER ) &dev_mgr->driver_list[ i ]; + + if( pdriver->driver_desc.flags & USB_DRIVER_FLAG_DEV_CAPABLE ) + credit = dev_mgr_score_driver_for_dev( dev_mgr, pdriver, pdev->pusb_dev_desc ); + else + { + continue; + } + if( credit > match ) + pcand = pdriver, match = credit; + + } + + if( match ) + { + // we set class driver here + // pdev->dev_driver = pcand; + handle = usb_make_handle( pdev->dev_id, 0, 0 ); + } + unlock_dev( pdev, FALSE ); + + if( match ) + { + + cd.dev_handle = handle; + cd.pdriver = pcand; + cd.dev_mgr = dev_mgr; + + if( dev_mgr_connect_to_dev( &cd ) ) + return; + + // ExInitializeWorkItem( pwork_item, dev_mgr_connect_to_dev, ( PVOID )pcd ); + // ExQueueWorkItem( pwork_item, DelayedWorkQueue ); + } + cd.dev_handle = handle; + cd.pdriver = &dev_mgr->driver_list[ GEN_DRIVER_IDX ]; + cd.dev_mgr = dev_mgr; + dev_mgr_connect_to_dev( &cd ); + return; +} + +BOOL +dev_mgr_build_usb_endp( +PUSB_INTERFACE pif, +PUSB_ENDPOINT pendp, +PUSB_ENDPOINT_DESC pendp_desc +) +{ + if( pendp == NULL || pif == NULL || pendp_desc == NULL ) + return FALSE; + + pendp->flags = 0; + InitializeListHead( &pendp->urb_list ); //pending urb queue + pendp->pusb_if = pif; + pendp->pusb_endp_desc = pendp_desc; + return TRUE; +} + +BOOL +dev_mgr_build_usb_if( +PUSB_CONFIGURATION pcfg, +PUSB_INTERFACE pif, +PUSB_INTERFACE_DESC pif_desc, +BOOL alt_if +) +{ + LONG i; + PUSB_ENDPOINT_DESC pendp_desc; + + if( pcfg == NULL || pif == NULL || pif_desc == NULL ) + return FALSE; + + if( alt_if == FALSE ) + { + pif->endp_count = pif_desc->bNumEndpoints > MAX_ENDPS_PER_IF + ? MAX_ENDPS_PER_IF + : pif_desc->bNumEndpoints; + + pif->pif_drv = NULL; + pif->pusb_config = pcfg; + pif->pusb_if_desc = pif_desc; + pif->if_ext_size = 0; + pif->if_ext = NULL; + + InitializeListHead( &pif->altif_list ); + pif->altif_count = 0; + + pendp_desc = ( PUSB_ENDPOINT_DESC )( &( ( PBYTE )pif_desc )[ sizeof( USB_INTERFACE_DESC ) ] ); + + for( i = 0; i < pif->endp_count; i++, pendp_desc++ ) + { + dev_mgr_build_usb_endp( pif, &pif->endp[ i ], pendp_desc ); + } + } + else + { + PUSB_INTERFACE paltif; + PLIST_ENTRY pthis, pnext; + + pif->altif_count++; + paltif = usb_alloc_mem( NonPagedPool, sizeof( USB_INTERFACE ) ); + RtlZeroMemory( paltif, sizeof( USB_INTERFACE ) ); + InsertTailList( &pif->altif_list, &paltif->altif_list ); + paltif->pif_drv = NULL; + paltif->pusb_config = pcfg; + paltif->pusb_if_desc = pif_desc; + paltif->if_ext_size = 0; + paltif->if_ext = NULL; + paltif->endp_count = pif_desc->bNumEndpoints > MAX_ENDPS_PER_IF + ? MAX_ENDPS_PER_IF + : pif_desc->bNumEndpoints; + + ListFirst( &pif->altif_list, pthis ); + + while( pthis ) + { + //synchronize the altif_count; + PUSB_INTERFACE pthis_if; + pthis_if =( PUSB_INTERFACE )( ( ( PBYTE )pthis ) - offsetof( USB_INTERFACE, altif_list ) ); + pthis_if->altif_count = pif->altif_count; + ListNext( &pif->altif_list, pthis, pnext ); + } + + } + return TRUE; +} + +NTSTATUS +dev_mgr_build_usb_config( +PUSB_DEV pdev, +PBYTE pbuf, +ULONG config_val, +LONG config_count +) +{ + PUSB_CONFIGURATION pcfg; + PUSB_INTERFACE_DESC pif_desc; + PUSB_INTERFACE pif; + int i; + LONG if_count; + + if( pdev == NULL || pbuf == NULL ) + return STATUS_INVALID_PARAMETER; + + + pdev->usb_config = usb_alloc_mem( NonPagedPool, sizeof( USB_CONFIGURATION ) ); + pcfg = pdev->usb_config; + + if( pdev->usb_config == NULL ) + return STATUS_NO_MEMORY; + + RtlZeroMemory( pcfg, sizeof( USB_CONFIGURATION ) ); + pcfg->pusb_config_desc = usb_find_config_desc_by_val( pbuf, + config_val, + config_count ); + + if( pcfg->pusb_config_desc == NULL ) + { + usb_free_mem( pcfg ); + pdev->usb_config = NULL; + return STATUS_UNSUCCESSFUL; + } + pcfg->if_count = pcfg->pusb_config_desc->bNumInterfaces; + pcfg->pusb_dev = pdev; + pif_desc = ( PUSB_INTERFACE_DESC )&( ( PBYTE )pcfg->pusb_config_desc )[ sizeof( USB_CONFIGURATION_DESC ) ]; + if_count = pcfg->if_count; + + for( i = 0 ; i < if_count; i++, pif_desc++ ) + { + if( pif_desc->bAlternateSetting == 0) + { + dev_mgr_build_usb_if( pcfg, &pcfg->interf[ i ], pif_desc, FALSE ); + } + else + { + i--; + pif = &pcfg->interf[ i ]; + dev_mgr_build_usb_if( pcfg, pif, pif_desc, TRUE ); + } + } + return STATUS_SUCCESS; +} + +NTSTATUS +dev_mgr_destroy_usb_config( +PUSB_CONFIGURATION pcfg +) +{ + long i; + PLIST_ENTRY pthis, pnext; + PUSB_INTERFACE pif; + + if( pcfg == NULL ) + return FALSE; + + for( i = 0; i < pcfg->if_count; i++ ) + { + pif = &pcfg->interf[ i ]; + + if( pif->altif_count ) + { + ListFirst( &pif->altif_list, pthis ); + while( pthis ) + { + PUSB_INTERFACE pthis_if; + pthis_if =( PUSB_INTERFACE )( ( ( PBYTE )pthis ) - offsetof( USB_INTERFACE, altif_list ) ); + RemoveEntryList( pthis ); + usb_free_mem( pthis_if ); + if( IsListEmpty( &pif->altif_list ) == TRUE ) + break; + + ListFirst( &pif->altif_list, pthis ); + } + } + } + usb_free_mem( pcfg ); + return TRUE; +} + +#define is_dev_product_match( pdriVER, pdev_DESC ) \ +( ( pdriVER )->driver_desc.vendor_id == ( pdev_DESC )->idVendor \ + && ( pdriVER )->driver_desc.product_id == ( pdev_DESC )->idProduct ) + +LONG +dev_mgr_score_driver_for_dev( +PUSB_DEV_MANAGER dev_mgr, +PUSB_DRIVER pdriver, +PUSB_DEVICE_DESC pdev_desc +) +{ + LONG credit = 0; + + //assume supports all the sub_class are supported if sub_class is zero + if( pdriver->driver_desc.dev_class == pdev_desc->bDeviceClass ) + { + if( pdriver->driver_desc.dev_sub_class == 0 && pdriver->driver_desc.dev_protocol == 0 ) + credit = 3; + else if( pdriver->driver_desc.dev_sub_class == pdev_desc->bDeviceSubClass ) + { + if( pdriver->driver_desc.dev_protocol == 0 ) + credit = 6; + else if( pdriver->driver_desc.dev_protocol == pdev_desc->bDeviceProtocol ) + credit = 9; + } + } + + if( is_dev_product_match( pdriver, pdev_desc ) ) + credit += 20; + + return credit; +} + +LONG +dev_mgr_score_driver_for_if( +PUSB_DEV_MANAGER dev_mgr, +PUSB_DRIVER pdriver, +PUSB_INTERFACE_DESC pif_desc +) +{ + LONG credit; + + if( pdriver == NULL + || !( pdriver->driver_desc.flags & USB_DRIVER_FLAG_IF_CAPABLE ) + || pif_desc == NULL + || dev_mgr == NULL ) + return 0; + + if( is_header_match( ( PBYTE )pif_desc, USB_DT_INTERFACE ) == FALSE ) + { + return 0; + } + + credit = 0; + if( ( pdriver->driver_desc.if_class == pif_desc->bInterfaceClass ) ) + { + if( pdriver->driver_desc.if_sub_class == 0 && pdriver->driver_desc.if_protocol == 0 ) + credit = 2; + if( pdriver->driver_desc.if_sub_class == pif_desc->bInterfaceSubClass ) + { + if( pdriver->driver_desc.if_protocol == 0 ) + credit = 4; + if( pdriver->driver_desc.if_protocol == pif_desc->bInterfaceProtocol ) + credit = 6; + } + } + else + credit = 1; + + return credit; +} + +#define is_equal_driver( pd1, pd2, ret ) \ +{\ + int i;\ + ret = TRUE;\ + PUSB_DRIVER pdr1, pdr2;\ + pdr1 = ( PUSB_DRIVER )( pd1 );\ + pdr2 = ( PUSB_DRIVER ) ( pd2 );\ + for( i = 0; i < 16; i++ )\ + {\ + if( pdr1->driver_name[ i ] != pdr2->driver_name[ i ] )\ + {\ + ret = FALSE;\ + break;\ + }\ + }\ +} + +UCHAR +dev_mgr_register_hcd( +PUSB_DEV_MANAGER dev_mgr, +PHCD hcd +) +//return value is the hcd id +{ + if( dev_mgr == NULL || hcd == NULL ) + return 0xff; + + if( dev_mgr->hcd_count >= MAX_HCDS ) + return 0xff; + + dev_mgr->hcd_array[ dev_mgr->hcd_count++ ] = hcd; + return dev_mgr->hcd_count - 1; +} + +BOOL +dev_mgr_register_irp( +PUSB_DEV_MANAGER dev_mgr, +PIRP pirp, +PURB purb +) +{ + KIRQL old_irql; + if( dev_mgr == NULL ) + return FALSE; + + if( add_irp_to_list( &dev_mgr->irp_list, pirp, purb ) ) + { + return TRUE; + } + TRAP(); + return FALSE; +} + +PURB +dev_mgr_remove_irp( +PUSB_DEV_MANAGER dev_mgr, +PIRP pirp +) +//caller must guarantee that when this func is called, +//the urb associated must exist. +{ + PURB purb; + KIRQL old_irql; + if( dev_mgr == NULL ) + return NULL; + + purb = remove_irp_from_list( &dev_mgr->irp_list, pirp, NULL ); + return purb; +} + +VOID +dev_mgr_cancel_irp( +PDEVICE_OBJECT dev_obj, +PIRP pirp +) +{ + PUSB_DEV_MANAGER dev_mgr; + PDEVEXT_HEADER pdev_ext_hdr; + ULONG i; + + pdev_ext_hdr = ( PDEVEXT_HEADER )dev_obj->DeviceExtension; + dev_mgr = pdev_ext_hdr->dev_mgr; + + if( dev_obj->CurrentIrp == pirp ) + { + IoReleaseCancelSpinLock( pirp->CancelIrql ); + // we did not IoStartNextPacket, leave it for the urb completion + } + else + { + KeRemoveEntryDeviceQueue( &dev_obj->DeviceQueue, &pirp->Tail.Overlay.DeviceQueueEntry ); + IoReleaseCancelSpinLock( pirp->CancelIrql ); + + pirp->IoStatus.Information = 0; + pirp->IoStatus.Status = STATUS_CANCELLED; + IoCompleteRequest( pirp, IO_NO_INCREMENT ); + // the device queue is moved on, no need to call IoStartNextPacket + return; + } + + // + // remove the irp and call the dev_mgr_cancel_irp + // the completion will be done in urb completion + // + remove_irp_from_list( &dev_mgr->irp_list, pirp, dev_mgr ); + return; + +} + +VOID +dev_mgr_release_hcd( +PUSB_DEV_MANAGER dev_mgr +) +// release the hcd +{ + LONG i; + PHCD hcd; + for( i = 0; i < dev_mgr->hcd_count; i++ ) + { + hcd = dev_mgr->hcd_array[ i ]; + hcd->hcd_release( hcd ); + dev_mgr->hcd_array[ i ] = 0; + } + dev_mgr->hcd_count = 0; + return; +} + +VOID +dev_mgr_start_hcd( +PUSB_DEV_MANAGER dev_mgr +) +{ + LONG i; + PHCD hcd; + for( i = 0; i < dev_mgr->hcd_count; i++ ) + { + hcd = dev_mgr->hcd_array[ i ]; + hcd->hcd_start( hcd ); + } + return; +} + +BOOL +hub_connect( +PCONNECT_DATA param, +DEV_HANDLE dev_handle +) +{ + URB urb, *purb; + CHAR buf[ 512 ]; + DEV_HANDLE endp_handle; + USB_DEVICE_DESC dev_desc; + PUSB_CONFIGURATION_DESC pcfg_desc; + PUSB_INTERFACE_DESC pif_desc; + PUSB_CTRL_SETUP_PACKET psetup; + NTSTATUS status; + LONG i, j, found, cfg_val; + PUSB_DEV_MANAGER dev_mgr; + PUSB_DEV pdev; + USE_IRQL; + + + if( param == NULL || dev_handle == 0 ) + return FALSE; + + dev_mgr = param->dev_mgr; + + pcfg_desc = ( PUSB_CONFIGURATION_DESC ) buf; + endp_handle = dev_handle | 0xffff; + UsbBuildGetDescriptorRequest(&urb, + endp_handle, + USB_DT_DEVICE, + 0, + 0, + ( &dev_desc ), + ( sizeof( USB_DEVICE_DESC ) ), + NULL, + 0, + 0 ); + + status = usb_submit_urb( dev_mgr, &urb ); + if( status != STATUS_SUCCESS ) + return FALSE; + + found = FALSE; + for( i = 0; i < dev_desc.bNumConfigurations; i++ ) + { + UsbBuildGetDescriptorRequest(&urb, + endp_handle, + USB_DT_CONFIG, + ( USHORT )i, + 0, + buf, + 512, + NULL, + 0, + 0 ); + + status = usb_submit_urb( dev_mgr, &urb ); + if( status != STATUS_SUCCESS ) + { + return FALSE; + } + + status = usb_query_and_lock_dev( dev_mgr, dev_handle, &pdev ); + if( status != STATUS_SUCCESS ) + return FALSE; + + pif_desc = ( PUSB_INTERFACE_DESC )&buf[ sizeof( USB_CONFIGURATION_DESC ) ]; + for( j = 0; j < pcfg_desc->bNumInterfaces; j++ ) + { + if( pif_desc->bInterfaceClass == USB_CLASS_HUB + && pif_desc->bInterfaceSubClass == 0 + && pif_desc->bNumEndpoints == 1 ) + { + if( ( pif_desc->bInterfaceProtocol > 0 && pif_desc->bInterfaceProtocol < 3 ) + || ( pif_desc->bInterfaceProtocol == 0 && pdev->flags & USB_DEV_FLAG_HIGH_SPEED ) + || ( pif_desc->bInterfaceProtocol == 0 && !usb2( pdev->hcd ) ) ) + { + found = TRUE; + cfg_val = pcfg_desc->bConfigurationValue; + break; + } + } + if( usb_skip_if_and_altif( ( PBYTE* )&pif_desc ) == FALSE ) + { + break; + } + } + usb_unlock_dev( pdev ); + + if( found ) + break; + + if( usb_skip_one_config( ( PBYTE* )&pcfg_desc ) == FALSE ) + { + break; + } + + } + if( found ) + { + purb = usb_alloc_mem( NonPagedPool, sizeof( URB ) ); + if( purb == NULL ) + return FALSE; + + psetup = ( PUSB_CTRL_SETUP_PACKET )( purb )->setup_packet; + urb_init( ( purb ) ); + + purb->endp_handle = endp_handle; + purb->data_buffer = NULL; + purb->data_length = 0; + purb->completion = hub_set_cfg_completion; + purb->context = dev_mgr; + purb->reference = ( LONG )param->pdriver; + psetup->bmRequestType = 0; + psetup->bRequest = USB_REQ_SET_CONFIGURATION; + psetup->wValue = ( USHORT )cfg_val; + psetup->wIndex = 0; + psetup->wLength = 0; + + status = usb_submit_urb( dev_mgr, purb ); + if( status != STATUS_PENDING ) + { + usb_free_mem( purb ); + return FALSE; + } + return TRUE; + } + + return FALSE; +} + +void +hub_set_interface_completion( +PURB purb, +PVOID pcontext +); + +void +hub_set_cfg_completion( +PURB purb, +PVOID pcontext +) +{ + PUSB_DEV_MANAGER dev_mgr; + PUSB_DRIVER pdriver; + ULONG endp_handle, dev_handle; + PUSB_CTRL_SETUP_PACKET psetup; + UCHAR if_idx, if_alt_idx; + PUSB_DEV pdev; + PUSB_INTERFACE pif; + BOOL high_speed, multiple_tt; + NTSTATUS status; + USE_IRQL; + + if( purb == NULL || pcontext == NULL ) + return; + + //pdev = NULL; + dev_mgr = ( PUSB_DEV_MANAGER )pcontext; + endp_handle = purb->endp_handle; + dev_handle = endp_handle & 0xffff0000; + pdriver = ( PUSB_DRIVER )purb->reference; + high_speed = FALSE; + multiple_tt = FALSE; + + if( purb->status != STATUS_SUCCESS ) + { + goto LBL_ERROR; + } + + status = usb_query_and_lock_dev( dev_mgr, purb->endp_handle, &pdev ); + if( status != STATUS_SUCCESS ) + { + usb_unlock_dev( pdev ); + goto LBL_ERROR; + } + lock_dev( pdev, TRUE ); + if( dev_state( pdev ) == USB_DEV_STATE_ZOMB ) + { + unlock_dev( pdev, TRUE ); + usb_unlock_dev( pdev ); + goto LBL_ERROR; + } + if( pdev->flags & USB_DEV_FLAG_HIGH_SPEED ) + { + high_speed = TRUE; + hub_if_from_dev( pdev, pif ); + if( pif->altif_count ) + { + multiple_tt = TRUE; + if_idx = pif - &pdev->usb_config->interf[ 0 ]; + } + } + unlock_dev( pdev, TRUE ); + usb_unlock_dev( pdev ); + + if( !high_speed || !multiple_tt ) + { + hub_set_interface_completion( purb, pcontext ); + return; + } + + psetup = ( PUSB_CTRL_SETUP_PACKET )( purb )->setup_packet; + urb_init( ( purb ) ); + + // set the mult-tt if exist + purb->endp_handle = endp_handle; + purb->data_buffer = NULL; + purb->data_length = 0; + purb->completion = hub_set_interface_completion; + purb->context = dev_mgr; + purb->reference = ( LONG )pdriver; + psetup->bmRequestType = 0; + psetup->bRequest = USB_REQ_SET_INTERFACE; + psetup->wValue = ( USHORT )1; // alternate tt + psetup->wIndex = if_idx; // if index + psetup->wLength = 0; + + status = usb_submit_urb( dev_mgr, purb ); + if( status == STATUS_PENDING ) + return; + +LBL_ERROR: + usb_free_mem( purb ); + purb = NULL; + return; +} + +void +hub_set_interface_completion( +PURB purb, +PVOID pcontext +) +{ + + PUSB_ENDPOINT pendp; + NTSTATUS status; + PUSB_CTRL_SETUP_PACKET psetup; + PUSB_DEV_MANAGER dev_mgr; + PBYTE dev_ext; + DEV_HANDLE endp_handle; + PUSB_DRIVER pdriver; + + if( purb == NULL || pcontext == NULL ) + return; + + //pdev = NULL; + dev_mgr = ( PUSB_DEV_MANAGER )pcontext; + endp_handle = purb->endp_handle; + pdriver = ( PUSB_DRIVER )purb->reference; + + if( purb->status != STATUS_SUCCESS ) + { + usb_free_mem( purb ); + return; + } + + dev_ext = usb_alloc_mem( NonPagedPool, sizeof( HUB2_EXTENSION ) ); + if( dev_ext == NULL ) + { + goto LBL_OUT; + } + + // + //acquire hub descriptor + // + RtlZeroMemory( dev_ext, sizeof( HUB2_EXTENSION ) ); + urb_init( purb ); + + purb->data_buffer = ( PUCHAR )&( ( HUB2_EXTENSION* ) dev_ext )->hub_desc; + purb->endp_handle = endp_handle; + purb->data_length = sizeof( USB_HUB_DESCRIPTOR ); + purb->completion = hub_get_hub_desc_completion; + purb->context = ( PVOID )dev_mgr; + purb->reference = ( ULONG )dev_ext; + purb->pirp = ( PIRP )pdriver; + + psetup = ( PUSB_CTRL_SETUP_PACKET ) purb->setup_packet; + psetup->bmRequestType = 0xa0; + psetup->bRequest = USB_REQ_GET_DESCRIPTOR; + psetup->wValue = ( 0x29 << 8 ); + psetup->wLength = sizeof( USB_HUB_DESCRIPTOR ); + status = usb_submit_urb( dev_mgr, purb ); + + if( status != STATUS_PENDING ) + { + usb_free_mem( dev_ext ); + goto LBL_OUT; + } + return; + + LBL_OUT: + + //clear the dev_driver fields in the dev. + usb_free_mem( purb ); + return; +} + + +void +hub_power_on_port_completion( +PURB purb, +PVOID pcontext +) +{ + PUSB_DEV_MANAGER dev_mgr; + PUSB_DEV pdev; + if( purb == NULL ) + return; + if( pcontext == NULL ) + goto LBL_OUT; + + dev_mgr = ( PUSB_DEV_MANAGER ) pcontext; + + if( purb->status != STATUS_SUCCESS ) + { + usb_dbg_print( DBGLVL_MAXIMUM, ( "hub_power_on_port_completion(): port%d power on failed\n", purb->reference ) ); + } + else + { + usb_dbg_print( DBGLVL_MAXIMUM, ( "hub_power_on_port_completion(): port%d power on succeed\n", purb->reference ) ); + } + +LBL_OUT: + usb_free_mem( purb ); + return; +} + +NTSTATUS +hub_power_on_port( +PUSB_DEV pdev, +UCHAR port_idx +) +{ + + PUSB_ENDPOINT pendp; + NTSTATUS status; + PUSB_CTRL_SETUP_PACKET psetup; + PUSB_DEV_MANAGER dev_mgr; + PBYTE dev_ext; + DEV_HANDLE endp_handle; + PURB purb; + PHCD hcd; + + USE_IRQL; + if( pdev == NULL || port_idx == 0 ) + return STATUS_INVALID_PARAMETER; + + purb = usb_alloc_mem( NonPagedPool, sizeof( URB ) ); + if( purb == NULL ) + return STATUS_NO_MEMORY; + + urb_init( purb ); + + lock_dev( pdev, FALSE ); + if( dev_state( pdev ) == USB_DEV_STATE_ZOMB ) + { + unlock_dev( pdev, FALSE ); + status = STATUS_DEVICE_DOES_NOT_EXIST; + goto LBL_OUT; + } + dev_mgr = dev_mgr_from_dev( pdev ); + hcd = pdev->hcd; + + purb->completion = hub_power_on_port_completion; + purb->context = ( PVOID )dev_mgr; + purb->reference = ( ULONG )port_idx; + purb->pdev = pdev; + purb->pendp = &pdev->default_endp; + + psetup = ( PUSB_CTRL_SETUP_PACKET ) purb->setup_packet; + psetup->bmRequestType = 0x23; + psetup->bRequest = USB_REQ_SET_FEATURE; + psetup->wValue = USB_PORT_FEAT_POWER; + psetup->wIndex = ( WORD )port_idx; + psetup->wLength = 0; + + unlock_dev( pdev, FALSE ); + + status = hcd->hcd_submit_urb( hcd, pdev, purb->pendp, purb ); + + if( status != STATUS_PENDING ) + { + goto LBL_OUT; + } + return STATUS_PENDING; + + LBL_OUT: + + usb_free_mem( purb ); + return status; + +} + +void +hub_get_hub_desc_completion( +PURB purb, +PVOID pcontext +) +{ + PUSB_DEV_MANAGER dev_mgr; + PHUB2_EXTENSION hub_ext; + PUSB_DEV pdev; + LONG i; + PUSB_INTERFACE pif; + ULONG status; + LONG port_count; + PUSB_DRIVER pdriver; + DEV_HANDLE dev_handle; + + USE_IRQL; + + if( purb == NULL ) + { + return; + } + + dev_mgr = ( PUSB_DEV_MANAGER ) pcontext; + hub_ext = ( PHUB2_EXTENSION )purb->reference; + pdriver = ( PUSB_DRIVER )purb->pirp; + dev_handle = purb->endp_handle & 0xffff0000; + + if( pcontext == NULL || purb->reference == 0 ) + goto LBL_OUT; + + if( purb->status != STATUS_SUCCESS ) + { + goto LBL_OUT; + } + + // obtain the pointer to the dev + status = usb_query_and_lock_dev( dev_mgr, purb->endp_handle, &pdev ); + if( status != STATUS_SUCCESS ) + { + usb_unlock_dev( pdev ); + goto LBL_OUT; + } + // safe to release the pdev ref since we are in urb completion + usb_unlock_dev( pdev ); + + lock_dev( pdev, TRUE ); + if( dev_state( pdev ) == USB_DEV_STATE_ZOMB || + dev_mgr_set_driver( dev_mgr, dev_handle, pdriver, pdev ) == FALSE ) + { + unlock_dev( pdev, TRUE ); + goto LBL_OUT; + } + + //transit the state to configured + pdev->flags &= ~USB_DEV_STATE_MASK; + pdev->flags |= USB_DEV_STATE_CONFIGURED; + + pdev->dev_ext = ( PBYTE )hub_ext; + pdev->dev_ext_size = sizeof( HUB2_EXTENSION ); + + port_count = hub_ext->port_count = hub_ext->hub_desc.bNbrPorts; + hub_ext->pdev = pdev; + for( i = 0; i < pdev->usb_config->if_count; i++ ) + { + pif = &pdev->usb_config->interf[ i ]; + if( pif->pusb_if_desc->bInterfaceClass == USB_CLASS_HUB + && pif->pusb_if_desc->bInterfaceSubClass == 0 + && pif->pusb_if_desc->bInterfaceProtocol < 3 + && pif->pusb_if_desc->bNumEndpoints == 1 ) + { + hub_ext->pif = pif; + break; + } + } + for( i = 0; i < MAX_HUB_PORTS + 1; i++ ) + { + psq_init( ( PPORT_STATUS_QUEUE )hub_ext->port_status_queue ); + } + + hub_ext->multiple_tt = ( pif->pusb_if_desc->bInterfaceProtocol == 2 ); + + unlock_dev( pdev, TRUE ); + usb_free_mem( purb ); + + hub_start_int_request( pdev ); + + for( i = 0; i < port_count; i ++ ) + { + hub_power_on_port( pdev, ( UCHAR )( i + 1 ) ); + } + return; + + LBL_OUT: + if( purb ) + usb_free_mem( purb ); + + if( hub_ext ) + usb_free_mem( hub_ext ); + return; +} + +BOOL +hub_stop( +PUSB_DEV_MANAGER dev_mgr, +DEV_HANDLE dev_handle +) +{ + return TRUE; +} + +BOOL +hub_disconnect( +PUSB_DEV_MANAGER dev_mgr, +DEV_HANDLE dev_handle +) +{ + PUSB_DEV pdev; + //special use of usb_query and lock dev + if( usb_query_and_lock_dev( dev_mgr, dev_handle, &pdev ) != STATUS_SUCCESS ) + { + //will never be success, since the dev is already in zomb state + //at this point, the dev is valid, ref_count is of none use, + //no need to lock it + if( pdev ) + { + usb_free_mem( pdev->dev_ext ); + } + } + + return TRUE; +} + +static BOOL +hub_lock_unlock_tt( +PUSB_DEV pdev, +UCHAR port_idx, +UCHAR type, +BOOL lock +) +{ + PUSB_INTERFACE pif; + PHUB2_EXTENSION dev_ext; + PULONG pmap; + + USE_IRQL; + + if( pdev == NULL || port_idx > 127 ) + return FALSE; + + lock_dev( pdev, FALSE ); + if( dev_state( pdev ) == USB_DEV_STATE_ZOMB ) + { + unlock_dev( pdev, FALSE ); + return FALSE; + } + + dev_ext = hub_ext_from_dev( pdev ); + if( dev_ext == NULL ) + { + unlock_dev( pdev, FALSE ); + return FALSE; + } + if( type == USB_ENDPOINT_XFER_INT || \ + type == USB_ENDPOINT_XFER_ISOC ) + { + pmap = dev_ext->tt_status_map; + } + else if( type == USB_ENDPOINT_XFER_BULK || \ + type == USB_ENDPOINT_XFER_CONTROL ) + { + pmap = dev_ext->tt_bulk_map; + } + + if( lock ) + { + if( pmap[ port_idx >> 5 ] & ( 1 << port_idx ) ) + { + unlock_dev( pdev, FALSE ); + return FALSE; + } + pmap[ port_idx >> 5 ] |= ( 1 << port_idx ); + } + else + { + pmap[ port_idx >> 5 ] &= ~( 1 << port_idx ); + } + + unlock_dev( pdev, FALSE ); + return TRUE; +} + +BOOL +hub_lock_tt( +PUSB_DEV pdev, +UCHAR port_idx, +UCHAR type // transfer type +) +{ + return hub_lock_unlock_tt( pdev, port_idx, type, TRUE ); +} + +BOOL +hub_unlock_tt( +PUSB_DEV pdev, +UCHAR port_idx, +UCHAR type +) +{ + return hub_lock_unlock_tt( pdev, port_idx, type, FALSE ); +} + +VOID +hub_clear_tt_buffer_completion( +PURB purb, +PVOID context +) +{ + PUSB_CTRL_SETUP_PACKET psetup; + PURB_HS_PIPE_CONTENT pipe_content; + PHUB2_EXTENSION hub_ext; + PHCD hcd; + + if( purb == NULL || context == NULL ) + return; + + hub_ext = ( PHUB2_EXTENSION )context; + psetup = ( PUSB_CTRL_SETUP_PACKET )purb->setup_packet; + pipe_content = ( PURB_HS_PIPE_CONTENT )&purb->reference; + hub_unlock_tt( purb->pdev, ( UCHAR )psetup->wIndex, ( UCHAR )pipe_content->trans_type ); + usb_free_mem( purb ); + purb = NULL; + hcd = hub_ext->pdev->hcd; + + // let those blocked urbs ( sharing the same tt )have chance to be scheduled + if( hcd && usb2( hcd ) ) + hcd->hcd_submit_urb( hcd, NULL, NULL, NULL ); + + return; +} + +BOOL +hub_clear_tt_buffer( +PUSB_DEV pdev, +URB_HS_PIPE_CONTENT pipe_content, +UCHAR port_idx +) +// send CLEAR_TT_BUFFER to the hub +{ + PURB purb; + PUSB_CTRL_SETUP_PACKET psetup; + PHUB2_EXTENSION hub_ext; + PHCD hcd; + NTSTATUS status; + USE_IRQL; + + if( pdev == NULL ) + return FALSE; + + if( pipe_content.speed_high ) + return FALSE; + + lock_dev( pdev, FALSE ); + if( dev_state( pdev ) == USB_DEV_STATE_ZOMB ) + { + unlock_dev( pdev, FALSE ); + return FALSE; + } + + hub_ext = hub_ext_from_dev( pdev ); + if( hub_ext == NULL ) + { + unlock_dev( pdev, FALSE ); + return FALSE; + } + purb = usb_alloc_mem( NonPagedPool, sizeof( URB ) ); + RtlZeroMemory( purb, sizeof( URB ) ); + + if( purb == NULL ) + { + unlock_dev( pdev, FALSE ); + return STATUS_NO_MEMORY; + } + + purb->flags = 0; + purb->status = STATUS_SUCCESS; + purb->data_buffer = NULL; + purb->data_length = 0; // ( hub_ext->port_count + 7 ) / 8; + + // hub_if_from_dev( pdev, pif ); + purb->pendp = &pdev->default_endp; + purb->pdev = pdev; + + purb->completion = hub_clear_tt_buffer_completion; + purb->context = hub_ext; + purb->reference = *( ( PLONG )&pipe_content ); + + purb->pirp = NULL; + hcd = pdev->hcd; + + psetup = ( PUSB_CTRL_SETUP_PACKET )purb->setup_packet; + psetup->bmRequestType = 0x23; //host-device class other recepient + psetup->bRequest = HUB_REQ_CLEAR_TT_BUFFER; + psetup->wValue = ( USHORT )( ( pipe_content.endp_addr ) | ( pipe_content.dev_addr << 4 )| \ + ( pipe_content.trans_type << 10 ) | ( pipe_content.trans_dir << 15 ) ); + + if( hub_ext->multiple_tt ) + { + psetup->wIndex = ( USHORT )port_idx; + } + else + psetup->wIndex = 1; + + psetup->wLength = 0; + unlock_dev( pdev, FALSE ); + + status = hcd->hcd_submit_urb( hcd, pdev, purb->pendp, purb ); + if( status != STATUS_PENDING ) + { + usb_free_mem( purb ); + purb = NULL; + return FALSE; + } + return TRUE; +} + +BOOL +hub_event_clear_tt_buffer( +PUSB_DEV pdev, //always null. we do not use this param +ULONG event, +ULONG context, +ULONG param +) +{ + hub_clear_tt_buffer( pdev, *( ( PURB_HS_PIPE_CONTENT )&context ), ( UCHAR )param ); + return TRUE; +} + +VOID +hub_post_clear_tt_event( +PUSB_DEV pdev, +BYTE port_idx, +ULONG pipe +) +{ + PUSB_DEV_MANAGER dev_mgr; + PUSB_EVENT pevent; + USE_IRQL; + + dev_mgr = dev_mgr_from_dev( pdev ); + + KeAcquireSpinLock( &dev_mgr->event_list_lock, &old_irql ); + lock_dev( pdev, TRUE ); + if( dev_state( pdev ) == USB_DEV_STATE_ZOMB ) + { + unlock_dev( pdev, TRUE ); + KeReleaseSpinLock( &dev_mgr->event_list_lock, old_irql ); + return; + } + pevent = alloc_event( &dev_mgr->event_pool, 1 ); + if( pevent == NULL ) + { + unlock_dev( pdev, TRUE ); + KeReleaseSpinLock( &dev_mgr->event_list_lock, old_irql ); + TRAP(); + return; + } + + pevent->event = USB_EVENT_WAIT_RESET_PORT; + pevent->pdev = pdev; + pevent->context = pipe; + pevent->param = port_idx; + pevent->flags = USB_EVENT_FLAG_ACTIVE; + pevent->process_queue = event_list_default_process_queue; + pevent->process_event = hub_event_clear_tt_buffer; + pevent->pnext = NULL; + InsertTailList( &dev_mgr->event_list, &pevent->event_link ); + + unlock_dev( pdev, TRUE ); + KeReleaseSpinLock( &dev_mgr->event_list_lock, old_irql ); + + KeSetEvent( &dev_mgr->wake_up_event, 0, FALSE ); + return; +} + +BOOL +init_irp_list( +PIRP_LIST irp_list +) +{ + LONG i; + KeInitializeSpinLock( &irp_list->irp_list_lock ); + InitializeListHead( &irp_list->irp_busy_list ); + InitializeListHead( &irp_list->irp_free_list ); + irp_list->irp_list_element_array = usb_alloc_mem( NonPagedPool, sizeof( IRP_LIST_ELEMENT ) * MAX_IRP_LIST_SIZE ); + + if( irp_list->irp_list_element_array == NULL ) + return FALSE; + + RtlZeroMemory( irp_list->irp_list_element_array, sizeof( IRP_LIST_ELEMENT ) * MAX_IRP_LIST_SIZE ); + for( i = 0; i < MAX_IRP_LIST_SIZE; i++ ) + { + InsertTailList( &irp_list->irp_free_list, &irp_list->irp_list_element_array[ i ].irp_link ); + } + irp_list->irp_free_list_count = MAX_IRP_LIST_SIZE; + return TRUE; +} + +VOID +destroy_irp_list( +PIRP_LIST irp_list +) +{ + InitializeListHead( &irp_list->irp_busy_list ); + InitializeListHead( &irp_list->irp_free_list ); + usb_free_mem( irp_list->irp_list_element_array ); + irp_list->irp_list_element_array = NULL; + irp_list->irp_free_list_count = 0; + return; +} + +BOOL +add_irp_to_list( +PIRP_LIST irp_list, +PIRP pirp, +PURB purb +) +{ + KIRQL old_irql; + PIRP_LIST_ELEMENT pile; + + if( irp_list == NULL || pirp == NULL || purb == NULL ) + return FALSE; + + IoAcquireCancelSpinLock( &old_irql ); + KeAcquireSpinLockAtDpcLevel( &irp_list->irp_list_lock ); + + if( irp_list->irp_free_list_count == 0 ) + { + KeReleaseSpinLockFromDpcLevel( &irp_list->irp_list_lock ); + IoReleaseCancelSpinLock( old_irql ); + return FALSE; + } + pile = ( PIRP_LIST_ELEMENT )RemoveHeadList( &irp_list->irp_free_list ); + + pile->pirp = pirp; + pile->purb = purb; + + irp_list->irp_free_list_count--; + InsertTailList( &irp_list->irp_busy_list, &pile->irp_link ); + IoSetCancelRoutine( pirp, dev_mgr_cancel_irp ); + + KeReleaseSpinLockFromDpcLevel( &irp_list->irp_list_lock ); + IoReleaseCancelSpinLock( old_irql ); + return TRUE; +} + +PURB +remove_irp_from_list( +PIRP_LIST irp_list, +PIRP pirp, +PUSB_DEV_MANAGER dev_mgr //if dev_mgr is not NULL, the urb needs to be canceled +) +{ + PIRP_LIST_ELEMENT pile; + PLIST_ENTRY pthis, pnext; + PURB purb; + DEV_HANDLE endp_handle; + PUSB_DEV pdev; + PUSB_ENDPOINT pendp; + PHCD hcd; + + USE_IRQL; + + if( irp_list == NULL || pirp == NULL ) + return NULL; + + KeAcquireSpinLock( &irp_list->irp_list_lock, &old_irql ); + + if( irp_list->irp_free_list_count == MAX_IRP_LIST_SIZE ) + { + KeReleaseSpinLock( &irp_list->irp_list_lock, old_irql ); + return NULL; + } + + purb = NULL; + ListFirst( &irp_list->irp_busy_list, pthis ); + while( pthis ) + { + pile = struct_ptr( pthis, IRP_LIST_ELEMENT, irp_link ); + if( pile->pirp == pirp ) + { + purb = pile->purb; + pile->pirp = NULL; + pile->purb = NULL; + RemoveEntryList( pthis ); + InsertTailList( &irp_list->irp_free_list, pthis ); + irp_list->irp_free_list_count++; + break; + } + ListNext( &irp_list->irp_busy_list, pthis, pnext ); + pthis = pnext; + } + + if( purb == NULL ) + { + // not found + KeReleaseSpinLock( &irp_list->irp_list_lock, old_irql ); + return NULL; + } + + endp_handle = purb->endp_handle; + KeReleaseSpinLock( &irp_list->irp_list_lock, old_irql ); + + if( dev_mgr ) + { + // indicate we needs to cancel the urb, this condition happens only in cancel routine + // we should notice that even the hcd_cancel_urb is called, the irp may not be canceled + // if the urb does not exist in any queue of the host controller driver, indicating + // it is being processed by dpc. Thus, the dpc will certainly prevent the irp in + // completion from being canceled at the same time. On the other hand, if the + // hcd_cancel_urb succeeds, it either directly complete the irp or queue the dpc for + // irp completion. So, there won't be two simutaneous threads processing the same + // irp. + + if( usb_query_and_lock_dev( dev_mgr, endp_handle, &pdev ) != STATUS_SUCCESS ) + return NULL; + + lock_dev( pdev, TRUE ); + if( dev_state( pdev ) == USB_DEV_STATE_ZOMB ) + { + unlock_dev( pdev, FALSE ); + usb_unlock_dev( pdev ); + return NULL; + } + + hcd = pdev->hcd; + endp_from_handle( pdev, endp_handle, pendp ); + unlock_dev( pdev, TRUE ); + hcd->hcd_cancel_urb( hcd, pdev, pendp, purb ); + usb_unlock_dev( pdev ); + return NULL; + } + return purb; +} + +BOOL +irp_list_empty( +PIRP_LIST irp_list +) +{ + KIRQL old_irql; + BOOL ret; + KeAcquireSpinLock( &irp_list->irp_list_lock, &old_irql ); + ret = ( irp_list->irp_free_list_count == MAX_IRP_LIST_SIZE ); + KeReleaseSpinLock( &irp_list->irp_list_lock, old_irql ); + return ret; +} + +BOOL +irp_list_full( +PIRP_LIST irp_list +) +{ + KIRQL old_irql; + BOOL ret; + KeAcquireSpinLock( &irp_list->irp_list_lock, &old_irql ); + ret = ( irp_list->irp_free_list_count == 0 ); + KeReleaseSpinLock( &irp_list->irp_list_lock, old_irql ); + return ret; +} + + +VOID +zzz() +{}; diff --git a/reactos/drivers/usb/nt4compat/usbdriver/hub.h b/reactos/drivers/usb/nt4compat/usbdriver/hub.h new file mode 100644 index 00000000000..45a400a628b --- /dev/null +++ b/reactos/drivers/usb/nt4compat/usbdriver/hub.h @@ -0,0 +1,794 @@ + +#ifndef __HUB_H__ +#define __HUB_H__ + +#include "td.h" +//#include "hcd.h" +#include "ntddk.h" + +/* + * Hub Class feature numbers + */ +#define C_HUB_LOCAL_POWER 0 +#define C_HUB_OVER_CURRENT 1 + +/* + * Port feature numbers + */ +#define USB_PORT_FEAT_CONNECTION 0 +#define USB_PORT_FEAT_ENABLE 1 +#define USB_PORT_FEAT_SUSPEND 2 +#define USB_PORT_FEAT_OVER_CURRENT 3 +#define USB_PORT_FEAT_RESET 4 +#define USB_PORT_FEAT_POWER 8 +#define USB_PORT_FEAT_LOWSPEED 9 +#define USB_PORT_FEAT_C_CONNECTION 16 +#define USB_PORT_FEAT_C_ENABLE 17 +#define USB_PORT_FEAT_C_SUSPEND 18 +#define USB_PORT_FEAT_C_OVER_CURRENT 19 +#define USB_PORT_FEAT_C_RESET 20 + +/* wPortStatus bits */ +#define USB_PORT_STAT_CONNECTION 0x0001 +#define USB_PORT_STAT_ENABLE 0x0002 +#define USB_PORT_STAT_SUSPEND 0x0004 +#define USB_PORT_STAT_OVERCURRENT 0x0008 +#define USB_PORT_STAT_RESET 0x0010 +#define USB_PORT_STAT_POWER 0x0100 +#define USB_PORT_STAT_LOW_SPEED 0x0200 + +/* usb 2.0 features */ +#define USB_PORT_STAT_HIGH_SPEED 0x0400 +#define USB_PORT_STAT_PORT_TEST 0x0800 +#define USB_PORT_STAT_PORT_INDICATOR 0x1000 + +/* wPortChange bits */ +#define USB_PORT_STAT_C_CONNECTION 0x0001 +#define USB_PORT_STAT_C_ENABLE 0x0002 +#define USB_PORT_STAT_C_SUSPEND 0x0004 +#define USB_PORT_STAT_C_OVERCURRENT 0x0008 +#define USB_PORT_STAT_C_RESET 0x0010 + +/* wHubCharacteristics (masks) */ +#define HUB_CHAR_LPSM 0x0003 +#define HUB_CHAR_COMPOUND 0x0004 +#define HUB_CHAR_OCPM 0x0018 + +/* + *Hub Status & Hub Change bit masks + */ +#define HUB_STATUS_LOCAL_POWER 0x0001 +#define HUB_STATUS_OVERCURRENT 0x0002 + +#define HUB_CHANGE_LOCAL_POWER 0x0001 +#define HUB_CHANGE_OVERCURRENT 0x0002 + +#define HUB_DESCRIPTOR_MAX_SIZE 39 /* enough for 127 ports on a hub */ + +//event definitions + +#define MAX_EVENTS 128 +#define MAX_TIMER_SVCS 24 + +#define USB_EVENT_FLAG_ACTIVE 0x80000000 + +#define USB_EVENT_FLAG_QUE_TYPE 0x000000FF +#define USB_EVENT_FLAG_QUE_RESET 0x01 +#define USB_EVENT_FLAG_NOQUE 0x00 + +#define USB_EVENT_DEFAULT 0x00 //as a placeholder +#define USB_EVENT_INIT_DEV_MGR 0x01 +#define USB_EVENT_HUB_POLL 0x02 +#define USB_EVENT_WAIT_RESET_PORT 0x03 +#define USB_EVENT_CLEAR_TT_BUFFER 0x04 + +typedef VOID ( *PROCESS_QUEUE )( +PLIST_HEAD event_list, +struct _USB_EVENT_POOL *event_pool, +struct _USB_EVENT *usb_event, +struct _USB_EVENT *out_event +); + +typedef VOID ( *PROCESS_EVENT )( +PUSB_DEV dev, +ULONG event, +ULONG context, +ULONG param +); + +typedef struct _USB_EVENT +{ + LIST_ENTRY event_link; + ULONG flags; + ULONG event; + PUSB_DEV pdev; + ULONG context; + ULONG param; + struct _USB_EVENT *pnext; //vertical queue for serialized operation + PROCESS_EVENT process_event; + PROCESS_QUEUE process_queue; + +} USB_EVENT, *PUSB_EVENT; + +typedef struct _USB_EVENT_POOL +{ + PUSB_EVENT event_array; + LIST_HEAD free_que; + LONG free_count; + LONG total_count; + KSPIN_LOCK pool_lock; + +} USB_EVENT_POOL, *PUSB_EVENT_POOL; + +BOOL +init_event_pool( +PUSB_EVENT_POOL pool +); + +BOOL +free_event( +PUSB_EVENT_POOL pool, +PUSB_EVENT pevent +); //add qhs till pnext == NULL + +PUSB_EVENT +alloc_event( +PUSB_EVENT_POOL pool, +LONG count +); //null if failed + +BOOL +destroy_event_pool( +PUSB_EVENT_POOL pool +); + +VOID +lock_event_pool( +PUSB_EVENT_POOL pool +); + +VOID +unlock_event_pool( +PUSB_EVENT_POOL pool +); + +#define DEV_MGR_TIMER_INTERVAL_NS ( 10 * 1000 * 10 ) //unit 100 ns +#define DEV_MGR_TIMER_INTERVAL_MS 10 + +typedef VOID ( *TIMER_SVC_HANDLER )(PUSB_DEV dev, PVOID context); + +typedef struct _TIMER_SVC +{ + LIST_ENTRY timer_svc_link; + ULONG counter; + ULONG threshold; + ULONG context; + PUSB_DEV pdev; + TIMER_SVC_HANDLER func; + +} TIMER_SVC, *PTIMER_SVC; + +typedef struct _TIMER_SVC_POOL +{ + PTIMER_SVC timer_svc_array; + LIST_HEAD free_que; + LONG free_count; + LONG total_count; + KSPIN_LOCK pool_lock; + +} TIMER_SVC_POOL, *PTIMER_SVC_POOL; + +BOOL +init_timer_svc_pool( +PTIMER_SVC_POOL pool +); +BOOL +free_timer_svc( +PTIMER_SVC_POOL pool, +PTIMER_SVC ptimer +); + +PTIMER_SVC +alloc_timer_svc( +PTIMER_SVC_POOL pool, +LONG count +); //null if failed + +BOOL +destroy_timer_svc_pool( +PTIMER_SVC_POOL pool +); + +VOID +lock_timer_svc_pool( +PTIMER_SVC_POOL pool +); + +VOID +unlock_timer_svc_pool( +PTIMER_SVC_POOL pool +); + +#define MAX_IRP_LIST_SIZE 32 + +typedef struct _IRP_LIST_ELEMENT +{ + LIST_ENTRY irp_link; + PIRP pirp; + struct _URB *purb; + +} IRP_LIST_ELEMENT, *PIRP_LIST_ELEMENT; + +typedef struct _IRP_LIST +{ + KSPIN_LOCK irp_list_lock; + LIST_HEAD irp_busy_list; + LONG irp_free_list_count; + LIST_HEAD irp_free_list; + PIRP_LIST_ELEMENT irp_list_element_array; + +} IRP_LIST, *PIRP_LIST; + +BOOL +init_irp_list( +PIRP_LIST irp_list +); + +VOID +destroy_irp_list( +PIRP_LIST irp_list +); + +BOOL +add_irp_to_list( +PIRP_LIST irp_list, +PIRP pirp, +PURB purb +); + +PURB +remove_irp_from_list( +PIRP_LIST irp_list, +PIRP pirp, +struct _USB_DEV_MANAGER *dev_mgr +); + +BOOL +irp_list_empty( +PIRP_LIST irp_list +); + +BOOL +irp_list_full( +PIRP_LIST irp_list +); + +#define MAX_HUB_PORTS 8 +#define USB_HUB_INTERVAL 0xff + +#define USB_PORT_FLAG_STATE_MASK ( 0xf << 16 ) +#define USB_PORT_FLAG_ENABLE ( 0x1 << 16 ) +#define USB_PORT_FLAG_DISABLE ( 0x2 << 16 ) +#define USB_PORT_FLAG_DISCONNECT ( 0x3 << 16 ) + +#define USB_PORT_QUE_STATE_MASK 0xff // for detail, refer to document.txt +#define STATE_IDLE 0x00 +#define STATE_EXAMINE_STATUS_QUE 0x01 // set when post a event to examine the status queue +#define STATE_WAIT_STABLE 0x02 // set when a new connection comes and about to wait some ms +#define STATE_WAIT_RESET 0x03 // set when dev stable and about to reset, may pending in the event queue +#define STATE_WAIT_RESET_COMPLETE 0x04 // set when reset signal is about to assert on the port +#define STATE_WAIT_ADDRESSED 0x05 // set when reset complete and before address is assigned + +#define port_state( port_FLAG ) \ +( port_FLAG & USB_PORT_QUE_STATE_MASK ) + +#define set_port_state( port_FLAG, stATE ) \ +{ port_FLAG = ( port_FLAG & (~USB_PORT_QUE_STATE_MASK ) ) | ( stATE & USB_PORT_QUE_STATE_MASK ) ;} + +#define default_endp_handle( endp_HANDLE ) \ +( ( endp_HANDLE & 0xffff ) == 0xffff ) + +#define is_if_dev( pdev ) ( pdev->flags & USB_DEV_FLAG_IF_DEV ) + +#define is_composite_dev( pdev ) \ +( is_if_dev( pdev ) == FALSE \ +&& pdev->pusb_dev_desc->bDeviceClass == 0 \ +&& pdev->pusb_dev_desc->bDeviceSubClass == 0 ) + +#define dev_handle_from_dev( pdev ) ( pdev->dev_id << 16 ) + +#pragma pack( push, hub_align, 1 ) +typedef struct _USB_PORT_STATUS +{ + USHORT wPortStatus; + USHORT wPortChange; + +} USB_PORT_STATUS, *PUSB_PORT_STATUS; + + +typedef struct _USB_HUB_STATUS +{ + USHORT wHubStatus; + USHORT wHubChange; + +} USB_HUB_STATUS, *PUSB_HUB_STATUS; + + +typedef struct _USB_HUB_DESCRIPTOR +{ + BYTE bLength; + BYTE bDescriptorType; + BYTE bNbrPorts; + USHORT wHubCharacteristics; + BYTE bPwrOn2PwrGood; + BYTE bHubContrCurrent; + + /* DeviceRemovable and PortPwrCtrlMask want to be variable-length + bitmaps that hold max 256 entries, but for now they're ignored */ + BYTE bitmap[0]; +} USB_HUB_DESCRIPTOR, *PUSB_HUB_DESCRIPTOR; +#pragma pack( pop, hub_align ) + +typedef struct _PORT_STATUS_QUEUE +{ + USB_PORT_STATUS port_status[ 4 ]; + BYTE status_count; + ULONG port_flags; + +} PORT_STATUS_QUEUE, *PPORT_STATUS_QUEUE; + +VOID +psq_init( +PPORT_STATUS_QUEUE psq +); + +BOOL +psq_enqueue( +PPORT_STATUS_QUEUE psq, +ULONG status +); +ULONG +psq_outqueue( +PPORT_STATUS_QUEUE psq +); //return 0xffffffff if no element + +BOOL +psq_push( +PPORT_STATUS_QUEUE psq, +ULONG status +); + +#define psq_is_empty( pSQ ) ( ( pSQ )->status_count == 0 ) +#define psq_is_full( pSQ ) ( ( pSQ )->status_count == 4 ) +#define psq_count( psq ) ( ( psq )->status_count ) +#define psq_peek( psq, i ) \ +( ( ( ULONG )i ) < 3 ? ( psq )->port_status[ i ] : ( psq )->port_status[ 3 ] ) + +#define MAX_DEVS 127 +#define EHCI_MAX_ROOT_PORTS 15 + +typedef struct _HUB_EXTENSION +{ + + LONG port_count; + PUSB_DEV child_dev[ MAX_HUB_PORTS + 1 ]; + + PORT_STATUS_QUEUE port_status_queue[ MAX_HUB_PORTS + 1]; + PUSB_DEV pdev; + PUSB_INTERFACE pif; + BYTE int_data_buf[ 64 ]; + + USB_HUB_STATUS hub_status; + USB_PORT_STATUS port_status; //working data buf for get port feature + + USB_PORT_STATUS rh_port1_status; //working buf for get rh port1 feature + USB_PORT_STATUS rh_port2_status; //working buf for get rh port2 feature + + USB_HUB_DESCRIPTOR hub_desc; + +} HUB_EXTENSION, *PHUB_EXTENSION; + +typedef struct _HUB2_PORT_TT +{ + ULONG tt_busy : 1; + +} HUB2_PORT_TT, *PHUB2_PORT_TT; + +typedef struct _HUB2_EXTENSION +{ + + LONG port_count; + PUSB_DEV child_dev[ MAX_HUB_PORTS + 1 ]; + + PORT_STATUS_QUEUE port_status_queue[ MAX_HUB_PORTS + 1]; + + PUSB_DEV pdev; + PUSB_INTERFACE pif; + BYTE int_data_buf[ 32 ]; // for ports up to 127 + + USB_HUB_STATUS hub_status; + USB_PORT_STATUS port_status; //working data buf for get port feature + + USB_PORT_STATUS rh_port_status[ EHCI_MAX_ROOT_PORTS ]; //working buf for get rh ports feature + + UCHAR multiple_tt; // boolean + ULONG tt_status_map[ 4 ]; // bit map to indicate the indexed tt's periodic buffer busy or not + ULONG tt_bulk_map[ 4 ]; // bit map to indicate the indexed tt's bulk/control buffer busy or not + USB_HUB_DESCRIPTOR hub_desc; + +} HUB2_EXTENSION, *PHUB2_EXTENSION; + +typedef struct _CONNECT_DATA +{ + DEV_HANDLE dev_handle; + struct _USB_DRIVER *pdriver; + struct _USB_DEV_MANAGER *dev_mgr; + +} CONNECT_DATA, *PCONNECT_DATA; + +typedef BOOL ( *PDEV_CONNECT_EX )( PCONNECT_DATA init_param, DEV_HANDLE dev_handle ); +typedef BOOL ( *PDEV_CONNECT )( struct _USB_DEV_MANAGER *dev_mgr, DEV_HANDLE dev_handle ); +typedef BOOL ( *PDRVR_INIT )( struct _USB_DEV_MANAGER *dev_mgr, struct _USB_DRIVER *pdriver ); + +typedef struct _PNP_DISPATCH +{ + ULONG version; + PDEV_CONNECT_EX dev_connect; + PDEV_CONNECT dev_reserved; //currently we do not use this entry + PDEV_CONNECT dev_stop; + PDEV_CONNECT dev_disconnect; + +}PNP_DISPATCH, *PPNP_DISPATCH; + +#define USB_DRIVER_FLAG_IF_CAPABLE 0x80000000 +#define USB_DRIVER_FLAG_DEV_CAPABLE 0x40000000 + +typedef struct _USB_DRIVER_DESCRIPTION +{ + // Device Info + DWORD flags; + WORD vendor_id; // USB Vendor ID + WORD product_id; // USB Product ID. + WORD release_num; // Release Number of Device + + // Interface Info + BYTE config_val; // Configuration Value + BYTE if_num; // Interface Number + BYTE if_class; // Interface Class + BYTE if_sub_class; // Interface SubClass + BYTE if_protocol; // Interface Protocol + + // Driver Info + PBYTE driver_name; // Driver name for Name Registry + BYTE dev_class; // Device Class (from SampleStorageDeviceID.h) + BYTE dev_sub_class; // Device Subclass + BYTE dev_protocol; // Protocol Info. + +} USB_DRIVER_DESCRIPTION,*PUSB_DRIVER_DESCRIPTION; + +#define DEVMGR_MAX_DRIVERS 6 +#define RH_DRIVER_IDX 0 +#define HUB_DRIVER_IDX 1 +#define UMSS_DRIVER_IDX 2 +#define COMP_DRIVER_IDX 3 +#define GEN_DRIVER_IDX 4 +#define GEN_IF_DRIVER_IDX 5 + +typedef struct _USB_DRIVER +{ + USB_DRIVER_DESCRIPTION driver_desc; + PNP_DISPATCH disp_tbl; + + PBYTE driver_ext; + LONG driver_ext_size; + + PDRVR_INIT driver_init; + PDRVR_INIT driver_destroy; + +} USB_DRIVER, *PUSB_DRIVER; + +extern USB_DRIVER g_driver_list[ DEVMGR_MAX_DRIVERS ]; + +#define MAX_HCDS 8 +#define dev_mgr_from_hcd( hCD ) ( ( hCD )->hcd_get_dev_mgr( hCD ) ) +#define dev_mgr_from_dev( pdEV ) ( dev_mgr_from_hcd( pdEV->hcd ) ) + +typedef struct _USB_DEV_MANAGER +{ + //BYTE dev_addr_map[ MAX_DEVS / 8 ]; //one bit per dev + struct _HCD *hcd_array[ MAX_HCDS ]; + unsigned char hcd_count; + + KSPIN_LOCK dev_list_lock; + LIST_HEAD dev_list; + + //PDEVICE_EXTENSION pdev_ext; + + PVOID pthread; + BOOL term_flag; + KEVENT wake_up_event; + + KSPIN_LOCK event_list_lock; + LIST_HEAD event_list; + USB_EVENT_POOL event_pool; + + KTIMER dev_mgr_timer; + KDPC dev_mgr_timer_dpc; + KSPIN_LOCK timer_svc_list_lock; + LIST_HEAD timer_svc_list; + TIMER_SVC_POOL timer_svc_pool; + LONG timer_click; + IRP_LIST irp_list; + + PUSB_DRIVER driver_list; + LONG hub_count; + LIST_HEAD hub_list; //for reference only + + //statistics + LONG conn_count; //will also be used to assign device id + PDRIVER_OBJECT usb_driver_obj; //this driver object + LONG open_count; //increment when IRP_MJ_CREATE arrives + //and decrement when IRP_MJ_CLOSE arrives + +} USB_DEV_MANAGER, *PUSB_DEV_MANAGER; + +BOOL +dev_mgr_post_event( +PUSB_DEV_MANAGER dev_mgr, +PUSB_EVENT event +); + +BOOL +dev_mgr_init( +PUSB_DEV dev, //always null. we do not use this param +ULONG event, +ULONG dev_mgr +); + +VOID +dev_mgr_destroy( +PUSB_DEV_MANAGER dev_mgr +); + +VOID +dev_mgr_thread( +PVOID dev_mgr +); + +VOID +dev_mgr_timer_dpc_callback( +PKDPC Dpc, +PVOID DeferredContext, +PVOID SystemArgument1, +PVOID SystemArgument2 +); + +BOOL +dev_mgr_request_timer_svc( +PUSB_DEV_MANAGER dev_mgr, +PUSB_DEV pdev, +ULONG context, +ULONG due_time, +TIMER_SVC_HANDLER handler +); + +BYTE +dev_mgr_alloc_addr( +PUSB_DEV_MANAGER dev_mgr, +PHCD hcd +); + +BOOL +dev_mgr_free_addr( +PUSB_DEV_MANAGER dev_mgr, +PUSB_DEV pdev, +BYTE addr +); + +PUSB_DEV +dev_mgr_alloc_device( +PUSB_DEV_MANAGER dev_mgr, +PHCD hcd +); + +VOID +dev_mgr_free_device( +PUSB_DEV_MANAGER dev_mgr, +PUSB_DEV pdev +); + +VOID +dev_mgr_disconnect_dev( +PUSB_DEV pdev +); + +BOOL +dev_mgr_strobe( +PUSB_DEV_MANAGER dev_mgr +); + +NTSTATUS +dev_mgr_build_usb_config( +PUSB_DEV pdev, +PBYTE pbuf, +ULONG config_val, +LONG config_count +); + +UCHAR +dev_mgr_register_hcd( +PUSB_DEV_MANAGER dev_mgr, +PHCD hcd +); + +NTSTATUS +dev_mgr_dispatch( +IN PUSB_DEV_MANAGER dev_mgr, +IN PIRP irp +); + +BOOL +dev_mgr_register_irp( +PUSB_DEV_MANAGER dev_mgr, +PIRP pirp, +PURB purb +); + +PURB +dev_mgr_remove_irp( +PUSB_DEV_MANAGER dev_mgr, +PIRP pirp +); + +LONG +dev_mgr_score_driver_for_if( +PUSB_DEV_MANAGER dev_mgr, +PUSB_DRIVER pdriver, +PUSB_INTERFACE_DESC pif_desc +); + +BOOL +dev_mgr_set_driver( +PUSB_DEV_MANAGER dev_mgr, +DEV_HANDLE dev_handle, +PUSB_DRIVER pdriver, +PUSB_DEV pdev //if pdev != NULL, we use pdev instead if_handle +); + +BOOL +dev_mgr_set_if_driver( +PUSB_DEV_MANAGER dev_mgr, +DEV_HANDLE if_handle, +PUSB_DRIVER pdriver, +PUSB_DEV pdev //if pdev != NULL, we use pdev instead if_handle +); + +VOID +dev_mgr_release_hcd( +PUSB_DEV_MANAGER dev_mgr +); + +VOID +dev_mgr_start_hcd( +PUSB_DEV_MANAGER dev_mgr +); + +VOID +event_list_default_process_queue( +PLIST_HEAD event_list, +PUSB_EVENT_POOL event_pool, +PUSB_EVENT usb_event, +PUSB_EVENT out_event +); + +VOID +event_list_default_process_event( +PUSB_DEV dev, +ULONG event, +ULONG context, +ULONG param +); + +// root hub routines and definitions + +#define RH_INTERVAL ( USB_HUB_INTERVAL / DEV_MGR_TIMER_INTERVAL_MS ) + +BOOL +rh_driver_destroy( +PUSB_DEV_MANAGER dev_mgr, +PUSB_DRIVER pdriver +); + +BOOL +rh_driver_init( +PUSB_DEV_MANAGER dev_mgr, +PUSB_DRIVER pdriver +); + +BOOL +rh_destroy( +PUSB_DEV pdev +); + +VOID +rh_timer_svc( +PUSB_DEV dev, +PVOID context +); + +BOOL +rh_submit_urb( +PUSB_DEV pdev, +PURB urb +); + +BOOL +hub_init( +PUSB_DEV pdev +); + +BOOL +hub_destroy( +PUSB_DEV pdev +); + +BOOL +hub_lock_tt( +PUSB_DEV pdev, +UCHAR port_idx, +UCHAR type // transfer type +); + +BOOL +hub_unlock_tt( +PUSB_DEV pdev, +UCHAR port_idx, +UCHAR type +); + + +VOID +hub_post_clear_tt_event( +PUSB_DEV pdev, +BYTE port_idx, +ULONG pipe +); + +BOOL +compdev_driver_init( +PUSB_DEV_MANAGER dev_mgr, +PUSB_DRIVER pdriver +); + +BOOL +compdev_driver_destroy( +PUSB_DEV_MANAGER dev_mgr, +PUSB_DRIVER pdriver +); + +BOOL +gendrv_driver_init( +PUSB_DEV_MANAGER dev_mgr, +PUSB_DRIVER pdriver +); + +BOOL +gendrv_driver_destroy( +PUSB_DEV_MANAGER dev_mgr, +PUSB_DRIVER pdriver +); + +BOOL +gendrv_if_driver_init( +PUSB_DEV_MANAGER dev_mgr, +PUSB_DRIVER pdriver +); + +BOOL +gendrv_if_driver_destroy( +PUSB_DEV_MANAGER dev_mgr, +PUSB_DRIVER pdriver +); +#endif + + diff --git a/reactos/drivers/usb/nt4compat/usbdriver/td.c b/reactos/drivers/usb/nt4compat/usbdriver/td.c new file mode 100644 index 00000000000..a42fc8c6a45 --- /dev/null +++ b/reactos/drivers/usb/nt4compat/usbdriver/td.c @@ -0,0 +1,687 @@ +/** + * td.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 "td.h" +#include "debug.h" + +#define UHCI_MIN_TD_POOLS 4 + +BOOL +free_td_to_pool( +PUHCI_TD_POOL ptd_pool, +PUHCI_TD ptd +); //add tds till pnext == NULL + + +PUHCI_QH +alloc_qh( +PUHCI_QH_POOL pqh_pool +); //null if failed + +BOOL +init_td_pool( +PUHCI_TD_POOL ptd_pool +) +{ + int i, pages; + PTD_EXTENSION ptde; + PHYSICAL_ADDRESS phys_addr; + + if (ptd_pool == NULL) + return FALSE; + + if( ptd_pool->padapter == NULL) + return FALSE; + + pages = sizeof( UHCI_TD ) * UHCI_MAX_POOL_TDS / PAGE_SIZE; + RtlZeroMemory( ptd_pool->td_array, sizeof( ptd_pool->td_array ) ); + RtlZeroMemory( ptd_pool->logic_addr, sizeof( ptd_pool->logic_addr ) ); + + for( i = 0; i < pages; i++ ) + { + ptd_pool->td_array[ i ] = + HalAllocateCommonBuffer( + ptd_pool->padapter, + PAGE_SIZE, + &ptd_pool->logic_addr[ i ], + FALSE); + if( ptd_pool->td_array[ i ] == NULL ) + goto failed; + } + + ptd_pool->tde_array = ( PTD_EXTENSION )usb_alloc_mem( + NonPagedPool, + sizeof( TD_EXTENSION ) * UHCI_MAX_POOL_TDS ); + + if (ptd_pool->tde_array == NULL) + goto failed; + + for( i = 0; i < pages; i++ ) + { + RtlZeroMemory( ptd_pool->td_array[ i ], PAGE_SIZE ); + } + + RtlZeroMemory( + ptd_pool->tde_array, + sizeof( TD_EXTENSION ) * UHCI_MAX_POOL_TDS ); + + ptde = ptd_pool->tde_array; + ptd_pool->free_count = 0; + ptd_pool->total_count = UHCI_MAX_POOL_TDS; + InitializeListHead( &ptd_pool->free_que); + + for( i = 0; i < UHCI_MAX_POOL_TDS; i++ ) + { + //link tde and the td one by one, fixed since this init + ptd_pool->td_array[ i >> 7 ][ i & 0x7f ].ptde = &ptde[ i ]; + ptde[ i ].ptd = &ptd_pool->td_array[ i >> 7 ][ i & 0x7f ]; + ptde[ i ].flags = UHCI_ITEM_FLAG_TD; + ptd_pool->td_array[ i >> 7 ][ i & 0x7f ].phy_addr = ptd_pool->logic_addr[ i >> 7 ].LowPart + ( i & 0x7f ) * sizeof( UHCI_TD ); + ptd_pool->td_array[ i >> 7 ][ i & 0x7f ].pool = ptd_pool; + ptd_pool->td_array[ i >> 7 ][ i & 0x7f ].purb = NULL; + free_td_to_pool( ptd_pool, &ptd_pool->td_array[ i >> 7 ][ i & 0x7f ]); + + } + return TRUE; + + failed: + for( i = 0; i < pages; i++ ) + { + if( ptd_pool->td_array[ i ] ) + { + HalFreeCommonBuffer( ptd_pool->padapter, + PAGE_SIZE, + ptd_pool->logic_addr[ i ], + ptd_pool->td_array[ i ], + FALSE); + ptd_pool->td_array[ i ] = NULL; + ptd_pool->logic_addr[ i ].QuadPart = 0; + } + } + + if( ptd_pool->tde_array ) + usb_free_mem( ptd_pool->tde_array); + + uhci_dbg_print( DBGLVL_MAXIMUM, ( "init_td_pool(): failed to init the td pool\n" ) ); + TRAP(); + + ptd_pool->free_count = ptd_pool->total_count = 0; + return FALSE; +} + +BOOL +free_td_to_pool( +PUHCI_TD_POOL ptd_pool, +PUHCI_TD ptd +) //add tds till pnext == NULL +{ + if( ptd_pool == NULL || ptd == NULL ) + { + return FALSE; + } + + ptd->link = ptd->status = ptd->info = ptd->buffer = 0; + ptd->purb = NULL; + ptd_pool->free_count++; + + InsertTailList( &ptd_pool->free_que, &ptd->ptde->vert_link); + + return TRUE; + +} +// qh routines +PUHCI_TD +alloc_td_from_pool( +PUHCI_TD_POOL ptd_pool +) //null if failed +{ + PTD_EXTENSION ptde; + PLIST_ENTRY temp; + + if( ptd_pool == NULL ) + return FALSE; + + if( IsListEmpty( &ptd_pool->free_que ) ) + return FALSE; + + temp = RemoveHeadList( &ptd_pool->free_que ); + + if( temp == NULL ) + return FALSE; + + ptde = struct_ptr( temp, TD_EXTENSION, vert_link ); + + ptd_pool->free_count--; + + InitializeListHead( &ptde->vert_link ); + InitializeListHead( &ptde->hori_link ); + + return ptde->ptd; + +} + +BOOL +is_pool_free( +PUHCI_TD_POOL pool +) //test whether the pool is all free +{ + if( pool == NULL ) + return FALSE; + + if( pool->free_count == pool->total_count ) + return TRUE; + + return FALSE; +} + +BOOL +is_pool_empty( +PUHCI_TD_POOL pool +) +{ + if( pool == NULL ) + return FALSE; + + return ( pool->free_count == 0 ); +} + +BOOL +destroy_td_pool( +PUHCI_TD_POOL ptd_pool +) +{ + int i, pages; + PADAPTER_OBJECT padapter; //we need this garbage for allocation + + padapter = ptd_pool->padapter; + + pages = sizeof( UHCI_TD ) * UHCI_MAX_POOL_TDS / PAGE_SIZE; + if( ptd_pool && ptd_pool->padapter ) + { + usb_free_mem( ptd_pool->tde_array ); + ptd_pool->tde_array = NULL; + for( i = 0; i < pages; i++ ) + { + if( ptd_pool->td_array[ i ] ) + { + HalFreeCommonBuffer( ptd_pool->padapter, + PAGE_SIZE, + ptd_pool->logic_addr[ i ], + ptd_pool->td_array[ i ], + FALSE); + ptd_pool->td_array[ i ] = NULL; + ptd_pool->logic_addr[ i ].QuadPart = 0; + } + } + RtlZeroMemory( ptd_pool, sizeof( UHCI_TD_POOL ) ); + ptd_pool->padapter = padapter; + ptd_pool->free_count = ptd_pool->total_count = 0; + } + else + return FALSE; + + return TRUE; +} + +BOOL +init_td_pool_list( +PUHCI_TD_POOL_LIST pool_list, +PADAPTER_OBJECT padapter +) +{ + int i; + RtlZeroMemory( pool_list, sizeof( UHCI_TD_POOL_LIST)); + InitializeListHead( &pool_list->busy_pools ); + InitializeListHead( &pool_list->free_pools ); + + pool_list->free_count = UHCI_MAX_TD_POOLS; + pool_list->free_tds = 0; + + for( i = 0; i < UHCI_MAX_TD_POOLS; i++) + { + pool_list->pool_array[i].padapter = padapter; + InsertTailList( &pool_list->free_pools, &pool_list->pool_array[i].pool_link); + } + + KeInitializeSpinLock( &pool_list->pool_lock ); + return expand_pool_list( pool_list, UHCI_MIN_TD_POOLS ); +} + +BOOL +destroy_td_pool_list( +PUHCI_TD_POOL_LIST pool_list +) +{ + PUHCI_TD_POOL pool; + while( IsListEmpty( &pool_list->busy_pools) == FALSE ) + { + pool = (PUHCI_TD_POOL) RemoveHeadList( &pool_list->busy_pools); + destroy_td_pool( pool ); + } + + RtlZeroMemory( pool_list, sizeof( UHCI_TD_POOL_LIST )); + return TRUE; +} + +BOOL +expand_pool_list( +PUHCI_TD_POOL_LIST pool_list, +LONG pool_count +) //private +{ + PUHCI_TD_POOL pool; + int i; + + if( IsListEmpty( &pool_list->free_pools) == TRUE ) + return FALSE; + + if( pool_list->free_count < pool_count ) + return FALSE; + + for( i = 0; i < pool_count; i++ ) + { + pool = (PUHCI_TD_POOL) RemoveHeadList( &pool_list->free_pools); + + if( init_td_pool( pool ) == FALSE ) + { + //reverse the allocation + InsertHeadList( &pool_list->free_pools, &pool->pool_link ); + // collect_garbage( pool_list ); + return FALSE; + } + + InsertTailList( &pool_list->busy_pools, &pool->pool_link ); + pool_list->free_tds += UHCI_MAX_POOL_TDS; + pool_list->free_count--; + } + return TRUE; +} + +BOOL +collect_garbage( +PUHCI_TD_POOL_LIST pool_list +) +{ + PLIST_ENTRY prev, next; + + // no garbage + if( pool_list->free_count >= UHCI_MAX_TD_POOLS - UHCI_MIN_TD_POOLS ) + return TRUE; + + ListFirstPrev( &pool_list->busy_pools, prev); + ListNext( &pool_list->busy_pools, prev, next); + + while( next && next != &pool_list->busy_pools) + { + if( is_pool_free( (PUHCI_TD_POOL)next ) ) + { + RemoveEntryList( next ); + destroy_td_pool( ( PUHCI_TD_POOL )next ); + InsertTailList( &pool_list->free_pools, next); + pool_list->free_count++; + pool_list->free_tds -= UHCI_MAX_POOL_TDS; + ListNext( &pool_list->busy_pools, prev, next); + if( pool_list->free_count >= UHCI_MAX_TD_POOLS - UHCI_MIN_TD_POOLS ) + break; + } + else + { + prev = next; + ListNext( &pool_list->busy_pools, prev, next); + } + } + return TRUE; + +} + +//private +LONG +get_num_free_tds( +PUHCI_TD_POOL_LIST pool_list +) +{ + return pool_list->free_tds; +} + +//private +LONG +get_max_free_tds( +PUHCI_TD_POOL_LIST pool_list +) +{ + return pool_list->free_tds + pool_list->free_count * UHCI_MAX_POOL_TDS; +} + +BOOL +free_td( +PUHCI_TD_POOL_LIST pool_list, +PUHCI_TD ptd +) //add tds till pnext == NULL +{ + if( pool_list == NULL || ptd == NULL ) + return FALSE; + + if( free_td_to_pool( ptd->pool, ptd ) == FALSE ) + return FALSE; + + pool_list->free_tds++; + + if( is_pool_free( ptd->pool ) ) + { + collect_garbage( pool_list ); + } + return TRUE; +} + +PUHCI_TD +alloc_td( +PUHCI_TD_POOL_LIST pool_list +) //null if failed +{ + PLIST_ENTRY prev, next; + PUHCI_TD new_td; + + if( pool_list == NULL ) + return NULL; + + if( pool_list->free_tds == 0 ) + { + if( expand_pool_list( pool_list, 1 ) == FALSE ) + return NULL; + } + + ListFirst( &pool_list->busy_pools, prev ); + + while( prev && prev != &pool_list->busy_pools ) + { + if( is_pool_empty( (PUHCI_TD_POOL) prev ) == FALSE ) + { + new_td = alloc_td_from_pool( (PUHCI_TD_POOL) prev ); + + if( new_td == NULL ) + TRAP(); + + pool_list->free_tds--; + + return new_td; + } + + ListNext( &pool_list->busy_pools, prev, next); + prev = next; + } + + return NULL; +} + +PUHCI_TD +alloc_tds( +PUHCI_TD_POOL_LIST pool_list, +LONG count +) +{ + //return value is a list of tds, vert_link chain. + + LONG i; + PUHCI_TD ptd, pnext; + + if( pool_list == NULL || count <= 0 ) + return NULL; + + if( count >= get_max_free_tds( pool_list ) ) + return NULL; + + ptd = alloc_td( pool_list ); + + for( i = 1; i < count; i ++ ) + { + pnext = alloc_td( pool_list ); + + if( pnext ) + { + InsertTailList( &ptd->ptde->vert_link, &pnext->ptde->vert_link ); + } + else + TRAP(); + } + + uhci_dbg_print( DBGLVL_MEDIUM, ("alloc_tds(): td pool-list free_tds=0x%x, free pools=0x%x\n", \ + pool_list->free_tds, + pool_list->free_count ) ); + + return ptd; + +} + +VOID +free_tds( +PUHCI_TD_POOL_LIST pool_list, +PUHCI_TD ptd +) +{ + PUHCI_TD ptofree; + PLIST_ENTRY pthis; + + if( pool_list == NULL || ptd == NULL ) + return; + + while( IsListEmpty( &ptd->ptde->vert_link ) == FALSE ) + { + pthis = RemoveHeadList( &ptd->ptde->vert_link ) + ptofree = ( ( PTD_EXTENSION)pthis )->ptd; + free_td( pool_list, ptofree ); + } + + free_td( pool_list, ptd ); + return; +} + + + +BOOL +can_transfer( +PUHCI_TD_POOL_LIST pool_list, +LONG td_count +) +{ + if( td_count > get_max_free_tds( pool_list ) ) + return FALSE; + + return TRUE; +} + +VOID +lock_td_pool( +PUHCI_TD_POOL_LIST pool_list, +BOOL at_dpc +) +{ + //if( !at_dpc ) + // KeAcquireSpinLock( &pool_list->pool_lock ); + //else + // KeAcquireSpinLockAtDpcLevel( &pool_list->pool_lock ); +} + +VOID +unlock_td_pool( +PUHCI_TD_POOL_LIST pool_list, +BOOL at_dpc +) +{ + //if( !at_dpc ) + // KeReleaseSpinLock( &pool_list->pool_lock ); + //else + // KeReleaseSpinLockFromDpcLevel( &pool_list->pool_lock ); +} + +BOOL +init_qh_pool( +PUHCI_QH_POOL pqh_pool, +PADAPTER_OBJECT padapter +) +{ + PQH_EXTENSION pqhe; + LONG i; + + if( pqh_pool == NULL || padapter == NULL ) + return FALSE; + + pqh_pool->padapter = padapter; + + pqh_pool->qhe_array = (PQH_EXTENSION)usb_alloc_mem( + NonPagedPool, + sizeof(QH_EXTENSION) * UHCI_MAX_POOL_QHS); + + if (pqh_pool->qhe_array == NULL) + return FALSE; + + pqh_pool->qh_array = \ + (PUHCI_QH)HalAllocateCommonBuffer( + padapter, + sizeof(UHCI_QH) * UHCI_MAX_POOL_QHS, + &pqh_pool->logic_addr, + FALSE); + + if( pqh_pool->qh_array == NULL ) + { + usb_free_mem( pqh_pool->qhe_array ); + pqh_pool->qhe_array = NULL; + return FALSE; + } + + pqhe = pqh_pool->qhe_array; + + pqh_pool->free_count = 0; + pqh_pool->total_count = UHCI_MAX_POOL_TDS; + + KeInitializeSpinLock( &pqh_pool->pool_lock); + InitializeListHead( &pqh_pool->free_que); + + + for(i = 0; i < UHCI_MAX_POOL_QHS; i++) + { + pqh_pool->qh_array[i].pqhe = &pqhe[i]; + pqhe[i].pqh = &pqh_pool->qh_array[i]; + + pqh_pool->qh_array[i].phy_addr = ( pqh_pool->logic_addr.LowPart + ( sizeof( UHCI_QH) * i ) ) | UHCI_PTR_QH; + //pqh_pool->qh_array[i].reserved = 0; + + //always breadth first + pqhe[i].flags = UHCI_ITEM_FLAG_QH; + + free_qh( pqh_pool, &pqh_pool->qh_array[i] ); + + } + return TRUE; + +} + +BOOL +free_qh( +PUHCI_QH_POOL pqh_pool, +PUHCI_QH pqh +) //add qhs till pnext == NULL +{ + if( pqh_pool == NULL || pqh == NULL) + return FALSE; + + pqh->link = pqh->element = 0; + pqh->pqhe->purb = NULL; + InsertTailList( &pqh_pool->free_que, &pqh->pqhe->vert_link); + pqh_pool->free_count ++; + + return TRUE; +} + +PUHCI_QH +alloc_qh( +PUHCI_QH_POOL pqh_pool +) //null if failed +{ + PQH_EXTENSION pqhe; + + if( pqh_pool == NULL ) + return FALSE; + + if( IsListEmpty( &pqh_pool->free_que)) + return FALSE; + + pqhe = ( PQH_EXTENSION )RemoveHeadList( &pqh_pool->free_que ); + + if( pqhe ) + { + InitializeListHead( &pqhe->hori_link ); + InitializeListHead( &pqhe->vert_link ); + return pqhe->pqh; + } + return NULL; + +} + +BOOL +destroy_qh_pool( +PUHCI_QH_POOL pqh_pool +) +{ + int i; + + if(pqh_pool) + { + usb_free_mem(pqh_pool->qhe_array); + + HalFreeCommonBuffer( pqh_pool->padapter, + sizeof(UHCI_QH) * UHCI_MAX_POOL_QHS, + pqh_pool->logic_addr, + pqh_pool->qh_array, + FALSE); + + RtlZeroMemory( pqh_pool, sizeof( UHCI_QH_POOL ) ); + + } + else + return FALSE; + + return TRUE; +} + +VOID +lock_qh_pool( +PUHCI_QH_POOL pool, +BOOL at_dpc +) +{ + //if( !at_dpc ) + // KeAcquireSpinLock( &pool->pool_lock ); + //else + // KeAcquireSpinLockAtDpcLevel( &pool->pool_lock ); +} + +VOID +unlock_qh_pool( +PUHCI_QH_POOL pool, +BOOL at_dpc +) +{ + //if( !at_dpc ) + // KeReleaseSpinLock( &pool->pool_lock ); + //else + // KeReleaseSpinLockFromDpcLevel( &pool->pool_lock ); +} + diff --git a/reactos/drivers/usb/nt4compat/usbdriver/td.h b/reactos/drivers/usb/nt4compat/usbdriver/td.h new file mode 100644 index 00000000000..72c74a6c67b --- /dev/null +++ b/reactos/drivers/usb/nt4compat/usbdriver/td.h @@ -0,0 +1,1031 @@ +/* + * NOTE!!!: part of the code is pasted from linux/driver/usb/uhci.h + */ + +#ifndef __UHCI_H__ +#define __UHCI_H__ + +#include "usb.h" +#include "ntddk.h" +#include "hcd.h" + +#define BOOL ULONG +#define LIST_HEAD LIST_ENTRY +#define PLIST_HEAD PLIST_ENTRY +#define BYTE UCHAR +#define PBYTE PUCHAR +#define WORD USHORT +#define DWORD ULONG + +#define LOWORD(l) ( (WORD) ( ( l ) & 0xffff ) ) +#define HIWORD(l) ( (WORD) ( ( l ) >> 16 ) ) + +#define PCI_MAX_FUNCTIONS 8 + + +#define UHCI_MAX_POOL_TDS 1024 //8 pages of 4k +#define UHCI_MAX_POOL_QHS 256 //1 page of 4k +#define UHCI_MAX_ANT_TDS 0x20e82 + +#define UHCI_MAX_TD_POOLS 8 +#define UHCI_MAX_TDS_PER_TRANSFER UHCI_MAX_POOL_TDS + +#define UHCI_FULL_SPEED_BANDWIDHT ( 12 * 1000 * 1000 ) +#define UHCI_LOW_SPEED_BANDWIDHT ( 15 * 100 * 1000 ) +/* + * Universal Host Controller Interface data structures and defines + */ + +/* Command register */ +#define USBCMD 0 +#define USBCMD_RS 0x0001 /* Run/Stop */ +#define USBCMD_HCRESET 0x0002 /* Host reset */ +#define USBCMD_GRESET 0x0004 /* Global reset */ +#define USBCMD_EGSM 0x0008 /* Global Suspend Mode */ +#define USBCMD_FGR 0x0010 /* Force Global Resume */ +#define USBCMD_SWDBG 0x0020 /* SW Debug mode */ +#define USBCMD_CF 0x0040 /* Config Flag (sw only) */ +#define USBCMD_MAXP 0x0080 /* Max Packet (0 = 32, 1 = 64) */ + +/* Status register */ +#define USBSTS 2 +#define USBSTS_USBINT 0x0001 /* Interrupt due to IOC */ +#define USBSTS_ERROR 0x0002 /* Interrupt due to error */ +#define USBSTS_RD 0x0004 /* Resume Detect */ +#define USBSTS_HSE 0x0008 /* Host System Error - basically PCI problems */ +#define USBSTS_HCPE 0x0010 /* Host Controller Process Error - the scripts were buggy */ +#define USBSTS_HCH 0x0020 /* HC Halted */ + +/* Interrupt enable register */ +#define USBINTR 4 +#define USBINTR_TIMEOUT 0x0001 /* Timeout/CRC error enable */ +#define USBINTR_RESUME 0x0002 /* Resume interrupt enable */ +#define USBINTR_IOC 0x0004 /* Interrupt On Complete enable */ +#define USBINTR_SP 0x0008 /* Short packet interrupt enable */ + +#define USBFRNUM 6 +#define USBFLBASEADD 8 +#define USBSOF 12 + +/* USB port status and control registers */ +#define USBPORTSC1 16 +#define USBPORTSC2 18 +#define USBPORTSC_CCS 0x0001 /* Current Connect Status ("device present") */ +#define USBPORTSC_CSC 0x0002 /* Connect Status Change */ +#define USBPORTSC_PE 0x0004 /* Port Enable */ +#define USBPORTSC_PEC 0x0008 /* Port Enable Change */ +#define USBPORTSC_LS 0x0020 /* Line Status */ +#define USBPORTSC_RD 0x0040 /* Resume Detect */ +#define USBPORTSC_LSDA 0x0100 /* Low Speed Device Attached */ +#define USBPORTSC_PR 0x0200 /* Port Reset */ +#define USBPORTSC_SUSP 0x1000 /* Suspend */ + +/* Legacy support register */ +#define USBLEGSUP 0xc0 +#define USBLEGSUP_DEFAULT 0x2000 /* only PIRQ enable set */ + +#define UHCI_NULL_DATA_SIZE 0x7FF /* for UHCI controller TD */ + +#define UHCI_PTR_BITS 0x000F +#define UHCI_PTR_TERM 0x0001 +#define UHCI_PTR_QH 0x0002 +#define UHCI_PTR_DEPTH 0x0004 + +#define UHCI_MAX_FRAMES 1024 /* in the frame list [array] */ +#define UHCI_MAX_SOF_NUMBER 2047 /* in an SOF packet */ +#define CAN_SCHEDULE_FRAMES 1000 /* how far future frames can be scheduled */ + + +//from linux's uhci.h +#define UHCI_MAX_SKELTDS 10 +#define skel_int1_td skel_td[0] +#define skel_int2_td skel_td[1] +#define skel_int4_td skel_td[2] +#define skel_int8_td skel_td[3] +#define skel_int16_td skel_td[4] +#define skel_int32_td skel_td[5] +#define skel_int64_td skel_td[6] +#define skel_int128_td skel_td[7] +#define skel_int256_td skel_td[8] +#define skel_term_td skel_td[9] /* To work around PIIX UHCI bug */ + +#define UHCI_MAX_SKELQHS 4 +#define skel_ls_control_qh skel_qh[0] +#define skel_hs_control_qh skel_qh[1] +#define skel_bulk_qh skel_qh[2] +#define skel_term_qh skel_qh[3] + + + +struct URB; + +/* + * for TD : + */ +#define TD_CTRL_SPD (1 << 29) /* Short Packet Detect */ +#define TD_CTRL_C_ERR_MASK (3 << 27) /* Error Counter bits */ +#define TD_CTRL_C_ERR_SHIFT 27 +#define TD_CTRL_LS (1 << 26) /* Low Speed Device */ +#define TD_CTRL_IOS (1 << 25) /* Isochronous Select */ +#define TD_CTRL_IOC (1 << 24) /* Interrupt on Complete */ +#define TD_CTRL_ACTIVE (1 << 23) /* TD Active */ +#define TD_CTRL_STALLED (1 << 22) /* TD Stalled */ +#define TD_CTRL_DBUFERR (1 << 21) /* Data Buffer Error */ +#define TD_CTRL_BABBLE (1 << 20) /* Babble Detected */ +#define TD_CTRL_NAK (1 << 19) /* NAK Received */ +#define TD_CTRL_CRCTIMEO (1 << 18) /* CRC/Time Out Error */ +#define TD_CTRL_BITSTUFF (1 << 17) /* Bit Stuff Error */ +#define TD_CTRL_ACTLEN_MASK 0x7FF /* actual length, encoded as n - 1 */ + +#define TD_CTRL_ANY_ERROR (TD_CTRL_STALLED | TD_CTRL_DBUFERR | \ + TD_CTRL_BABBLE | TD_CTRL_CRCTIMEO | TD_CTRL_BITSTUFF) + +#define uhci_status_bits(ctrl_sts) (ctrl_sts & 0xFE0000) +#define uhci_actual_length(ctrl_sts) ((ctrl_sts + 1) & TD_CTRL_ACTLEN_MASK) /* 1-based */ + +/* + * for TD : (a.k.a. Token) + */ +#define TD_TOKEN_TOGGLE 19 +#define TD_PID 0xFF + +#define uhci_maxlen(token) ((token) >> 21) +#define uhci_expected_length(info) (((info >> 21) + 1) & TD_CTRL_ACTLEN_MASK) /* 1-based */ +#define uhci_toggle(token) (((token) >> TD_TOKEN_TOGGLE) & 1) +#define uhci_endpoint(token) (((token) >> 15) & 0xf) +#define uhci_devaddr(token) (((token) >> 8) & 0x7f) +#define uhci_devep(token) (((token) >> 8) & 0x7ff) +#define uhci_packetid(token) ((token) & 0xff) +#define uhci_packetout(token) (uhci_packetid(token) != USB_PID_IN) +#define uhci_packetin(token) (uhci_packetid(token) == USB_PID_IN) + +/* + * The documentation says "4 dwords for hardware, 4 dwords for software". + * + * That's silly, the hardware doesn't care. The hardware only cares that + * the hardware words are 16-byte aligned, and we can have any amount of + * sw space after the TD entry as far as I can tell. + * + * But let's just go with the documentation, at least for 32-bit machines. + * On 64-bit machines we probably want to take advantage of the fact that + * hw doesn't really care about the size of the sw-only area. + * + * Alas, not anymore, we have more than 4 dwords for software, woops. + * Everything still works tho, surprise! -jerdfelt + */ + +#define UHCI_ITEM_FLAG_TYPE 0x3 +#define UHCI_ITEM_FLAG_TD 0x0 +#define UHCI_ITEM_FLAG_QH 0x1 + +#define offsetof( s, m ) ( ( ULONG )( &( ( s* )0 )->m ) ) + +#define struct_ptr( meMBER_ptr, stRUCT_name, meMBER_name ) \ +( ( stRUCT_name* ) ( ( (CHAR*)( meMBER_ptr ) ) - offsetof( stRUCT_name, meMBER_name ) ) ) + +#define dev_state( pdEV ) ( ( pdEV )->flags & USB_DEV_STATE_MASK ) + +#define dev_class( pdev ) ( pdev->flags & USB_DEV_CLASS_MASK ) + +#define uhci_from_hcd( hCD ) ( struct_ptr( ( hCD ), UHCI_DEV, hcd_interf ) ) +#define uhci_from_dev( dEV ) ( struct_ptr( ( dEV->hcd ), UHCI_DEV, hcd_interf ) ) + +typedef struct _TD_EXTENSION +{ + LIST_ENTRY vert_link; // urb will use this to link qh and tds + LIST_ENTRY hori_link; + ULONG flags; + struct _UHCI_TD *ptd; //link to is companion td, constant since initialized + +} TD_EXTENSION, *PTD_EXTENSION; + +typedef struct _UHCI_TD +{ + /* Hardware fields */ + ULONG link; + ULONG status; + ULONG info; + ULONG buffer; + + /* Software fields */ + ULONG phy_addr; + PTD_EXTENSION ptde; + struct _URB *purb; + struct _UHCI_TD_POOL *pool; //pool this td belongs to + +} UHCI_TD, *PUHCI_TD; + +typedef struct _UHCI_TD_POOL +{ + + LIST_ENTRY pool_link; //link to next and prev td pool + PUHCI_TD td_array[ sizeof( UHCI_TD ) * UHCI_MAX_POOL_TDS / PAGE_SIZE ]; //would be allcated in common buffer + PHYSICAL_ADDRESS logic_addr[ sizeof( UHCI_TD ) * UHCI_MAX_POOL_TDS / PAGE_SIZE ]; //logical addr of the array + + PTD_EXTENSION tde_array; + LIST_ENTRY free_que; //list of free tds + LONG free_count; //free tds in this pool + LONG total_count; //total count of the tds + PADAPTER_OBJECT padapter; + +} UHCI_TD_POOL, *PUHCI_TD_POOL; + +BOOL +init_td_pool( +PUHCI_TD_POOL pool +); + +BOOL +free_td_to_pool( +PUHCI_TD_POOL pool, +PUHCI_TD ptd +); //add tds till pnext == NULL + +PUHCI_TD +alloc_td_from_pool( +PUHCI_TD_POOL ptd_pool +); //null if failed] + +BOOL +is_pool_free( +PUHCI_TD_POOL pool +); //test whether the pool is all free + +BOOL +is_pool_full( +PUHCI_TD_POOL pool +); + +BOOL +destroy_td_pool( +PUHCI_TD_POOL pool +); + +typedef struct _UHCI_TD_POOL_LIST +{ + LIST_ENTRY busy_pools; + KSPIN_LOCK pool_lock; + LONG free_tds; //free tds in all the pool + LONG free_count; //free pool count + LIST_ENTRY free_pools; + UHCI_TD_POOL pool_array[UHCI_MAX_TD_POOLS]; //max transfer is 640k + +} UHCI_TD_POOL_LIST, *PUHCI_TD_POOL_LIST; + +BOOL +init_td_pool_list( +PUHCI_TD_POOL_LIST pool_list, +PADAPTER_OBJECT padapter +); + +BOOL +destroy_td_pool_list( +PUHCI_TD_POOL_LIST pool_list +); + +BOOL +expand_pool_list( +PUHCI_TD_POOL_LIST pool_list, +LONG pool_count +); //private + +BOOL +collect_garbage( +PUHCI_TD_POOL_LIST pool_list +); + +static LONG +get_free_tds( +PUHCI_TD_POOL_LIST pool_list +); //private + +static LONG +get_max_free_tds( +PUHCI_TD_POOL_LIST pool_list +); //private + +BOOL +can_transfer( +PUHCI_TD_POOL_LIST pool_list, +LONG td_count); + +BOOL +free_td( +PUHCI_TD_POOL_LIST pool_list, +PUHCI_TD ptd +); //add tds till pnext == NULL + +PUHCI_TD +alloc_td( +PUHCI_TD_POOL_LIST pool_list +); //null if failed + +VOID +lock_td_pool( +PUHCI_TD_POOL_LIST pool_list, +BOOL at_dpc +); + +VOID +unlock_td_pool( +PUHCI_TD_POOL_LIST pool_list, +BOOL at_dpc +); + +typedef struct _UHCI_QH +{ + /* Hardware fields */ + ULONG link; // Next queue + ULONG element; // Queue element pointer + + /* Software fields */ + ULONG phy_addr; //constant since initialized + struct _QH_EXTENSION *pqhe; + +} UHCI_QH, *PUHCI_QH; + +typedef struct _QH_EXTENSION +{ + + LIST_ENTRY vert_link; + LIST_ENTRY hori_link; + ULONG flags; + PUHCI_QH pqh; //constant since initialized + struct _URB *purb; + +} QH_EXTENSION, *PQH_EXTENSION; + +typedef struct _UHCI_QH_POOL +{ + + PUHCI_QH qh_array; + PHYSICAL_ADDRESS logic_addr; //logical addr of the array + + PQH_EXTENSION qhe_array; + LIST_ENTRY free_que; + LONG free_count; + LONG total_count; + KSPIN_LOCK pool_lock; + PADAPTER_OBJECT padapter; //we need this garbage for allocation + +} UHCI_QH_POOL, *PUHCI_QH_POOL; + + +BOOL +init_qh_pool( +PUHCI_QH_POOL pool, +PADAPTER_OBJECT padapter +); + +BOOL +free_qh( +PUHCI_QH_POOL pool, +PUHCI_QH ptd +); //add qhs till pnext == NULL + +PUHCI_QH +alloc_qh( +PUHCI_QH_POOL pool +); //null if failed + +BOOL +destroy_qh_pool( +PUHCI_QH_POOL pool +); + +VOID +lock_qh_pool( +PUHCI_QH_POOL pool, +BOOL at_dpc +); + +VOID +unlock_qh_pool( +PUHCI_QH_POOL pool, +BOOL at_dpc +); + +/* + * Search tree for determining where fits in the + * skelqh[] skeleton. + * + * An interrupt request should be placed into the slowest skelqh[] + * which meets the interval/period/frequency requirement. + * An interrupt request is allowed to be faster than but not slower. + * + * For a given , this function returns the appropriate/matching + * skelqh[] index value. + * + * NOTE: For UHCI, we don't really need int256_qh since the maximum interval + * is 255 ms. However, we do need an int1_qh since 1 is a valid interval + * and we should meet that frequency when requested to do so. + * This will require some change(s) to the UHCI skeleton. + */ +static int __interval_to_skel(int interval) +{ + if (interval < 16) { + if (interval < 4) { + if (interval < 2) + return 0; /* int1 for 0-1 ms */ + return 1; /* int2 for 2-3 ms */ + } + if (interval < 8) + return 2; /* int4 for 4-7 ms */ + return 3; /* int8 for 8-15 ms */ + } + if (interval < 64) { + if (interval < 32) + return 4; /* int16 for 16-31 ms */ + return 5; /* int32 for 32-63 ms */ + } + if (interval < 128) + return 6; /* int64 for 64-127 ms */ + return 7; /* int128 for 128-255 ms (Max.) */ +} + +#define USB_ENDP_FLAG_BUSY_MASK 0x0000ff00 +#define USB_ENDP_FLAG_STAT_MASK 0xff +#define USB_ENDP_FLAG_STALL 0x01 +#define USB_ENDP_FLAG_DATATOGGLE 0x80000000 +#define USB_ENDP_FLAG_DEFAULT_ENDP 0x40000000 + +#define usb_endp_busy_count( peNDP ) \ +( ( ( peNDP )->flags & USB_ENDP_FLAG_BUSY_MASK ) >> 8 ) + +#define usb_endp_busy_count_inc( peNDP ) \ +( peNDP->flags = ( ( ( ( usb_endp_busy_count( peNDP ) + 1 ) << 8 ) & USB_ENDP_FLAG_BUSY_MASK ) | \ +( ( peNDP )->flags & ~USB_ENDP_FLAG_BUSY_MASK ) ) ) + +#define usb_endp_busy_count_dec( peNDP ) \ +( peNDP->flags = ( ( ( ( usb_endp_busy_count( peNDP ) - 1 ) << 8 ) & USB_ENDP_FLAG_BUSY_MASK )| \ +( ( peNDP )->flags & ~USB_ENDP_FLAG_BUSY_MASK ) ) ) + +typedef struct _USB_ENDPOINT +{ + ULONG flags; //toggle | busy-count | stall | default-endp; busy count( usually 1 when busy, may be greater if iso endp ) + LIST_ENTRY urb_list; //pending urb queue + + struct _USB_INTERFACE *pusb_if; + struct _USB_ENDPOINT_DESC *pusb_endp_desc; + +} USB_ENDPOINT, *PUSB_ENDPOINT; + +#define MAX_ENDPS_PER_IF 10 + +typedef struct _USB_INTERFACE +{ + UCHAR endp_count; + USB_ENDPOINT endp[MAX_ENDPS_PER_IF]; + + + struct _USB_CONFIGURATION *pusb_config; + PUSB_INTERFACE_DESC pusb_if_desc; + + struct _USB_DRIVER *pif_drv; //for hub_dev use + PVOID if_ext; + LONG if_ext_size; + UCHAR altif_count; + LIST_ENTRY altif_list; + + +} USB_INTERFACE, *PUSB_INTERFACE; + +#define MAX_INTERFACES_PER_CONFIG 4 +#define MAX_CONFIGS_PER_DEV 4 + +typedef struct _USB_CONFIGURATION +{ + //only for active configuration + struct _USB_CONFIGURATION_DESC *pusb_config_desc; + UCHAR if_count; + USB_INTERFACE interf[MAX_INTERFACES_PER_CONFIG]; + struct _USB_DEV *pusb_dev; + +} USB_CONFIGURATION, *PUSB_CONFIGURATION; + +#define USE_IRQL \ +KIRQL _pending_endp_lock_old_irql, _pending_endp_list_lock_old_irql, _dev_lock_old_irql, old_irql; + +#define USB_DEV_STATE_MASK ( 0xff << 8 ) +#define USB_DEV_STATE_POWERED ( 0x01 << 8 ) +#define USB_DEV_STATE_RESET ( 0x02 << 8 ) +#define USB_DEV_STATE_ADDRESSED ( 0x03 << 8 ) +#define USB_DEV_STATE_FIRST_CONFIG ( 0x04 << 8 ) +#define USB_DEV_STATE_RECONFIG ( 0x05 << 8 ) +#define USB_DEV_STATE_CONFIGURED ( 0x06 << 8 ) +#define USB_DEV_STATE_SUSPENDED ( 0x07 << 8 ) +#define USB_DEV_STATE_BEFORE_ZOMB ( 0x08 << 8 ) +#define USB_DEV_STATE_ZOMB ( 0x09 << 8 ) + +#define USB_DEV_CLASS_MASK ( 0xff << 16 ) +#define USB_DEV_CLASS_HUB ( USB_CLASS_HUB << 16 ) +#define USB_DEV_CLASS_MASSSTOR ( USB_CLASS_MASS_STORAGE << 16 ) +#define USB_DEV_CLASS_ROOT_HUB ( ( USB_CLASS_VENDOR_SPEC - 2 ) << 16 ) +#define USB_DEV_CLASS_SCANNER ( ( USB_DEV_CLASS_ROOT_HUB - 1 ) << 16 ) + +#define USB_DEV_FLAG_HIGH_SPEED 0x20 // high speed dev for usb2.0 +#define USB_DEV_FLAG_LOW_SPEED 0x40 // note: this bit is shared in urb->pipe +#define USB_DEV_FLAG_IF_DEV 0x01 // this dev is a virtual dev, that is a interface + +#define lock_dev( pdev, at_dpc ) \ +{\ + KIRQL cur_irql;\ + cur_irql = KeGetCurrentIrql();\ + if( cur_irql == DISPATCH_LEVEL )\ + {\ + KeAcquireSpinLockAtDpcLevel( &pdev->dev_lock );\ + _dev_lock_old_irql = DISPATCH_LEVEL;\ + }\ + else if( cur_irql < DISPATCH_LEVEL )\ + KeAcquireSpinLock( &pdev->dev_lock, &_dev_lock_old_irql );\ + else\ + TRAP();\ +} + +#define unlock_dev( pdev, from_dpc ) \ +{\ + if( _dev_lock_old_irql == DISPATCH_LEVEL )\ + KeReleaseSpinLockFromDpcLevel( &pdev->dev_lock );\ + else if( _dev_lock_old_irql < DISPATCH_LEVEL )\ + KeReleaseSpinLock( &pdev->dev_lock, _dev_lock_old_irql );\ + else\ + TRAP();\ +} + +typedef struct _USB_DEV +{ + LIST_ENTRY dev_link; //for dev-list + + KSPIN_LOCK dev_lock; + PDEVICE_OBJECT dev_obj; + ULONG flags; //class | cur_state | low speed + LONG ref_count; //client count + + UCHAR dev_addr; //usb addr + ULONG dev_id; //will be used to compose dev handle + + struct _USB_DEV *parent_dev; + UCHAR port_idx; //parent hub's port idx, to which the dev attached + + struct _HCD *hcd; //point to the hcd the dev belongs to + + USB_ENDPOINT default_endp; //control endp. its interfac pointer is to the first interface + + LONG desc_buf_size; + PUCHAR desc_buf; + struct _USB_DEVICE_DESC *pusb_dev_desc; + + UCHAR active_config_idx; + PUSB_CONFIGURATION usb_config; //the active configuration + + struct _USB_DRIVER *dev_driver; + PVOID dev_ext; + LONG dev_ext_size; + + LONG time_out_count; //default pipe error counter, three time-outs will cause the dev not function + LONG error_count; //usb transfer error counter for statics only + +} USB_DEV, *PUSB_DEV; + +// pending endpoint pool definitions + +#define UHCI_MAX_PENDING_ENDPS 32 + +typedef struct _UHCI_PENDING_ENDP +{ + LIST_ENTRY endp_link; + PUSB_ENDPOINT pendp; + +} UHCI_PENDING_ENDP, *PUHCI_PENDING_ENDP; + +typedef struct _UHCI_PENDING_ENDP_POOL +{ + PUHCI_PENDING_ENDP pending_endp_array; + LIST_ENTRY free_que; + LONG free_count; + LONG total_count; + KSPIN_LOCK pool_lock; + +} UHCI_PENDING_ENDP_POOL, *PUHCI_PENDING_ENDP_POOL; + +BOOL +init_pending_endp_pool( +PUHCI_PENDING_ENDP_POOL pool +); + +BOOL +free_pending_endp( +PUHCI_PENDING_ENDP_POOL pool, +PUHCI_PENDING_ENDP pending_endp +); + +PUHCI_PENDING_ENDP +alloc_pending_endp( +PUHCI_PENDING_ENDP_POOL pool, +LONG count +); + +BOOL +destroy_pending_endp_pool( +PUHCI_PENDING_ENDP_POOL pool +); + +// pool type is PUHCI_PENDING_ENDP_POOL +#define lock_pending_endp_pool( pool ) \ +{\ + KeAcquireSpinLock( &pool->pool_lock, &_pending_endp_lock_old_irql );\ +} + +#define unlock_pending_endp_pool( pool ) \ +{\ + KeReleaseSpinLock( &pool->pool_lock, &_pending_endp_lock_old_irql );\ +} + + +// end of pending endpoint pool +typedef struct _FRAME_LIST_CPU_ENTRY +{ + LIST_ENTRY td_link; + +} FRAME_LIST_CPU_ENTRY, *PFRAME_LIST_CPU_ENTRY; + +#define uhci_public_res_lock pending_endp_list_lock +#define uhci_status( _uhci_ ) ( READ_PORT_USHORT( ( PUSHORT )( ( _uhci_ )->port_base + USBSTS ) ) ) + +typedef struct _UHCI +{ + PHYSICAL_ADDRESS uhci_reg_base; // io space + BOOL port_mapped; + PBYTE port_base; + + PHYSICAL_ADDRESS io_buf_logic_addr; + PBYTE io_buf; + + PHYSICAL_ADDRESS frame_list_logic_addr; + + KSPIN_LOCK frame_list_lock; //run at DIRQL + PULONG frame_list; + PFRAME_LIST_CPU_ENTRY frame_list_cpu; + LIST_HEAD urb_list; //active urb-list + PUHCI_TD skel_td[UHCI_MAX_SKELTDS]; + PUHCI_QH skel_qh[UHCI_MAX_SKELQHS]; //skeltons + + + + UHCI_TD_POOL_LIST td_pool; + UHCI_QH_POOL qh_pool; + + + //for iso and int bandwidth claim, bandwidth schedule + KSPIN_LOCK pending_endp_list_lock; //lock to access the following two + LIST_HEAD pending_endp_list; + UHCI_PENDING_ENDP_POOL pending_endp_pool; + PLONG frame_bw; + LONG fsbr_cnt; //used to record number of fsbr users + + KTIMER reset_timer; //used to reset the host controller + + //struct _USB_DEV_MANAGER dev_mgr; //it is in hcd_interf + struct _DEVICE_EXTENSION *pdev_ext; + + PUSB_DEV root_hub; //root hub + HCD hcd_interf; + +} UHCI_DEV, *PUHCI_DEV; + +#define lock_pending_endp_list( list_lock ) \ +{\ + KeAcquireSpinLock( list_lock, &_pending_endp_list_lock_old_irql );\ +} + +#define unlock_pending_endp_list( list_lock ) \ +{\ + KeReleaseSpinLock( list_lock, _pending_endp_list_lock_old_irql );\ +} +typedef struct _UHCI_INTERRUPT +{ + ULONG level; + ULONG vector; + ULONG affinity; + +} UHCI_INTERRUPT, *PUHCI_INTERRUPT; + +typedef struct _UHCI_PORT +{ + PHYSICAL_ADDRESS Start; + ULONG Length; + +} UHCI_PORT, *PUHCI_PORT; + +typedef NTSTATUS ( *PDISPATCH_ROUTINE )( PDEVICE_OBJECT dev_obj, PIRP irp ); + +#define NTDEV_TYPE_HCD 1 +#define NTDEV_TYPE_CLIENT_DEV 2 + +typedef struct _DEVEXT_HEADER +{ + ULONG type; + PDISPATCH_ROUTINE dispatch; + PDRIVER_STARTIO start_io; + + struct _USB_DEV_MANAGER *dev_mgr; //mainly for use by cancel irp + +} DEVEXT_HEADER, *PDEVEXT_HEADER; + +typedef struct _DEVICE_EXTENSION +{ + //struct _USB_DEV_MANAGER *pdev_mgr; + DEVEXT_HEADER dev_ext_hdr; + PDEVICE_OBJECT pdev_obj; + PDRIVER_OBJECT pdrvr_obj; + PUHCI_DEV uhci; + + //device resources + PADAPTER_OBJECT padapter; + ULONG map_regs; + PCM_RESOURCE_LIST res_list; + ULONG pci_addr; // bus number | slot number | funciton number + UHCI_INTERRUPT res_interrupt; + UHCI_PORT res_port; + + PKINTERRUPT uhci_int; + KDPC uhci_dpc; + +} DEVICE_EXTENSION, *PDEVICE_EXTENSION; + +//helper macro +#define ListFirst( heAD, firST) \ +{\ + if( IsListEmpty( ( heAD ) ) )\ + firST = NULL;\ + else\ + firST = ( heAD )->Flink;\ +} + +#define ListNext( heAD, curreNT, neXT) \ +{\ + if( IsListEmpty( ( heAD ) ) == FALSE )\ + {\ + neXT = (curreNT)->Flink;\ + if( neXT == heAD )\ + neXT = NULL;\ + }\ + else\ + neXT = NULL;\ +} + +#define ListPrev( heAD, curreNT, prEV) \ +{\ + if( IsListEmpty( ( heAD ) ) == FALSE )\ + {\ + prEV = (curreNT)->Blink;\ + if( prEV == heAD )\ + prEV = NULL;\ + else\ + prEV = NULL;\ +} + +#define ListFirstPrev( heAD, firST) \ +{\ + if( IsListEmpty( ( heAD ) ) )\ + firST = NULL;\ + else\ + firST = ( heAD )->Blink;\ +} + +#define MergeList( liST1, liST2 )\ +{\ + PLIST_ENTRY taIL1, taIL2;\ + if( IsListEmpty( liST2 ) == TRUE )\ + {\ + InsertTailList( liST1, liST2 );\ + }\ + else if( IsListEmpty( liST1 ) == TRUE )\ + {\ + InsertTailList( liST2, liST1 );\ + }\ + else\ + {\ + ListFirstPrev( liST1, taIL1 );\ + ListFirstPrev( liST2, taIL2 );\ +\ + taIL1->Flink = ( liST2 );\ + ( liST2 )->Blink = taIL1;\ +\ + taIL2->Flink = ( liST1 );\ + ( liST1 )->Blink = taIL2;\ + }\ +} + +PUHCI_TD +alloc_tds( +PUHCI_TD_POOL_LIST pool_list, +LONG count +); + +VOID +free_tds( +PUHCI_TD_POOL_LIST pool_list, +PUHCI_TD ptd +); + +BOOL +uhci_init( +PUHCI_DEV uhci, +PADAPTER_OBJECT padapter +); + +BOOL +uhci_destroy( +PUHCI_DEV uhci +); + +// funcitons exported to dev-manager +BOOL +uhci_add_device( +PUHCI_DEV uhci, +PUSB_DEV dev +); + +BOOL +uhci_remove_device( +PUHCI_DEV uhci, +PUSB_DEV dev +); + +//helpers +static VOID +uhci_dpc_callback( +PKDPC dpc, +PVOID context, +PVOID sysarg1, +PVOID sysarg2 +); + +static VOID +uhci_flush_adapter_buf() +{ +#ifdef _X86 + __asm invd; +#endif +} + +BOOL +uhci_submit_urb( +PUHCI_DEV uhci, +PUSB_DEV pdev, +PUSB_ENDPOINT pendp, +struct _URB *urb +); + +//must have dev_lock acquired +static NTSTATUS +uhci_internal_submit_bulk( +PUHCI_DEV uhci, +struct _URB *urb +); + +static NTSTATUS +uhci_internal_submit_iso( +PUHCI_DEV uhci, +struct _URB *urb +); + +static NTSTATUS +uhci_internal_submit_ctrl( +PUHCI_DEV uhci, +struct _URB *urb +); + +static NTSTATUS +uhci_internal_submit_int( +PUHCI_DEV uhci, +struct _URB *urb +); + +static BOOL +uhci_remove_bulk_from_schedule( +PUHCI_DEV uhci, +struct _URB *urb +); + +#define uhci_remove_ctrl_from_schedule uhci_remove_bulk_from_schedule + +static BOOL +uhci_remove_iso_from_schedule( +PUHCI_DEV uhci, +struct _URB *urb +); + +static BOOL +uhci_remove_int_from_schedule( +PUHCI_DEV uhci, +struct _URB *urb +); + +static BOOL +uhci_remove_urb_from_schedule( +PUHCI_DEV uhci, +struct _URB *urb +); + +static BOOL +uhci_is_xfer_finished( //will set urb error code here +struct _URB *urb +); + +static NTSTATUS +uhci_set_error_code( +struct _URB *urb, +ULONG raw_status +); + +static BOOL +uhci_insert_tds_qh( +PUHCI_QH pqh, +PUHCI_TD td_chain +); + +static BOOL +uhci_insert_qh_urb( +struct _URB *urb, +PUHCI_QH qh_chain +); + +static BOOL +uhci_insert_urb_schedule( +PUHCI_DEV uhci, +struct _URB *urb +); + +static BOOL +uhci_claim_bandwidth( +PUHCI_DEV uhci, +struct _URB *urb, +BOOL claim_bw +); + +static BOOL +uhci_process_pending_endp( +PUHCI_DEV uhci +); + +NTSTATUS +uhci_cancel_urb( +PUHCI_DEV uhci, +PUSB_DEV pdev, +PUSB_ENDPOINT endp, +struct _URB *urb +); + +VOID +uhci_generic_urb_completion( +struct _URB *urb, +PVOID context +); + +// the following are NT driver definitions + +// NT device name +#define UHCI_DEVICE_NAME "\\Device\\UHCI" + +// File system device name. When you execute a CreateFile call to open the +// device, use "\\.\GpdDev", or, given C's conversion of \\ to \, use +// "\\\\.\\GpdDev" + +#define DOS_DEVICE_NAME "\\DosDevices\\UHCI" + + +#define CLR_RH_PORTSTAT( port_idx, x ) \ +{\ + PUSHORT addr; \ + addr = ( PUSHORT )( uhci->port_base + port_idx ); \ + status = READ_PORT_USHORT( addr ); \ + status = ( status & 0xfff5 ) & ~( x ); \ + WRITE_PORT_USHORT( addr, ( USHORT )status ); \ +} + +#define SET_RH_PORTSTAT( port_idx, x ) \ +{\ + PUSHORT addr; \ + addr = ( PUSHORT )( uhci->port_base + port_idx ); \ + status = READ_PORT_USHORT( addr ); \ + status = ( status & 0xfff5 ) | ( x ); \ + WRITE_PORT_USHORT( addr, ( USHORT )status ); \ +} + +//this is for dispatch routine +#define EXIT_DISPATCH( nTstatUs, iRp)\ +{\ + if( nTstatUs != STATUS_PENDING)\ + {\ + iRp->IoStatus.Status = nTstatUs;\ + IoCompleteRequest( iRp, IO_NO_INCREMENT);\ + return nTstatUs;\ + }\ + IoMarkIrpPending( iRp);\ + return nTstatUs;\ +} + +#endif + diff --git a/reactos/drivers/usb/nt4compat/usbdriver/uhci.c b/reactos/drivers/usb/nt4compat/usbdriver/uhci.c new file mode 100644 index 00000000000..9b19ea2de5a --- /dev/null +++ b/reactos/drivers/usb/nt4compat/usbdriver/uhci.c @@ -0,0 +1,4222 @@ +/** + * uhci.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 "uhciver.h" +#include "td.h" +#include "hub.h" +#include "debug.h" +#include "stdio.h" +#include "usb.h" +#include + +//---------------------------------------------------------- +// uhci routines +//#define DEMO + +#ifdef INCLUDE_EHCI + +#define rh_port1_status rh_port_status[ 1 ] +#define rh_port2_status rh_port_status[ 2 ] + +extern PDEVICE_OBJECT +ehci_probe( +PDRIVER_OBJECT drvr_obj, +PUNICODE_STRING reg_path, +PUSB_DEV_MANAGER dev_mgr +); + +#endif + +#define DEFAULT_ENDP( enDP ) \ +( enDP->flags & USB_ENDP_FLAG_DEFAULT_ENDP ) + +#define dev_from_endp( enDP ) \ +( DEFAULT_ENDP( enDP )\ + ? ( ( PUSB_DEV )( enDP )->pusb_if )\ + : ( ( enDP )->pusb_if->pusb_config->pusb_dev ) ) + +#define endp_state( enDP ) ( ( enDP )->flags & USB_ENDP_FLAG_STAT_MASK ) + +#define endp_num( enDP ) \ +( DEFAULT_ENDP( enDP )\ + ? 0 \ + : ( ( enDP )->pusb_endp_desc->bEndpointAddress & 0x0f ) ) + +#define endp_dir( enDP ) \ +( DEFAULT_ENDP( enDP )\ + ? 0L\ + : ( ( enDP )->pusb_endp_desc->bEndpointAddress & USB_DIR_IN ) ) + +#define dev_set_state( pdEV, staTE ) \ +( pdEV->flags = ( ( pdEV )->flags & ( ~USB_DEV_STATE_MASK ) ) | ( staTE ) ) + +#define endp_max_packet_size( enDP ) \ +( DEFAULT_ENDP( enDP )\ + ? ( ( ( PUSB_DEV )enDP->pusb_if )->pusb_dev_desc ? \ + ( ( PUSB_DEV )enDP->pusb_if )->pusb_dev_desc->bMaxPacketSize0\ + : 8 )\ + : enDP->pusb_endp_desc->wMaxPacketSize ) + + +#define release_adapter( padapTER ) \ +{\ + ( ( padapTER ) ); \ +} + +#define get_int_idx( _urb, _idx ) \ +{\ + UCHAR interVAL;\ + interVAL = ( UCHAR )( ( _urb )->pipe >> 24 );\ + for( _idx = 1; _idx < 9; _idx++ )\ + {\ + interVAL >>= 1;\ + if( !interVAL )\ + break;\ + }\ + _idx --;\ +} + +#define uhci_insert_urb_to_schedule( uHCI, pURB, rET ) \ +{\ + SYNC_PARAM sync_param;\ + sync_param.uhci = uHCI;\ + sync_param.context = pURB;\ +\ + rET = KeSynchronizeExecution( uHCI->pdev_ext->uhci_int, uhci_sync_insert_urb_schedule, &sync_param );\ +} + +//declarations +typedef struct +{ + PUHCI_DEV uhci; + PVOID context; + ULONG ret; + +} SYNC_PARAM, *PSYNC_PARAM; + +PDEVICE_OBJECT +uhci_alloc( +PDRIVER_OBJECT drvr_obj, +PUNICODE_STRING reg_path, +ULONG bus_addr, +PUSB_DEV_MANAGER dev_mgr +); + +BOOL +uhci_init_schedule( +PUHCI_DEV uhci, +PADAPTER_OBJECT padapter +); + +BOOL +uhci_release( +PDEVICE_OBJECT pdev +); + +static VOID +uhci_stop( +PUHCI_DEV uhci +); + +BOOL +uhci_destroy_schedule( +PUHCI_DEV uhci +); + +BOOLEAN +uhci_sync_insert_urb_schedule( +PVOID context +); + +VOID +uhci_init_hcd_interface( +PUHCI_DEV uhci +); + +NTSTATUS +uhci_rh_submit_urb( +PUSB_DEV rh, +PURB purb +); + +NTSTATUS +uhci_dispatch_irp( +IN PDEVICE_OBJECT DeviceObject, +IN PIRP irp +); + +extern VOID +rh_timer_svc_reset_port_completion( +PUSB_DEV dev, +PVOID context +); + +extern VOID +rh_timer_svc_int_completion( +PUSB_DEV dev, +PVOID context +); + +ULONG debug_level = DBGLVL_MAXIMUM; +PDRIVER_OBJECT usb_driver_obj = NULL; +extern USB_DEV_MANAGER g_dev_mgr; + +//pending endpoint pool funcs +VOID +uhci_wait_ms( +PUHCI_DEV uhci, +LONG ms +) +{ + LARGE_INTEGER lms; + if( ms <= 0 ) + return; + + lms.QuadPart = -10 * ms; + KeSetTimer( &uhci->reset_timer, lms, NULL ); + + KeWaitForSingleObject( + &uhci->reset_timer, + Executive, + KernelMode, + FALSE, + NULL ); + + return; +} + +BOOL +init_pending_endp_pool( +PUHCI_PENDING_ENDP_POOL pool +) +{ + int i; + if( pool == NULL ) + return FALSE; + + pool->pending_endp_array = usb_alloc_mem( NonPagedPool, sizeof( UHCI_PENDING_ENDP ) * UHCI_MAX_PENDING_ENDPS ); + InitializeListHead( &pool->free_que ); + pool->free_count = 0; + pool->total_count = UHCI_MAX_PENDING_ENDPS; + KeInitializeSpinLock( &pool->pool_lock ); + + for( i = 0; i < MAX_TIMER_SVCS; i++ ) + { + free_pending_endp( pool, &pool->pending_endp_array[i] ); + } + + return TRUE; + +} + +BOOL +free_pending_endp( +PUHCI_PENDING_ENDP_POOL pool, +PUHCI_PENDING_ENDP pending_endp +) +{ + if( pool == NULL || pending_endp == NULL ) + { + return FALSE; + } + + RtlZeroMemory( pending_endp, sizeof( UHCI_PENDING_ENDP ) ); + InsertTailList( &pool->free_que, (PLIST_ENTRY) &pending_endp->endp_link ); + pool->free_count++; + + return TRUE; +} + +PUHCI_PENDING_ENDP +alloc_pending_endp( +PUHCI_PENDING_ENDP_POOL pool, +LONG count) +{ + PUHCI_PENDING_ENDP new; + if( pool == NULL || count != 1 ) + return NULL; + + if( pool->free_count <= 0 ) + return NULL; + + new = ( PUHCI_PENDING_ENDP )RemoveHeadList( &pool->free_que ); + pool->free_count --; + return new; +} + +BOOL +destroy_pending_endp_pool( +PUHCI_PENDING_ENDP_POOL pool +) +{ + if( pool == NULL ) + return FALSE; + + InitializeListHead( &pool->free_que ); + pool->free_count = pool->total_count = 0; + usb_free_mem( pool->pending_endp_array ); + pool->pending_endp_array = NULL; + + return TRUE; + +} + + +//end of pending endpoint pool funcs + +static void +uhci_fill_td( +PUHCI_TD td, +ULONG status, +ULONG info, +ULONG buffer) +{ + td->status = status; + td->info = info; + td->buffer = buffer; +} + +BOOL +uhci_insert_td_fl( +PUHCI_TD prev_td, +PUHCI_TD ptd +) +{ + ULONG temp; + PLIST_ENTRY temp_entry; + + if( prev_td == NULL || ptd == NULL ) + return FALSE; + + temp_entry = &prev_td->ptde->hori_link; + + ptd->link = ( struct_ptr( temp_entry, TD_EXTENSION, hori_link ) )->ptd->phy_addr; + prev_td->link = ptd->phy_addr; + + InsertHeadList( &prev_td->ptde->hori_link, &ptd->ptde->hori_link ); + return TRUE; +} + +BOOL +uhci_remove_td_fl( +PUHCI_TD ptd +) +{ + PUHCI_TD prev_td; + + if( ptd == NULL ) + return FALSE; + + prev_td = ( struct_ptr( ptd->ptde->hori_link.Blink, TD_EXTENSION, hori_link ) )->ptd; + prev_td->link = ptd->link; + ptd->link = UHCI_PTR_TERM; + + RemoveEntryList( &ptd->ptde->hori_link ); + + return FALSE; +} + +BOOL +uhci_insert_qh_fl( +PVOID prev_item, +PUHCI_QH pqh +) +{ + //only horizontal link allowed + PUHCI_QH pprev_qh; + PUHCI_TD pprev_td; + ULONG temp; + PLIST_ENTRY temp_entry; + + if( prev_item == NULL || pqh == NULL ) + return FALSE; + + if( ( ( (PUHCI_TD)prev_item )->ptde->flags & UHCI_ITEM_FLAG_TYPE ) + == UHCI_ITEM_FLAG_QH ) + { + pprev_qh = (PUHCI_QH)prev_item; + temp_entry = pprev_qh->pqhe->hori_link.Flink; + pqh->link = ( struct_ptr( temp_entry, TD_EXTENSION, hori_link ) )->ptd->phy_addr; + pprev_qh->link = pqh->phy_addr; + + InsertHeadList( &pprev_qh->pqhe->hori_link, &pqh->pqhe->hori_link ); + } + else + { + pprev_td = ( (PUHCI_TD)prev_item ); + + temp_entry = pprev_td->ptde->hori_link.Flink; + pprev_td->link = pqh->phy_addr; + pqh->link = ( struct_ptr( temp_entry, TD_EXTENSION, hori_link ) )->ptd->phy_addr; + + InsertHeadList( &pprev_td->ptde->hori_link, &pqh->pqhe->hori_link ); + } + + return FALSE; +} + +BOOL +uhci_remove_qh_fl( +PUHCI_QH pqh +) +{ + PVOID prev_item; + PUHCI_QH pprevqh; + PUHCI_TD pprevtd; + + if( pqh == NULL ) + return FALSE; + + prev_item = ( struct_ptr( pqh->pqhe->hori_link.Blink, TD_EXTENSION, hori_link ) )->ptd; + + if( ( ( ( PUHCI_TD )prev_item )->ptde->flags & UHCI_ITEM_FLAG_TYPE ) + == UHCI_ITEM_FLAG_QH ) + { + pprevqh = (PUHCI_QH)prev_item; + pprevqh->link = pqh->link; + } + else + { + pprevtd = ( (PUHCI_TD)prev_item ); + pprevtd->link = pqh->link; + } + + RemoveEntryList( &pqh->pqhe->hori_link ); + + pqh->link = UHCI_PTR_TERM; + pqh->pqhe->hori_link.Flink = pqh->pqhe->hori_link.Blink = NULL; + + return TRUE; +} + +BOOL +uhci_init_frame_list( +PUHCI_DEV uhci, +PADAPTER_OBJECT padapter +) +{ + int i; + if( uhci == NULL || padapter == NULL ) + return FALSE; + + //note: frame_list_lock will be connected to interrupt + KeInitializeSpinLock( &uhci->frame_list_lock ); + + uhci->io_buf = \ + HalAllocateCommonBuffer( + padapter, + 4096, + &uhci->io_buf_logic_addr, + FALSE); + + if( uhci->io_buf == NULL ) + return FALSE; + + uhci->frame_list = \ + HalAllocateCommonBuffer( + padapter, + sizeof(ULONG) * UHCI_MAX_FRAMES, + &uhci->frame_list_logic_addr, + FALSE); + + if( uhci->frame_list == NULL ) + return FALSE; + + RtlZeroMemory( uhci->frame_list, + sizeof( ULONG ) * UHCI_MAX_FRAMES ); + + uhci->frame_list_cpu = usb_alloc_mem( + NonPagedPool, + sizeof( FRAME_LIST_CPU_ENTRY ) * UHCI_MAX_FRAMES ); + + if( uhci->frame_list_cpu == NULL ) + return FALSE; + + for( i = 0; i < UHCI_MAX_FRAMES; i++ ) + InitializeListHead( &uhci->frame_list_cpu[ i ].td_link ); + + uhci->frame_bw = usb_alloc_mem( NonPagedPool, + sizeof( LONG ) * UHCI_MAX_FRAMES ); + + if( uhci->frame_bw == NULL ) + return FALSE; + + for( i = 0; i < UHCI_MAX_FRAMES; i++ ) + { + uhci->frame_bw[ i ] = FRAME_TIME_MAX_USECS_ALLOC; + } + uhci->fsbr_cnt = 0; + + return TRUE; + +} + +BOOL +uhci_destroy_frame_list( +PUHCI_DEV uhci +) +{ + + if( uhci == NULL ) + return FALSE; + + if( uhci->frame_list ) + HalFreeCommonBuffer( + uhci->pdev_ext->padapter, + sizeof(ULONG) * UHCI_MAX_FRAMES, + uhci->frame_list_logic_addr, + uhci->frame_list, + FALSE ); + + uhci->frame_list = NULL; + uhci->frame_list_logic_addr.LowPart = 0; + uhci->frame_list_logic_addr.HighPart = 0; + + if( uhci->frame_list_cpu ) + usb_free_mem( uhci->frame_list_cpu ); + + uhci->frame_list_cpu = NULL; + + if( uhci->frame_bw ) + usb_free_mem( uhci->frame_bw ); + + uhci->frame_bw = NULL; + + return TRUE; +} + +PDEVICE_OBJECT +uhci_create_device( +PDRIVER_OBJECT drvr_obj, +PUSB_DEV_MANAGER dev_mgr +) +{ + NTSTATUS status; + PDEVICE_OBJECT pdev; + PDEVICE_EXTENSION pdev_ext; + + UNICODE_STRING dev_name; + UNICODE_STRING symb_name; + + STRING string, another_string; + CHAR str_dev_name[ 64 ], str_symb_name[ 64 ]; + UCHAR hcd_id; + + if( drvr_obj == NULL ) + return NULL; + + ASSERT( dev_mgr != NULL ); + + //note: hcd count wont increment till the hcd is registered in dev_mgr + sprintf( str_dev_name, "%s%d", UHCI_DEVICE_NAME, dev_mgr->hcd_count ); + sprintf( str_symb_name, "%s%d", DOS_DEVICE_NAME, dev_mgr->hcd_count ); + + RtlInitString( &string, str_dev_name ); + RtlAnsiStringToUnicodeString( &dev_name, &string, TRUE ); + + pdev = NULL; + status = IoCreateDevice( + drvr_obj, + sizeof( DEVICE_EXTENSION ) + sizeof( UHCI_DEV ), + &dev_name, + FILE_UHCI_DEV_TYPE, + 0, + FALSE, + &pdev); + + if( status != STATUS_SUCCESS || pdev == NULL ) + { + RtlFreeUnicodeString( &dev_name ); + return NULL; + } + + pdev_ext = pdev->DeviceExtension; + RtlZeroMemory( pdev_ext, + sizeof( DEVICE_EXTENSION ) + + sizeof( UHCI_DEV) ); + + pdev_ext->dev_ext_hdr.type = NTDEV_TYPE_HCD; + pdev_ext->dev_ext_hdr.dispatch = uhci_dispatch_irp; + pdev_ext->dev_ext_hdr.start_io = NULL; //we do not support startio + pdev_ext->dev_ext_hdr.dev_mgr = dev_mgr; + + pdev_ext->pdev_obj = pdev; + pdev_ext->pdrvr_obj = drvr_obj; + + pdev_ext->uhci = ( PUHCI_DEV ) &( pdev_ext[ 1 ] ); + + RtlInitString( &another_string, str_symb_name ); + RtlAnsiStringToUnicodeString( &symb_name, &another_string, TRUE ); + + IoCreateSymbolicLink( &symb_name, &dev_name ); + + uhci_dbg_print( DBGLVL_MAXIMUM, ( "uhci_create_device(): dev=0x%x\n, pdev_ext= 0x%x, uhci=0x%x, dev_mgr=0x%x\n", \ + pdev,\ + pdev_ext,\ + pdev_ext->uhci, \ + dev_mgr ) ); + + RtlFreeUnicodeString( &dev_name ); + RtlFreeUnicodeString( &symb_name ); + + //register with dev_mgr though it is not initilized + uhci_init_hcd_interface( pdev_ext->uhci ); + hcd_id = dev_mgr_register_hcd( dev_mgr, &pdev_ext->uhci->hcd_interf ); + + pdev_ext->uhci->hcd_interf.hcd_set_id( &pdev_ext->uhci->hcd_interf, hcd_id ); + pdev_ext->uhci->hcd_interf.hcd_set_dev_mgr( &pdev_ext->uhci->hcd_interf, dev_mgr ); + return pdev; +} + +BOOL +uhci_delete_device( +PDEVICE_OBJECT pdev +) +{ + STRING string; + UNICODE_STRING symb_name; + PDEVICE_EXTENSION pdev_ext; + CHAR str_symb_name[ 64 ]; + + + if( pdev == NULL ) + return FALSE; + + pdev_ext = pdev->DeviceExtension; + + sprintf( str_symb_name, + "%s%d", + DOS_DEVICE_NAME, + pdev_ext->uhci->hcd_interf.hcd_get_id( &pdev_ext->uhci->hcd_interf ) ); + RtlInitString( &string, str_symb_name ); + RtlAnsiStringToUnicodeString( &symb_name, &string, TRUE ); + IoDeleteSymbolicLink( &symb_name ); + RtlFreeUnicodeString( &symb_name ); + + if( pdev_ext->res_list ) + ExFreePool( pdev_ext->res_list ); // not allocated by usb_alloc_mem + + IoDeleteDevice( pdev ); + uhci_dbg_print( DBGLVL_MAXIMUM, ( "uhci_delete_device(): device deleted\n" ) ); + return TRUE; +} + +BOOLEAN +uhci_isr( +PKINTERRUPT interrupt, +PVOID context +) + // we can not use endp here for it is within the dev scope, and + // we can not acquire the dev-lock, fortunately we saved some + // info in urb->pipe in uhci_internal_submit_XXX. + +{ + + PUHCI_DEV uhci; + USHORT status; + PLIST_ENTRY pthis, pnext; + PURB purb; + + + /* + * Read the interrupt status, and write it back to clear the + * interrupt cause + */ + uhci = ( PUHCI_DEV )context; + if( uhci == NULL ) + return FALSE; + + status = READ_PORT_USHORT( ( PUSHORT )( uhci->port_base + USBSTS ) ); + if (!status) /* shared interrupt, not mine */ + return FALSE; + + if( status != 1 ) + { + uhci_dbg_print( DBGLVL_MAXIMUM, ( "uhci_isr(): current uhci status=0x%x\n", status ) ); + } + else + { + uhci_dbg_print( DBGLVL_MAXIMUM, ( "uhci_isr(): congratulations, no error occurs\n" ) ); + } + + /* clear it*/ + WRITE_PORT_USHORT( ( PUSHORT )( uhci->port_base + USBSTS ), status ); + + if ( status & ~( USBSTS_USBINT | USBSTS_ERROR | USBSTS_RD ) ) + { + if ( status & USBSTS_HSE ) + { + DbgPrint( "uhci_isr(): host system error, PCI problems?\n"); + //for( ; ; ); + } + if ( status & USBSTS_HCPE ) + { + DbgPrint( "uhci_isr(): host controller process error. something bad happened\n"); + //for( ; ; ); + //for( ; ; ); + } + if ( ( status & USBSTS_HCH ) ) //&& !uhci->is_suspended + { + DbgPrint( "uhci_isr(): host controller halted. very bad\n"); + /* FIXME: Reset the controller, fix the offending TD */ + } + } + + // don't no how to handle it yet + //if (status & USBSTS_RD) + //{ + //uhci_wakeup(uhci); + //}*/ + + //let's remove those force-cancel urbs from the schedule first + ListFirst( &uhci->urb_list, pthis ); + while( pthis ) + { + purb = ( PURB )pthis; + if( purb->flags & URB_FLAG_FORCE_CANCEL ) + { + uhci_remove_urb_from_schedule( uhci, purb ); + } + ListNext( &uhci->urb_list, pthis, pnext ); + pthis = pnext; + } + + //clear the interrupt if the urb is force canceled + uhci->skel_term_td->status &= ~TD_CTRL_IOC; + + //next we need to find if anything fininshed + ListFirst( &uhci->urb_list, pthis ); + while( pthis ) + { + purb = ( PURB )pthis; + if( purb->flags & URB_FLAG_IN_SCHEDULE ) + { + if( uhci_is_xfer_finished( purb ) ) + uhci_remove_urb_from_schedule( uhci, purb ); + } + ListNext( &uhci->urb_list, pthis, pnext ); + pthis = pnext; + } + + KeInsertQueueDpc( &uhci->pdev_ext->uhci_dpc, uhci, 0 ); + return TRUE; +} + +BOOLEAN +uhci_cal_cpu_freq( +PVOID context +) +{ + usb_cal_cpu_freq(); + return TRUE; +} + +PDEVICE_OBJECT +uhci_probe( +PDRIVER_OBJECT drvr_obj, +PUNICODE_STRING reg_path, +PUSB_DEV_MANAGER dev_mgr +) +{ + LONG bus, i, j, ret; + PCI_SLOT_NUMBER slot_num; + PPCI_COMMON_CONFIG pci_config; + PDEVICE_OBJECT pdev; + BYTE buffer[ sizeof( PCI_COMMON_CONFIG ) ]; + LONG count; + PDEVICE_EXTENSION pdev_ext; + + slot_num.u.AsULONG = 0; + pci_config = ( PPCI_COMMON_CONFIG ) buffer; + count = 0; + pdev = NULL; + + //scan the bus to find uhci controller + for( bus = 0; bus < 2; bus++ ) /*enum only bus0 and bus1*/ + { + for( i = 0; i < PCI_MAX_DEVICES; i++ ) + { + slot_num.u.bits.DeviceNumber = i; + for( j = 0; j < PCI_MAX_FUNCTIONS; j++ ) + { + slot_num.u.bits.FunctionNumber = j; + + ret = HalGetBusData( + PCIConfiguration, + bus, + slot_num.u.AsULONG, + pci_config, + PCI_COMMON_HDR_LENGTH ); + + if( ret == 0 ) /*no this bus*/ + break; + + if( ret == 2 ) /*no device on the slot*/ + break; + + if( pci_config->BaseClass == 0x0c && pci_config->SubClass == 0x03 ) + { + // well, we find our usb host controller, create device +#ifdef _MULTI_UHCI + { + pdev = uhci_alloc( drvr_obj, reg_path, ( ( bus << 8 ) | ( i << 3 ) | j ), dev_mgr ); + count++; + if( !pdev ) + return NULL; + } +#else + pdev = uhci_alloc( drvr_obj, reg_path, ( ( bus << 8 ) | ( i << 3 ) | j ), dev_mgr ); + if( pdev ) + goto LBL_LOOPOUT; +#endif + } + } + if( ret == 0 ) + break; + } + } +LBL_LOOPOUT: + if( pdev ) + { + pdev_ext = pdev->DeviceExtension; + if( pdev_ext ) + { + // acquire higher irql to eliminate pre-empty + KeSynchronizeExecution( pdev_ext->uhci_int, uhci_cal_cpu_freq, NULL ); + } + } + return NULL; +} + +PDEVICE_OBJECT +uhci_alloc( +PDRIVER_OBJECT drvr_obj, +PUNICODE_STRING reg_path, +ULONG bus_addr, +PUSB_DEV_MANAGER dev_mgr +) +{ + + LONG frd_num, prd_num; + UCHAR buffer[ PCI_COMMON_HDR_LENGTH ]; + PDEVICE_OBJECT pdev; + PDEVICE_EXTENSION pdev_ext; + ULONG vector, addr_space; + LONG bus; + KIRQL irql; + KAFFINITY affinity; + UCHAR hcd_id; + + DEVICE_DESCRIPTION dev_desc; + CM_PARTIAL_RESOURCE_DESCRIPTOR *pprd; + PCI_SLOT_NUMBER slot_num; + NTSTATUS status; + + + pdev = uhci_create_device( drvr_obj, dev_mgr ); + pdev_ext = pdev->DeviceExtension; + + pdev_ext->pci_addr = bus_addr; + bus = ( bus_addr >> 8 ); + + slot_num.u.AsULONG = 0; + slot_num.u.bits.DeviceNumber = ( ( bus_addr & 0xff ) >> 3 ); + slot_num.u.bits.FunctionNumber = ( bus_addr & 0x07 ); + + if( pdev == NULL ) + return pdev; + + //now create adapter object + RtlZeroMemory( &dev_desc, sizeof( dev_desc ) ); + + dev_desc.Version = DEVICE_DESCRIPTION_VERSION; + dev_desc.Master = TRUE; + dev_desc.ScatterGather = TRUE; + dev_desc.Dma32BitAddresses = TRUE; + dev_desc.BusNumber = bus; + dev_desc.InterfaceType = PCIBus; + dev_desc.MaximumLength = \ + UHCI_MAX_POOL_TDS * sizeof( UHCI_TD ) * UHCI_MAX_TD_POOLS \ + + sizeof( UHCI_QH ) * UHCI_MAX_POOL_QHS \ + + sizeof( ULONG ) * UHCI_MAX_FRAMES; + + pdev_ext->map_regs = 2; // UHCI_MAX_TD_POOLS + + //+ BYTES_TO_PAGES( ( UHCI_MAX_POOL_TDS * 64 ) * UHCI_MAX_TD_POOLS ) ; + + pdev_ext->padapter = HalGetAdapter( &dev_desc, &pdev_ext->map_regs); + + uhci_dbg_print( DBGLVL_MAXIMUM, ( "uhci_alloc(): padapter=0x%x\n", pdev_ext->padapter ) ); + if( pdev_ext->padapter == NULL ) + { + //fatal error + uhci_delete_device( pdev ); + return NULL; + } + + DbgPrint("uhci_alloc(): reg_path=0x%x, \n \ + uhci_alloc(): PCIBus=0x%x, bus=0x%x, bus_addr=0x%x \n \ + uhci_alloc(): slot_num=0x%x, &res_list=0x%x \n", \ + ( DWORD )reg_path, \ + ( DWORD )PCIBus, \ + ( DWORD )bus, \ + ( DWORD )bus_addr,\ + ( DWORD )slot_num.u.AsULONG, \ + ( DWORD )&pdev_ext->res_list ); + + //let's allocate resources for this device + DbgPrint( "uhci_alloc(): about to assign slot res\n" ); + if( ( status = HalAssignSlotResources( + reg_path, + NULL, //no class name yet + drvr_obj, + NULL, //no support of another uhci controller + PCIBus, + bus, + slot_num.u.AsULONG, + &pdev_ext->res_list ) ) + != STATUS_SUCCESS ) + { + DbgPrint( "uhci_alloc(): error assign slot res, 0x%x\n", status ); + release_adapter( pdev_ext->padapter ); + pdev_ext->padapter = NULL; + uhci_delete_device( pdev ); + return NULL; + } + + //parse the resource list + for( frd_num = 0; frd_num < ( LONG )pdev_ext->res_list->Count; frd_num ++ ) + { + for( prd_num = 0; prd_num < ( LONG )pdev_ext->res_list->List[ frd_num ].PartialResourceList.Count; prd_num ++ ) + { + pprd = &pdev_ext->res_list->List[ frd_num ].PartialResourceList.PartialDescriptors[ prd_num ]; + if( pprd->Type == CmResourceTypePort ) + { + RtlCopyMemory( &pdev_ext->res_port, &pprd->u.Port, sizeof( pprd->u.Port ) ); + + } + else if( pprd->Type == CmResourceTypeInterrupt ) + { + RtlCopyMemory( &pdev_ext->res_interrupt, &pprd->u.Interrupt, sizeof( pprd->u.Interrupt ) ); + } + } + } + + //for port, translate them to system address + addr_space = 1; + if( HalTranslateBusAddress( + PCIBus, + bus, + pdev_ext->res_port.Start, + &addr_space, //io space + &pdev_ext->uhci->uhci_reg_base + ) + != ( BOOLEAN )TRUE ) + { + DbgPrint( "uhci_alloc(): error, can not translate bus address\n" ); + release_adapter( pdev_ext->padapter ); + pdev_ext->padapter = NULL; + uhci_delete_device( pdev ); + return NULL; + } + + DbgPrint( "uhci_alloc(): address space=0x%x\n, reg_base=0x%x\n", \ + addr_space, pdev_ext->uhci->uhci_reg_base.u.LowPart ); + + if( addr_space == 0 ) + { + //port has been mapped to memory space + pdev_ext->uhci->port_mapped = TRUE; + pdev_ext->uhci->port_base = ( PBYTE )MmMapIoSpace( + pdev_ext->uhci->uhci_reg_base, + pdev_ext->res_port.Length, + FALSE ); + + //fatal error can not map the registers + if( pdev_ext->uhci->port_base == NULL ) + { + release_adapter( pdev_ext->padapter ); + pdev_ext->padapter = NULL; + uhci_delete_device( pdev ); + return NULL; + } + } + else + { + //io space + pdev_ext->uhci->port_mapped = FALSE; + pdev_ext->uhci->port_base = ( PBYTE )pdev_ext->uhci->uhci_reg_base.LowPart; + } + + //before we connect the interrupt, we have to init uhci + pdev_ext->uhci->fsbr_cnt = 0; + pdev_ext->uhci->pdev_ext = pdev_ext; + + if( uhci_init_schedule( pdev_ext->uhci, pdev_ext->padapter ) == FALSE ) + { + release_adapter( pdev_ext->padapter ); + pdev_ext->padapter = NULL; + uhci_delete_device( pdev ); + return NULL; + } + + InitializeListHead( &pdev_ext->uhci->urb_list ); + KeInitializeSpinLock( &pdev_ext->uhci->pending_endp_list_lock ); + InitializeListHead( &pdev_ext->uhci->pending_endp_list ); + + uhci_dbg_print( DBGLVL_MAXIMUM, ( "uhci_alloc(): pending_endp_list=0x%x\n", \ + &pdev_ext->uhci->pending_endp_list ) ); + + init_pending_endp_pool( &pdev_ext->uhci->pending_endp_pool ); + KeInitializeTimer( &pdev_ext->uhci->reset_timer ); + + vector = HalGetInterruptVector( + PCIBus, + bus, + pdev_ext->res_interrupt.level, + pdev_ext->res_interrupt.vector, + &irql, + &affinity); + + //connect the interrupt + DbgPrint( "uhci_alloc(): the int=0x%x\n", vector ); + if( IoConnectInterrupt( + &pdev_ext->uhci_int, + uhci_isr, + pdev_ext->uhci, + NULL, //&pdev_ext->uhci->frame_list_lock, + vector, + irql, + irql, + LevelSensitive, + TRUE, //share the vector + affinity, + FALSE ) //No float save + != STATUS_SUCCESS ) + { + uhci_release( pdev ); + return NULL; + } + + KeInitializeDpc( &pdev_ext->uhci_dpc, + uhci_dpc_callback, + ( PVOID )pdev_ext->uhci ); + + return pdev; +} + +BOOL +uhci_release( +PDEVICE_OBJECT pdev +) +{ + PDEVICE_EXTENSION pdev_ext; + PUHCI_DEV uhci; + KIRQL disp_level; + KIRQL old_level; + + if( pdev == NULL ) + return FALSE; + + pdev_ext = pdev->DeviceExtension; + + if( pdev_ext == NULL ) + return FALSE; + + uhci = pdev_ext->uhci; + if( uhci == NULL ) + return FALSE; + + uhci_stop( uhci ); + //pdev_ext->uhci->conn_count = 0; + pdev_ext->uhci->fsbr_cnt = 0; + + if( pdev_ext->uhci_int ) + { + IoDisconnectInterrupt( pdev_ext->uhci_int ); + pdev_ext->uhci_int = NULL; + } + else + TRAP(); + destroy_pending_endp_pool( &pdev_ext->uhci->pending_endp_pool ); + //pdev_ext->uhci->pending_endp_pool = NULL; + + uhci_destroy_schedule( uhci ); + + release_adapter( pdev_ext->padapter ); + pdev_ext->padapter = NULL; + + uhci_delete_device( pdev ); + + return FALSE; + +} + +BOOL +uhci_start( +PHCD hcd +) +{ +// send cmds to start the uhc +// shamelessly copied from linux's uhci.c + PUHCI_DEV uhci; + PBYTE io_addr; + int timeout = 10000; + + uhci = uhci_from_hcd( hcd ); + io_addr = uhci->port_base; + + /* + * Reset the HC - this will force us to get a + * new notification of any already connected + * ports due to the virtual disconnect that it + * implies. + */ + WRITE_PORT_USHORT( ( PUSHORT )( io_addr + USBCMD ), USBCMD_HCRESET ); + while ( READ_PORT_USHORT( ( PUSHORT )( io_addr + USBCMD ) ) & USBCMD_HCRESET ) + { + if (!--timeout) + { + break; + } + } + + /* Turn on all interrupts */ + WRITE_PORT_USHORT( + ( PUSHORT )( io_addr + USBINTR ), + USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC | USBINTR_SP + ); + + /* Start at frame 0 */ + WRITE_PORT_USHORT( ( PUSHORT )( io_addr + USBFRNUM ), 0 ); + WRITE_PORT_ULONG( ( PULONG )( io_addr + USBFLBASEADD ), uhci->frame_list_logic_addr.LowPart ); + + /* Run and mark it configured with a 64-byte max packet */ + WRITE_PORT_USHORT( ( PUSHORT )( io_addr + USBCMD ), USBCMD_RS | USBCMD_CF | USBCMD_MAXP ); + + DbgPrint( "uhci_start(): current uhci status=0x%x\n", uhci_status( uhci ) ); + + return TRUE; +} + +VOID +uhci_stop( +PUHCI_DEV uhci +) +{ + PBYTE io_addr = uhci->port_base; + // turn off all the interrupt + WRITE_PORT_USHORT( ( PUSHORT )( io_addr + USBINTR ), 0 ); + WRITE_PORT_USHORT( ( PUSHORT )( io_addr + USBCMD ), 0 ); +} + +static VOID +uhci_reset( +PUHCI_DEV uhci +) +{ + PUSB_DEV_MANAGER dev_mgr; + PLIST_ENTRY pthis, pnext; + PBYTE io_addr = uhci->port_base; + + uhci_stop( uhci ); + /* Global reset for 50ms */ + WRITE_PORT_USHORT( ( PUSHORT )( io_addr + USBCMD ), USBCMD_GRESET ); + //uhci_wait_ms( uhci, 50 ); + usb_wait_ms_dpc( 50 ); + + WRITE_PORT_USHORT( ( PUSHORT )( io_addr + USBCMD ), 0 ); + //uhci_wait_ms( uhci, 10 ); + usb_wait_ms_dpc( 10 ); +} + +static VOID +uhci_suspend( +PUHCI_DEV uhci +) +{ + PBYTE io_addr = uhci->port_base; + + //uhci->is_suspended = 1; + WRITE_PORT_USHORT( ( PUSHORT )( io_addr + USBCMD ), USBCMD_EGSM ); + +} + +static VOID +uhci_wakeup( +PUHCI_DEV uhci +) +{ + PBYTE io_addr; + unsigned int status; + + io_addr = uhci->port_base; + + WRITE_PORT_USHORT( ( PUSHORT )( io_addr + USBCMD ), 0 ); + + /* wait for EOP to be sent */ + status = READ_PORT_USHORT( ( PUSHORT )( io_addr + USBCMD ) ); + while (status & USBCMD_FGR) + status = READ_PORT_USHORT( ( PUSHORT )( io_addr + USBCMD ) ); + + //uhci->is_suspended = 0; + + /* Run and mark it configured with a 64-byte max packet */ + WRITE_PORT_USHORT( + ( PUSHORT )( io_addr + USBCMD ), + USBCMD_RS | USBCMD_CF | USBCMD_MAXP + ); + +} + +BOOL +uhci_init_schedule( +PUHCI_DEV uhci, +PADAPTER_OBJECT padapter +) +{ + int i, irq; + + uhci_dbg_print( DBGLVL_MAXIMUM, ( "uhci_init_schedule(): entering..., uhci=0x%x\n", uhci ) ); + if( uhci == NULL || padapter == NULL ) + return FALSE; + + if( init_td_pool_list( &uhci->td_pool, padapter ) == FALSE ) + { + return FALSE; + } + if( init_qh_pool( &uhci->qh_pool, padapter ) == FALSE ) + { + return FALSE; + } + + //since uhci is not started we can freely access all resources. + for( i = 0; i < UHCI_MAX_SKELTDS; i++ ) + { + uhci->skel_td[ i ] = alloc_td( &uhci->td_pool ); + uhci_fill_td(uhci->skel_td[ i ], + 0, + ( UHCI_NULL_DATA_SIZE << 21 ) + | ( 0x7f << 8 ) | USB_PID_IN, + 0 ); + + if( i > 0 ) + { + uhci->skel_td[ i ]->link = uhci->skel_td[ i - 1 ]->phy_addr; + } + } + + /*for( i = UHCI_MAX_SKELTDS - 3; i >= 0; i-- ) + { + InsertTailList( &uhci->skel_int256_td->ptde->hori_link, + &uhci->skel_td[ i ]->ptde->hori_link ); + }*/ + + for( i = 0; i < UHCI_MAX_SKELQHS; i++ ) + { + uhci->skel_qh[ i ] = alloc_qh( &uhci->qh_pool ); + if( i > 0 ) + { + uhci->skel_qh[ i - 1 ]->link = uhci->skel_qh[ i ]->phy_addr; + } + + uhci->skel_qh[ i ]->element = UHCI_PTR_TERM; + } + + uhci->skel_int1_td->link = uhci->skel_ls_control_qh->phy_addr; + + uhci_fill_td( uhci->skel_term_td, 0, ( UHCI_NULL_DATA_SIZE << 21 ) | ( 0x7f << 8 ) | USB_PID_IN, 0 ); + uhci->skel_term_td->link = uhci->skel_term_td->phy_addr; + + uhci->skel_term_qh->link = UHCI_PTR_TERM; + uhci->skel_term_qh->element = uhci->skel_term_td->phy_addr; + + InsertTailList( &uhci->skel_term_qh->pqhe->vert_link, &uhci->skel_term_td->ptde->vert_link ); + + /*for( i = 0; i < UHCI_MAX_SKELQHS; i++ ) + { + InsertTailList( &uhci->skel_int256_td->ptde->hori_link, + &uhci->skel_qh[ i ]->pqhe->hori_link ); + }*/ + + if( uhci_init_frame_list( uhci, uhci->pdev_ext->padapter ) == FALSE ) + uhci_destroy_frame_list( uhci ); + + //well all have been chained, now scatter the int tds to frame-list + //shamelessly pasted from linux's uhci.c :-) + for( i = 0; i < UHCI_MAX_FRAMES; i++ ) + { + irq = 0; + if (i & 1) + { + irq++; + if (i & 2) + { + irq++; + if (i & 4) + { + irq++; + if (i & 8) + { + irq++; + if (i & 16) + { + irq++; + if (i & 32) + { + irq++; + if (i & 64) + irq++; + } + } + } + } + } + } + + /* Only place we don't use the frame list routines */ + uhci->frame_list[ i ] = uhci->skel_td[ irq ]->phy_addr; + } + return TRUE; +} + +BOOL +uhci_destroy_schedule( +PUHCI_DEV uhci +) +{ + BOOL ret; + + ret = uhci_destroy_frame_list( uhci ); + ret = destroy_qh_pool( &uhci->qh_pool ); + ret = destroy_td_pool_list( &uhci->td_pool ); + + return ret; + +} + +static VOID +uhci_cancel_pending_endp_urb( +IN PVOID Parameter +) +{ + PLIST_ENTRY abort_list; + PUSB_DEV pdev; + PURB purb; + USE_IRQL; + + abort_list = ( PLIST_ENTRY )Parameter; + + if( abort_list == NULL ) + return; + + while( IsListEmpty( abort_list ) == FALSE ) + { + //these devs are protected by urb's ref-count + purb = ( PURB )RemoveHeadList( abort_list ); + pdev = purb->pdev; + // purb->status is set when they are added to abort_list + + uhci_generic_urb_completion( purb, purb->context ); + + lock_dev( pdev, FALSE ); + pdev->ref_count--; + unlock_dev( pdev, FALSE ); + } + usb_free_mem( abort_list ); + return; +} + +static BOOL +uhci_process_pending_endp( +PUHCI_DEV uhci +) +{ + PUSB_DEV pdev; + LIST_ENTRY temp_list, abort_list; + PLIST_ENTRY pthis; + PURB purb; + PUSB_ENDPOINT pendp; + BOOL can_submit; + PWORK_QUEUE_ITEM pwork_item; + PLIST_ENTRY cancel_list; + USE_IRQL; + + if( uhci == NULL ) + return FALSE; + + InitializeListHead( &temp_list ); + InitializeListHead( &abort_list ); + + purb = NULL; + uhci_dbg_print( DBGLVL_MEDIUM, ("uhci_process_pending_endp(): entering..., uhci=0x%x\n", uhci ) ); + + lock_pending_endp_list( &uhci->pending_endp_list_lock ); + while( IsListEmpty( &uhci->pending_endp_list ) == FALSE ) + { + + uhci_dbg_print( DBGLVL_MAXIMUM, ( "uhci_process_pending_endp(): pending_endp_list=0x%x\n", \ + &uhci->pending_endp_list ) ); + + pthis = RemoveHeadList( &uhci->pending_endp_list ); + pendp = ( ( PUHCI_PENDING_ENDP )pthis )->pendp; + pdev = dev_from_endp( pendp ); + + lock_dev( pdev, TRUE ); + + if( dev_state( pdev ) == USB_DEV_STATE_ZOMB ) + { + unlock_dev( pdev, TRUE ); + free_pending_endp( &uhci->pending_endp_pool, struct_ptr( pthis, UHCI_PENDING_ENDP, endp_link ) ); + //delegate to uhci_remove_device for remiving the urb queue on the endpoint + continue; + } + + if( endp_state( pendp ) == USB_ENDP_FLAG_STALL ) + { + while( IsListEmpty( &pendp->urb_list ) == FALSE ) + { + purb = ( PURB )RemoveHeadList( &pendp->urb_list ); + purb->status = USB_STATUS_ENDPOINT_HALTED; + InsertTailList( &abort_list, ( LIST_ENTRY* )purb ); + } + InitializeListHead( &pendp->urb_list ); + unlock_dev( pdev, TRUE ); + free_pending_endp( &uhci->pending_endp_pool, struct_ptr( pthis, UHCI_PENDING_ENDP, endp_link ) ); + continue; + } + + + if( IsListEmpty( &pendp->urb_list ) == FALSE ) + { + purb = ( PURB )RemoveHeadList( &pendp->urb_list ); + ASSERT( purb ); + } + else + { + InitializeListHead( &pendp->urb_list ); + unlock_dev( pdev, TRUE ); + free_pending_endp( &uhci->pending_endp_pool, struct_ptr( pthis, UHCI_PENDING_ENDP, endp_link ) ); + continue; + } + + // if can_submit is STATUS_SUCCESS, the purb is inserted into the schedule + switch( endp_type( pendp ) ) + { + case USB_ENDPOINT_XFER_BULK: + { +#ifdef DEMO + can_submit = STATUS_UNSUCCESSFUL; +#else + can_submit = uhci_internal_submit_bulk( uhci, purb ); +#endif + break; + } + case USB_ENDPOINT_XFER_CONTROL: + { + can_submit = uhci_internal_submit_ctrl( uhci, purb ); + break; + } + case USB_ENDPOINT_XFER_INT: + { + can_submit = uhci_internal_submit_int( uhci, purb ); + break; + } + case USB_ENDPOINT_XFER_ISOC: + { + can_submit = uhci_internal_submit_iso( uhci, purb ); + break; + } + } + + if( can_submit == STATUS_NO_MORE_ENTRIES ) + { + //no enough bandwidth or tds + InsertHeadList( &pendp->urb_list, ( PLIST_ENTRY )purb ); + InsertTailList( &temp_list, pthis ); + } + else + { + // other error or success + free_pending_endp( + &uhci->pending_endp_pool, + struct_ptr( pthis, UHCI_PENDING_ENDP, endp_link ) ); + + if( can_submit != STATUS_SUCCESS ) + { + //abort these URBs + InsertTailList( &abort_list, ( LIST_ENTRY* )purb ); + purb->status = can_submit; + } + + } + unlock_dev( pdev, TRUE ); + } + + if( IsListEmpty( &temp_list ) == FALSE ) + { + //re-append them to the pending_endp_list + ListFirst( &temp_list, pthis ); + RemoveEntryList( &temp_list ); + MergeList( &uhci->pending_endp_list, pthis ); + } + unlock_pending_endp_list( &uhci->pending_endp_list_lock ); + + if( IsListEmpty( &abort_list ) == FALSE ) + { + PLIST_ENTRY pthis; + cancel_list = ( PLIST_ENTRY )usb_alloc_mem( NonPagedPool, sizeof( WORK_QUEUE_ITEM ) + sizeof( LIST_ENTRY ) ); + ASSERT( cancel_list ); + + ListFirst( &abort_list, pthis ); + RemoveEntryList( &abort_list ); + InsertTailList( pthis, cancel_list ); + + pwork_item = ( PWORK_QUEUE_ITEM )&cancel_list[ 1 ]; + + // we do not need to worry the uhci_cancel_pending_endp_urb running when the + // driver is unloading since it will prevent the dev_mgr to quit till all the + // reference count to the dev drop to zero. + ExInitializeWorkItem( pwork_item, uhci_cancel_pending_endp_urb, ( PVOID )cancel_list ); + ExQueueWorkItem( pwork_item, DelayedWorkQueue ); + } + return TRUE; +} + +BOOL +uhci_submit_urb( +PUHCI_DEV uhci, +PUSB_DEV pdev, +PUSB_ENDPOINT pendp, +PURB purb +) +{ + int i; + PLIST_ENTRY pthis, pnext; + PUHCI_PENDING_ENDP pending_endp; + NTSTATUS status; + USE_IRQL; + + if( uhci == NULL || pdev == NULL || pendp == NULL || purb == NULL ) + return STATUS_INVALID_PARAMETER; + + lock_pending_endp_list( &uhci->pending_endp_list_lock ); + lock_dev( pdev, TRUE ); + + if( dev_state( pdev ) == USB_DEV_STATE_ZOMB ) + { + status = purb->status = STATUS_DEVICE_DOES_NOT_EXIST; + goto LBL_OUT; + } + + if( dev_class( pdev ) == USB_DEV_CLASS_ROOT_HUB ) + { + unlock_dev( pdev, TRUE ); + unlock_pending_endp_list( &uhci->pending_endp_list_lock ); + status = uhci_rh_submit_urb( pdev, purb ); + return status; + } + + if( pendp ) + purb->pendp = pendp; + else + purb->pendp = &pdev->default_endp; + + if( dev_from_endp( purb->pendp ) != pdev ) + { + status = purb->status = STATUS_INVALID_PARAMETER; + goto LBL_OUT; + } + + if( endp_state( purb->pendp ) == USB_ENDP_FLAG_STALL ) + { + status = purb->status = USB_STATUS_ENDPOINT_HALTED; + goto LBL_OUT; + } + + purb->pdev = pdev; + purb->rest_bytes = purb->data_length; + + if( endp_type( purb->pendp ) == USB_ENDPOINT_XFER_BULK ) + purb->bytes_to_transfer = + ( purb->data_length + > purb->pendp->pusb_endp_desc->wMaxPacketSize * UHCI_MAX_TDS_PER_TRANSFER + ? purb->pendp->pusb_endp_desc->wMaxPacketSize * UHCI_MAX_TDS_PER_TRANSFER + : purb->data_length ); //multiple transfer for large data block + else + purb->bytes_to_transfer = purb->data_length; + + uhci_dbg_print( DBGLVL_MEDIUM, ( "uhci_submit_urb(): bytes_to_transfer=0x%x\n", purb->bytes_to_transfer ) ); + + purb->bytes_transfered = 0; + InitializeListHead( &purb->trasac_list ); + purb->last_finished_td = &purb->trasac_list; + purb->flags &= ~( URB_FLAG_STATE_MASK | URB_FLAG_IN_SCHEDULE | URB_FLAG_FORCE_CANCEL ); + purb->flags |= URB_FLAG_STATE_PENDING; + + + i = IsListEmpty( &pendp->urb_list ); + InsertTailList( &pendp->urb_list, &purb->urb_link ); + + pdev->ref_count ++; //for urb reference + + if( i == FALSE ) + { + //there is urb pending, simply queue it and return + status = purb->status = STATUS_PENDING; + goto LBL_OUT; + } + else if( usb_endp_busy_count( purb->pendp ) + && endp_type( purb->pendp) != USB_ENDPOINT_XFER_ISOC ) + { + // + //No urb waiting but urb overlap not allowed, + //so leave it in queue and return, will be scheduled + //later + // + status = purb->status = STATUS_PENDING; + goto LBL_OUT; + } + + pending_endp = alloc_pending_endp( &uhci->pending_endp_pool, 1 ); + if( pending_endp == NULL ) + { + //panic + status = purb->status = STATUS_UNSUCCESSFUL; + goto LBL_OUT2; + } + + pending_endp->pendp = purb->pendp; + InsertTailList( &uhci->pending_endp_list, ( PLIST_ENTRY )pending_endp ); + + unlock_dev( pdev, TRUE ); + unlock_pending_endp_list( &uhci->pending_endp_list_lock ); + + uhci_process_pending_endp( uhci ); + return STATUS_PENDING; + +LBL_OUT2: + pdev->ref_count --; + RemoveEntryList( ( PLIST_ENTRY )purb ); + +LBL_OUT: + unlock_dev( pdev, TRUE ); + unlock_pending_endp_list( &uhci->pending_endp_list_lock ); + return status; +} + +static NTSTATUS +uhci_set_error_code( +PURB urb, +ULONG raw_status +) +{ + + if( ( raw_status & TD_CTRL_ANY_ERROR ) == 0 ) + { + //test if the urb is canceled + if( urb->flags & URB_FLAG_FORCE_CANCEL ) + urb->status = STATUS_CANCELLED; + else + urb->status = STATUS_SUCCESS; + } + + else if( raw_status & TD_CTRL_BABBLE ) + urb->status = USB_STATUS_DATA_OVERRUN; + + else if( raw_status & TD_CTRL_STALLED ) + urb->status = USB_STATUS_STALL_PID; + + else if( raw_status & TD_CTRL_DBUFERR ) + urb->status = USB_STATUS_BUFFER_OVERRUN; + + else if( raw_status & TD_CTRL_CRCTIMEO ) + urb->status = USB_STATUS_CRC; + + else if( raw_status & TD_CTRL_BITSTUFF ) + urb->status = USB_STATUS_BTSTUFF; + + else + urb->status = STATUS_UNSUCCESSFUL; + + return urb->status; +} + +BOOLEAN +uhci_sync_remove_urb_finished( +PVOID context +) +{ + PUHCI_DEV uhci; + PLIST_ENTRY pthis, pnext, ptemp; + PURB purb; + PSYNC_PARAM pparam; + + pparam = ( PSYNC_PARAM )context; + uhci = pparam->uhci; + ptemp = ( PLIST_ENTRY )pparam->context; + + if( uhci == NULL ) + { + return ( UCHAR )pparam->ret = FALSE; + } + + ListFirst( &uhci->urb_list, pthis ); + while( pthis ) + { + //remove urbs not in the schedule + ListNext( &uhci->urb_list, pthis, pnext ); + purb = ( PURB )pthis; + + if( ( purb->flags & URB_FLAG_IN_SCHEDULE ) == 0 ) + { + //finished or canceled( not apply for split bulk ). + purb->flags &= ~URB_FLAG_STATE_MASK; + purb->flags |= URB_FLAG_STATE_FINISHED; + RemoveEntryList( pthis ); + InsertTailList( ptemp, pthis ); + } + pthis = pnext; + } + pparam->ret = TRUE; + return ( UCHAR )TRUE; +} + +BOOLEAN +uhci_drop_fsbr( +PUHCI_DEV uhci +) +{ + + if( uhci == NULL ) + return ( UCHAR )FALSE; + + uhci->fsbr_cnt--; + + if( uhci->fsbr_cnt <= 0 ) + { + uhci->skel_term_qh->link = UHCI_PTR_TERM; + uhci->fsbr_cnt = 0; + } + + return ( UCHAR )TRUE; +} +VOID +uhci_dpc_callback( +PKDPC dpc, +PVOID context, +PVOID sysarg1, +PVOID sysarg2 +) +{ + PUHCI_DEV uhci; + + LIST_HEAD temp_list; + PLIST_ENTRY pthis, pnext; + PURB purb; + PQH_EXTENSION pqhe; + PTD_EXTENSION ptde; + PUHCI_PENDING_ENDP pending_endp; + PUSB_DEV pdev; + PUSB_ENDPOINT pendp; + + BOOL finished; + LONG i, j; + ULONG uhci_status, urb_status, toggle = 0; + + SYNC_PARAM sync_param; + USE_IRQL; + + uhci = ( PUHCI_DEV ) context; + if( uhci == NULL ) + return; + + uhci_status = ( ULONG )sysarg1; + + InitializeListHead( &temp_list ); + + sync_param.uhci = uhci; + sync_param.context = ( PVOID )&temp_list; + + uhci_dbg_print( DBGLVL_MAXIMUM, ( "uhci_dpc_callback(): entering..., uhci=0x%x\n", uhci ) ); + //remove finished urb from uhci's urb-list + KeSynchronizeExecution( uhci->pdev_ext->uhci_int, uhci_sync_remove_urb_finished, &sync_param ); + + //release resources( tds, and qhs ) the urb occupied + while( IsListEmpty( &temp_list ) == FALSE ) + { + //not in any public queue, if do not access into dev, no race + //condition will occur + purb = ( PURB ) RemoveHeadList( &temp_list ); + urb_status = purb->status; + + //the only place we do not use this lock on non-pending-endp-list data ops + KeAcquireSpinLockAtDpcLevel( &uhci->pending_endp_list_lock ); + while( IsListEmpty( &purb->trasac_list ) == FALSE ) + { + pthis = RemoveHeadList( &purb->trasac_list ); + + if( ( ( ( PTD_EXTENSION )pthis )->flags & UHCI_ITEM_FLAG_TYPE ) + == UHCI_ITEM_FLAG_QH ) + { + pqhe = ( PQH_EXTENSION ) pthis; + lock_qh_pool( &uhci->qh_pool, TRUE ); + free_qh( &uhci->qh_pool, pqhe->pqh ); + unlock_qh_pool( &uhci->qh_pool, TRUE ); + } + else + { + //must be a td chain + InsertHeadList( &purb->trasac_list, pthis ); + for( i = 0, purb->bytes_transfered = 0; i < purb->td_count; i++ ) + { + PUHCI_TD ptd; + // accumulate data transfered in tds + ptd = ( ( PTD_EXTENSION )pthis )->ptd; + if( ( ptd->status & TD_CTRL_ACTIVE ) == 0 && ( ptd->status & TD_CTRL_ANY_ERROR ) == 0 ) + { + j = ptd->status & 0x7ff; + purb->bytes_transfered += ( ( j == 0x7ff ) ? 0 : ( j + 1 ) ); + + } + ListNext( &purb->trasac_list, pthis, pnext ); + pthis = pnext; + } + + if( urb_status & TD_CTRL_ANY_ERROR ) + { + if( purb->last_finished_td != NULL && purb->last_finished_td != &purb->trasac_list ) + toggle = ( ( ( PTD_EXTENSION )purb->last_finished_td )->ptd->info & ( 1 << 19 ) ); + } + //trick, remove trasac_list + ListFirst( &purb->trasac_list, pthis ); + RemoveEntryList( &purb->trasac_list ); + lock_td_pool( &uhci->td_pool, TRUE ); + free_tds( &uhci->td_pool, ( ( PTD_EXTENSION )pthis )->ptd ); + unlock_td_pool( &uhci->td_pool, TRUE ); + //termination condition + InitializeListHead( &purb->trasac_list ); + purb->last_finished_td = NULL; + } + } + + if( endp_type( purb->pendp ) == USB_ENDPOINT_XFER_ISOC + || endp_type( purb->pendp ) == USB_ENDPOINT_XFER_INT ) + uhci_claim_bandwidth( uhci, purb, FALSE); //release band-width + + KeReleaseSpinLockFromDpcLevel( &uhci->pending_endp_list_lock ); + + uhci_set_error_code( purb, urb_status ); + + finished = TRUE; + + //since the ref_count for the urb is not released, we can safely have one + //pointer to dev + pdev = dev_from_endp( purb->pendp ); + pendp = purb->pendp; + + if( purb->status == USB_STATUS_BABBLE_DETECTED ) + { + usb_dbg_print( DBGLVL_MEDIUM, ( "uhci_dpc_callback(): alert!!!, babble detected, severe error, reset the whole bus\n" ) ); + uhci_reset( uhci ); + uhci_start( &uhci->hcd_interf ); + } + + //this will let the new request in uhci_generic_urb_completion to this endp + //be processed rather than queued in the pending_endp_list + lock_dev( pdev, TRUE ); + usb_endp_busy_count_dec( pendp ); + unlock_dev( pdev, TRUE ); + + if( usb_success( purb->status ) == FALSE ) + { + // set error code and complete the urb and purb is invalid from this point + uhci_generic_urb_completion( purb, purb->context ); + } + else + { + if( ( purb->pipe & USB_ENDPOINT_XFERTYPE_MASK ) + == USB_ENDPOINT_XFER_BULK ) + { + purb->rest_bytes -= purb->bytes_transfered; + if( purb->rest_bytes ) + { + finished = FALSE; + } + else + { + uhci_generic_urb_completion( purb, purb->context ); + } + } + else + { + uhci_generic_urb_completion( purb, purb->context ); + //purb is now invalid + } + } + + KeAcquireSpinLockAtDpcLevel( &uhci->pending_endp_list_lock ); + lock_dev( pdev, TRUE ); + + if( finished ) + pdev->ref_count--; + + if( urb_status & TD_CTRL_ANY_ERROR + && endp_type( pendp ) != USB_ENDPOINT_XFER_CONTROL ) + { + pendp->flags &= ~USB_ENDP_FLAG_STAT_MASK; + pendp->flags |= USB_ENDP_FLAG_STALL; + } + + if( dev_state( pdev )== USB_DEV_STATE_ZOMB ) + { + unlock_dev( pdev, TRUE); + KeReleaseSpinLockFromDpcLevel( &uhci->pending_endp_list_lock ); + if( finished == FALSE ) + { + + purb->status = STATUS_DEVICE_DOES_NOT_EXIST; + uhci_generic_urb_completion( purb, purb->context ); + + lock_dev( pdev, TRUE ); + pdev->ref_count--; + unlock_dev( pdev, TRUE); + } + continue; + } + + if( finished && IsListEmpty( &pendp->urb_list ) == TRUE ) + { + unlock_dev( pdev, TRUE); + KeReleaseSpinLockFromDpcLevel( &uhci->pending_endp_list_lock ); + continue; + } + else if( finished == TRUE ) + { + //has urb in the endp's urb-list + if( usb_endp_busy_count( pendp ) > 0 ) + { + //the urbs still have chance to be sheduled but not this time + unlock_dev( pdev, TRUE); + KeReleaseSpinLockFromDpcLevel( &uhci->pending_endp_list_lock ); + continue; + } + } + + if( finished == FALSE ) + { + //a split bulk transfer + purb->bytes_transfered = 0; + purb->bytes_to_transfer = + UHCI_MAX_TDS_PER_TRANSFER * purb->pendp->pusb_endp_desc->wMaxPacketSize + > purb->rest_bytes + ? purb->rest_bytes + : UHCI_MAX_TDS_PER_TRANSFER * purb->pendp->pusb_endp_desc->wMaxPacketSize; + + //the urb is not finished + purb->flags &= ~URB_FLAG_STATE_MASK; + purb->flags |= URB_FLAG_STATE_PENDING; + + InsertHeadList( &pendp->urb_list, ( PLIST_ENTRY )purb ); + } + + pending_endp = alloc_pending_endp( &uhci->pending_endp_pool, 1 ); + pending_endp->pendp = pendp; + InsertTailList( &uhci->pending_endp_list, &pending_endp->endp_link ); + + unlock_dev( pdev, TRUE); + KeReleaseSpinLockFromDpcLevel( &uhci->pending_endp_list_lock ); + } + + //ah...exhausted, let's find some in the pending_endp_list to rock + uhci_process_pending_endp( uhci ); + return; +} + +BOOL +uhci_add_device( +PUHCI_DEV uhci, +PUSB_DEV dev +) +{ + if( dev == NULL || uhci == NULL ) + return FALSE; + + return TRUE; + +} + +BOOLEAN +uhci_sync_cancel_urbs_dev( +PVOID context +) +{ + //cancel all the urbs on one dev + PUHCI_DEV uhci; + PUSB_DEV pdev, dest_dev; + PSYNC_PARAM sync_param; + PLIST_ENTRY pthis, pnext; + LONG count; + + sync_param = ( PSYNC_PARAM )context; + dest_dev = ( PUSB_DEV )sync_param->context; + uhci = sync_param->uhci; + + if( uhci == NULL || dest_dev == NULL ) + { + return ( UCHAR )sync_param->ret = FALSE; + } + count = 0; + ListFirst( &uhci->urb_list, pthis ); + while( pthis ) + { + pdev = dev_from_endp( ( ( PURB ) pthis )->pendp ); + if( pdev == dest_dev ) + { + ( ( PURB ) pthis )->flags |= URB_FLAG_FORCE_CANCEL; + } + ListNext( &uhci->urb_list, pthis, pnext ); + pthis = pnext; + count ++; + } + if( count ) + uhci->skel_term_td->status |= TD_CTRL_IOC; + + return ( UCHAR )sync_param->ret = TRUE; +} + +BOOL +uhci_remove_device( +PUHCI_DEV uhci, +PUSB_DEV dev +) +{ + PUHCI_PENDING_ENDP ppending_endp; + PLIST_ENTRY pthis, pnext; + PURB purb; + LIST_HEAD temp_list; + int i, j, k; + SYNC_PARAM sync_param; + + USE_IRQL; + + if( uhci == NULL || dev == NULL ) + return FALSE; + + InitializeListHead( &temp_list ); + + //free pending endp that has urb queued from pending endp list + lock_pending_endp_list( &uhci->pending_endp_list_lock ); + + ListFirst( &uhci->pending_endp_list, pthis ); + + while( pthis ) + { + ppending_endp = ( PUHCI_PENDING_ENDP ) pthis; + ListNext( &uhci->pending_endp_list, pthis, pnext ); + if( dev_from_endp( ppending_endp->pendp ) == dev ) + { + RemoveEntryList( pthis ); + free_pending_endp( &uhci->pending_endp_pool, struct_ptr( pthis, UHCI_PENDING_ENDP, endp_link ) ); + } + pthis = pnext; + } + unlock_pending_endp_list( &uhci->pending_endp_list_lock ); + + //cancel all the urbs in the urb-list + sync_param.uhci = uhci; + sync_param.context = ( PVOID )dev; + + KeSynchronizeExecution( uhci->pdev_ext->uhci_int, uhci_sync_cancel_urbs_dev, &sync_param ); + + //cancel all the urb in the endp's urb-list + k = 0; + lock_dev( dev, FALSE ); + if( dev->usb_config ) + { + //only for configed dev + for( i = 0; i < dev->usb_config->if_count; i++ ) + { + for( j = 0; j < dev->usb_config->interf[ i ].endp_count; j++ ) + { + ListFirst( &dev->usb_config->interf[ i ].endp[ j ].urb_list, pthis ); + while( pthis ) + { + ListNext( &dev->usb_config->interf[ i ].endp[ j ].urb_list, + pthis, + pnext ); + + RemoveEntryList( pthis ); + InsertHeadList( &temp_list, pthis ); + pthis = pnext; + k++; + } + + } + } + } + ListFirst( &dev->default_endp.urb_list, pthis ); + + while( pthis ) + { + ListNext( &dev->default_endp.urb_list, + pthis, + pnext ); + + RemoveEntryList( pthis ); + InsertHeadList( &temp_list, pthis ); + pthis = pnext; + k++; + } + unlock_dev( dev, FALSE ); + + if( IsListEmpty( &temp_list ) == FALSE ) + { + for( i = 0; i < k ; i++ ) + { + //complete those urbs with error + pthis = RemoveHeadList( &temp_list ); + purb = ( PURB ) pthis; + purb->status = STATUS_DEVICE_DOES_NOT_EXIST; + { + uhci_generic_urb_completion( purb, purb->context ); + } + } + } + + lock_dev( dev, FALSE ) + dev->ref_count -= k; + unlock_dev( dev, FALSE ); + + return TRUE; +} + + +static NTSTATUS +uhci_internal_submit_bulk( +PUHCI_DEV uhci, +PURB urb +) +// +// assume that the urb has its rest_bytes and bytes_to_transfer set +// and bytes_transfered is zeroed. +// dev_lock must be acquired outside +// urb comes from dev's endpoint urb-list. it is already removed from +// the endpoint urb-list. +// +{ + + LONG max_packet_size, td_count, offset, bytes_to_transfer, data_load; + PBYTE start_addr; + PUHCI_TD ptd; + PUHCI_QH pqh; + LIST_ENTRY td_list, *pthis, *pnext; + BOOL old_toggle, toggle, ret; + UCHAR pid; + + if( uhci == NULL || urb == NULL ) + return STATUS_INVALID_PARAMETER; + + max_packet_size = endp_max_packet_size( urb->pendp ); + if( urb->bytes_to_transfer == 0 ) + { + return STATUS_INVALID_PARAMETER; + } + + td_count = ( urb->bytes_to_transfer + max_packet_size - 1 ) / max_packet_size; + + lock_td_pool( &uhci->td_pool, TRUE ); + if( can_transfer( &uhci->td_pool, td_count ) == FALSE ) + { + unlock_td_pool( &uhci->td_pool, TRUE ); + return STATUS_NO_MORE_ENTRIES; + } + + ptd = alloc_tds( &uhci->td_pool, td_count ); + unlock_td_pool( &uhci->td_pool, TRUE ); + + if( ptd == NULL ) + { + return STATUS_UNSUCCESSFUL; + } + + InitializeListHead( &td_list ); + InsertTailList( &ptd->ptde->vert_link, &td_list ); + + ListFirst( &td_list, pthis ); + ListNext( &td_list, pthis, pnext ); + + start_addr = &urb->data_buffer[ urb->data_length - urb->rest_bytes ]; + offset = 0; + + old_toggle = toggle = urb->pendp->flags & USB_ENDP_FLAG_DATATOGGLE ? TRUE : FALSE; + bytes_to_transfer = urb->bytes_to_transfer; + + urb->pipe = ( ( max_packet_size - 1 ) << 21 ) + | ( ( ULONG )endp_num( urb->pendp ) << 15 ) + | ( dev_from_endp( urb->pendp )->dev_addr << 8 ) + | ( ( ULONG )endp_dir( urb->pendp ) ) + | USB_ENDPOINT_XFER_BULK; + + pid = ( ( ( ULONG )urb->pendp->pusb_endp_desc->bEndpointAddress & USB_DIR_IN ) ? USB_PID_IN : USB_PID_OUT ); + while( pthis ) + { + ptd = ( ( PTD_EXTENSION )pthis )->ptd; + + data_load = max_packet_size < bytes_to_transfer ? max_packet_size : bytes_to_transfer; + ptd->purb = urb; + uhci_fill_td( ptd, + ( 3 << TD_CTRL_C_ERR_SHIFT ) + | ( TD_CTRL_ACTIVE ), + ( ( data_load - 1 ) << 21 ) + | ( toggle << 19 ) + | ( ( ULONG )endp_num( urb->pendp ) << 15 ) + | ( dev_from_endp( urb->pendp )->dev_addr << 8 ) + | pid, + MmGetPhysicalAddress( start_addr + offset ).LowPart ); + + bytes_to_transfer -= data_load; + offset += data_load; + + if( pnext ) + { + ptd->link = ( ( PTD_EXTENSION ) pnext )->ptd->phy_addr; + } + else + { + //Last one, enable ioc and short packet detect if necessary + ptd->link = UHCI_PTR_TERM; + ptd->status |= TD_CTRL_IOC; + if( bytes_to_transfer < max_packet_size && ( pid == USB_PID_IN ) ) + { + //ptd->status |= TD_CTRL_SPD; + } + } + + pthis = pnext; + toggle ^= 1; + if( pthis ) + ListNext( &td_list, pthis, pnext ); + + } + + ListFirst( &td_list, pthis ); + RemoveEntryList( &td_list ); + + lock_qh_pool( &uhci->qh_pool, TRUE ); + pqh = alloc_qh( &uhci->qh_pool ); + unlock_qh_pool( &uhci->qh_pool, TRUE); + + if( pqh == NULL ) + { + lock_td_pool( &uhci->td_pool, TRUE ); + + if( pthis ) + free_tds( &uhci->td_pool, ( ( PTD_EXTENSION )pthis )->ptd ); + + unlock_td_pool( &uhci->td_pool, TRUE ); + return STATUS_NO_MORE_ENTRIES; + + } + + urb->td_count = td_count; + + uhci_insert_tds_qh( pqh, ( ( PTD_EXTENSION )pthis )->ptd ); + uhci_insert_qh_urb( urb, pqh ); + urb->pendp->flags = ( urb->pendp->flags & ~USB_ENDP_FLAG_DATATOGGLE ) | ( toggle ? USB_ENDP_FLAG_DATATOGGLE : 0 ) ; + usb_endp_busy_count_inc( urb->pendp ); + uhci_insert_urb_to_schedule( uhci, urb, ret ); + + if( ret == FALSE ) + { + // undo all we have done + RemoveEntryList( &pqh->pqhe->vert_link ); //remove qh from td_chain + RemoveEntryList( &urb->trasac_list ); + + lock_td_pool( &uhci->td_pool, TRUE ); + if( pthis ) + free_tds( &uhci->td_pool, ( ( PTD_EXTENSION )pthis )->ptd ); + unlock_td_pool( &uhci->td_pool, TRUE ); + + lock_qh_pool( &uhci->qh_pool, TRUE ); + if( pqh ) + free_qh( &uhci->qh_pool, pqh ); + unlock_qh_pool( &uhci->qh_pool, TRUE ); + + InitializeListHead( &urb->trasac_list ); + usb_endp_busy_count_dec( urb->pendp ); + urb->pendp->flags = ( urb->pendp->flags & ~USB_ENDP_FLAG_DATATOGGLE ) | ( old_toggle ? USB_ENDP_FLAG_DATATOGGLE : 0 ) ; + return STATUS_UNSUCCESSFUL; + } + return STATUS_SUCCESS; + +} + +static NTSTATUS +uhci_internal_submit_ctrl( +PUHCI_DEV uhci, +PURB urb +) +{ + + LIST_ENTRY td_list, *pthis, *pnext; + LONG i, td_count; + LONG toggle; + LONG max_packet_size, bytes_to_transfer, bytes_rest, start_idx; + PUHCI_TD ptd; + PUHCI_QH pqh; + ULONG dev_addr; + PUSB_DEV pdev; + BOOL ret; + + if( uhci == NULL || urb == NULL ) + return STATUS_INVALID_PARAMETER; + + toggle = 0; + bytes_rest = urb->rest_bytes; + bytes_to_transfer = urb->bytes_to_transfer; + max_packet_size = endp_max_packet_size( urb->pendp ); + start_idx = urb->data_length - urb->rest_bytes; + td_count = 2 + ( urb->bytes_to_transfer + max_packet_size - 1 ) / max_packet_size; + + lock_td_pool( &uhci->td_pool, TRUE ); + + if( can_transfer( &uhci->td_pool, td_count ) == FALSE ) + { + unlock_td_pool( &uhci->td_pool, TRUE ); + return STATUS_NO_MORE_ENTRIES; + } + + ptd = alloc_tds( &uhci->td_pool, td_count ); + unlock_td_pool( &uhci->td_pool, TRUE ); + + if( ptd == NULL ) + { + return STATUS_UNSUCCESSFUL; + } + + InsertTailList( &ptd->ptde->vert_link, &td_list ); + + ListFirst( &td_list, pthis ); + ListNext( &td_list, pthis, pnext ); + + ptd = ( ( PTD_EXTENSION ) pthis )->ptd; + + pdev = dev_from_endp( urb->pendp ); + dev_addr = pdev->dev_addr; + + if( dev_state( pdev ) <= USB_DEV_STATE_RESET ) + dev_addr = 0; + + usb_dbg_print( DBGLVL_MAXIMUM, ( "uhci_internal_submit_ctrl(): dev_addr =0x%x\n", \ + dev_addr ) ); + + RtlCopyMemory( uhci->io_buf, urb->setup_packet, 8 ); + + if( ( urb->setup_packet[ 0 ] & USB_DIR_IN ) == 0 ) //out + RtlCopyMemory( &uhci->io_buf[ 8 ], urb->data_buffer, bytes_to_transfer ); + else + RtlZeroMemory( &uhci->io_buf[ 8 ], bytes_to_transfer ); + + uhci_fill_td( ptd, + ( 3 << TD_CTRL_C_ERR_SHIFT ) | ( TD_CTRL_ACTIVE ), + ( 7 << 21 ) + | ( ( ( ULONG )endp_num( urb->pendp ) ) << 15 ) + | ( dev_addr << 8 ) + | ( USB_PID_SETUP ), + //uhci->io_buf_logic_addr.LowPart ); + MmGetPhysicalAddress( urb->setup_packet ).LowPart ); + + ptd->link = ( ( PTD_EXTENSION ) pnext )->ptd->phy_addr; + pthis = pnext; + ListNext( &td_list, pthis, pnext ); + + urb->pipe = ( ( max_packet_size - 1 ) << 21 ) + | ( ( ULONG )endp_num( urb->pendp ) << 15 ) + | ( dev_addr << 8 ) + | ( pdev->flags & USB_DEV_FLAG_LOW_SPEED ) + | USB_ENDPOINT_XFER_CONTROL; + + for( i = 0, toggle = 1; ( ( i < td_count - 2 ) && pthis ); i++, toggle ^= 1 ) + { + //construct tds for DATA packets of data stage. + ptd = ( ( PTD_EXTENSION ) pthis )->ptd; + uhci_fill_td( ptd, + ( 3 << TD_CTRL_C_ERR_SHIFT ) + | ( TD_CTRL_ACTIVE ), + ( ( bytes_to_transfer > max_packet_size ? max_packet_size - 1 : bytes_to_transfer - 1 ) << 21 ) + | ( toggle << 19 ) + | ( ( ( ULONG )endp_num( urb->pendp ) ) << 15 ) + | ( dev_addr << 8 ) + | ( ( urb->setup_packet[ 0 ] & USB_DIR_IN ) ? USB_PID_IN : USB_PID_OUT ), + //uhci->io_buf_logic_addr.LowPart + 8 + i * max_packet_size ); + MmGetPhysicalAddress( &urb->data_buffer[ start_idx + i * max_packet_size ] ).LowPart ); + + if( pnext ) + ptd->link = ( ( PTD_EXTENSION ) pnext )->ptd->phy_addr; + + if( i < td_count - 3 ) + { + bytes_to_transfer -= max_packet_size; + } + else + { + if( bytes_to_transfer > 0 ) + { + if( bytes_to_transfer < max_packet_size && ( urb->setup_packet[ 0 ] & USB_DIR_IN ) ) + ptd->status |= TD_CTRL_SPD; + } + } + pthis = pnext; + + if( pthis ) + ListNext( &td_list, pthis, pnext ); + } + + if( pnext ) + ptd->link = ( ( PTD_EXTENSION ) pnext )->ptd->phy_addr; + + ListFirstPrev( &td_list, pthis ); + ptd = ( ( PTD_EXTENSION ) pthis )->ptd; + + //the last is an IN transaction + uhci_fill_td( ptd, + ( 3 << TD_CTRL_C_ERR_SHIFT ) + | ( TD_CTRL_ACTIVE | TD_CTRL_IOC ), + ( UHCI_NULL_DATA_SIZE << 21 ) + | ( 1 << 19 ) + | ( ( ( ULONG )endp_num( urb->pendp ) ) << 15 ) + | ( dev_addr << 8 ) + | ( ( td_count > 2 ) + ? ( ( urb->setup_packet[ 0 ] & USB_DIR_IN ) + ? USB_PID_OUT : USB_PID_IN ) + : USB_PID_IN ), + 0 ); + + ptd->link = UHCI_PTR_TERM; + + ListFirst( &td_list, pthis ); + RemoveEntryList( &td_list ); + + lock_qh_pool( &uhci->qh_pool, TRUE ); + pqh = alloc_qh( &uhci->qh_pool ); + unlock_qh_pool( &uhci->qh_pool, TRUE); + + if( pqh == NULL ) + { + lock_td_pool( &uhci->td_pool, TRUE ); + if( pthis ) + free_tds( &uhci->td_pool, ( ( PTD_EXTENSION )pthis )->ptd ); + unlock_td_pool( &uhci->td_pool, TRUE ); + return STATUS_NO_MORE_ENTRIES; + + } + + urb->td_count = td_count; + + uhci_insert_tds_qh( pqh, ( ( PTD_EXTENSION )pthis )->ptd ); + uhci_insert_qh_urb( urb, pqh ); + + usb_endp_busy_count_inc( urb->pendp ); + uhci_insert_urb_to_schedule( uhci, urb, ret ); + if( ret == FALSE ) + { + RemoveEntryList( &pqh->pqhe->vert_link ); + RemoveEntryList( &urb->trasac_list ); + + lock_td_pool( &uhci->td_pool, TRUE ); + if( pthis ) + free_tds( &uhci->td_pool, ( ( PTD_EXTENSION )pthis )->ptd ); + unlock_td_pool( &uhci->td_pool, TRUE ); + + lock_qh_pool( &uhci->qh_pool, TRUE ); + if( pqh ) + free_qh( &uhci->qh_pool, pqh ); + unlock_qh_pool( &uhci->qh_pool, TRUE ); + + InitializeListHead( &urb->trasac_list ); + usb_endp_busy_count_dec( urb->pendp ); + return STATUS_UNSUCCESSFUL; + } + return STATUS_SUCCESS; +} + +static NTSTATUS +uhci_internal_submit_int( +PUHCI_DEV uhci, +PURB urb +) +{ + LIST_ENTRY td_list, *pthis, *pnext; + LONG i; + LONG toggle = 0; + LONG max_packet_size; + PUHCI_TD ptd; + BOOL ret; + + if( uhci == NULL || urb == NULL ) + return STATUS_INVALID_PARAMETER; + + toggle = ( urb->pendp->flags & USB_ENDP_FLAG_DATATOGGLE ) ? TRUE : FALSE; + max_packet_size = endp_max_packet_size( urb->pendp ); + + if( max_packet_size < urb->data_length || max_packet_size == 0 || max_packet_size > 64 ) + { + return STATUS_INVALID_PARAMETER; + } + + lock_td_pool( &uhci->td_pool, TRUE ); + ptd = alloc_td( &uhci->td_pool ); + unlock_td_pool( &uhci->td_pool, TRUE ); + + if( ptd == NULL ) + return STATUS_NO_MORE_ENTRIES; + + for( i = 1; i <= 7; i++ ) + { + if( ( ( ULONG )max_packet_size ) >> i ) + continue; + else + break; + } + + i --; + i &= 7; + + urb->pipe = ( ( ( ULONG )urb->pendp->pusb_endp_desc->bInterval ) << 24 ) + | ( i << 21 ) + | ( toggle << 19 ) + | ( ( ULONG )endp_num( urb->pendp ) << 15 ) + | ( ( ( ULONG )dev_from_endp( urb->pendp )->dev_addr ) << 8 ) + | USB_DIR_IN + | ( dev_from_endp( urb->pendp )->flags & USB_DEV_FLAG_LOW_SPEED ) + | USB_ENDPOINT_XFER_INT; + + uhci_fill_td( ptd, + ( 3 << TD_CTRL_C_ERR_SHIFT ) + | ( TD_CTRL_ACTIVE ) + | ( ( urb->data_length < max_packet_size ? TD_CTRL_SPD : 0 ) ) + | ( TD_CTRL_IOC ), + ( ( ( ULONG )max_packet_size - 1 ) << 21 ) + | ( toggle << 19 ) + | ( ( ULONG )endp_num( urb->pendp ) << 15 ) + | ( ( ( ULONG )dev_from_endp( urb->pendp )->dev_addr & 0x7f) << 8 ) + | USB_PID_IN, + MmGetPhysicalAddress( urb->data_buffer ).LowPart ); + + toggle ^= 1; + urb->td_count = 1; + + InitializeListHead( &urb->trasac_list ); + InsertTailList( &urb->trasac_list, &ptd->ptde->vert_link ); + + //indirectly guarded by pending_endp_list_lock + if( uhci_claim_bandwidth( uhci, urb, TRUE ) == FALSE ) + { + InitializeListHead( &urb->trasac_list ); + + lock_td_pool( &uhci->td_pool, TRUE ); + free_td( &uhci->td_pool, ptd ); + unlock_td_pool( &uhci->td_pool, TRUE ); + return STATUS_NO_MORE_ENTRIES; + + } + + urb->pendp->flags = ( urb->pendp->flags & ~USB_ENDP_FLAG_DATATOGGLE ) + | ( toggle << 31 ); + usb_endp_busy_count_inc( urb->pendp ); + + uhci_insert_urb_to_schedule( uhci, urb, ret ); + + if( ret == FALSE ) + { + lock_td_pool( &uhci->td_pool, TRUE ); + if( ptd ) + free_td( &uhci->td_pool, ptd ); + unlock_td_pool( &uhci->td_pool, TRUE ); + + InitializeListHead( &urb->trasac_list ); + usb_endp_busy_count_dec( urb->pendp ); + urb->pendp->flags = ( urb->pendp->flags & ~USB_ENDP_FLAG_DATATOGGLE ) | ( ( toggle ^ 1 ) << 31 ); + uhci_claim_bandwidth( uhci, urb, FALSE ); + return STATUS_UNSUCCESSFUL; + } + + return STATUS_SUCCESS; +} + + +static NTSTATUS +uhci_internal_submit_iso( +PUHCI_DEV uhci, +PURB urb +) +{ + PUHCI_TD ptd; + LIST_ENTRY td_list, *pthis, *pnext; + int i; + BOOL toggle, ret; + KIRQL old_irql; + + if( uhci == NULL || urb == NULL ) + return STATUS_INVALID_PARAMETER; + + if( urb->iso_frame_count == 0 ) + return STATUS_INVALID_PARAMETER; + + lock_td_pool( &uhci->td_pool, TRUE ); + + if( can_transfer( &uhci->td_pool, urb->iso_frame_count ) == FALSE ) + { + unlock_td_pool( &uhci->td_pool, TRUE ); + return STATUS_NO_MORE_ENTRIES; + } + + ptd = alloc_tds( &uhci->td_pool, urb->iso_frame_count ); + unlock_td_pool( &uhci->td_pool, TRUE ); + + if( ptd == NULL ) + { + return STATUS_UNSUCCESSFUL; + } + + InsertTailList( &ptd->ptde->vert_link, &td_list ); + ListFirst( &td_list, pthis ); + + urb->td_count = urb->iso_frame_count; + + urb->pipe = ( ( ( ULONG )urb->iso_packet_desc[ 0 ].length ) << 21 ) + | ( ( ULONG )endp_num( urb->pendp ) << 15 ) + | ( ( ( ULONG )dev_from_endp( urb->pendp )->dev_addr ) << 8 ) + | ( ( ULONG )endp_dir( urb->pendp ) ) + | USB_ENDPOINT_XFER_ISOC; + + + for( i = 0; i < urb->iso_frame_count && pthis; i++ ) + { + ptd = ( ( PTD_EXTENSION ) pthis )->ptd; + uhci_fill_td( ptd, + ( 3 << TD_CTRL_C_ERR_SHIFT ) + | ( TD_CTRL_ACTIVE ) + | ( TD_CTRL_IOS ), + ( ( ( ULONG )urb->iso_packet_desc[ i ].length - 1 ) << 21 ) + | ( 0 << 19 ) + | ( ( ULONG )endp_num( urb->pendp ) << 15 ) + | ( ( ( ULONG )dev_from_endp( urb->pendp )->dev_addr ) << 8 ) + | ( ( urb->pendp->pusb_endp_desc->bEndpointAddress & USB_DIR_IN ) + ? USB_PID_OUT : USB_PID_IN ), + MmGetPhysicalAddress( &urb->data_buffer[ urb->iso_packet_desc[ i ].offset ] ).LowPart ); + + toggle ^= 1; + ListNext( &td_list, pthis, pnext ); + pthis = pnext; + } + + ptd->status |= TD_CTRL_IOC; //need interrupt + + ListFirst( &td_list, pthis ); + RemoveEntryList( &td_list ); + + InsertTailList( pthis, &urb->trasac_list ); + + //indirectly guarded by pending_endp_list_lock + if( uhci_claim_bandwidth( uhci, urb, TRUE ) == FALSE ) + { + //bad news: we can not allocate the enough bandwidth for the urb + RemoveEntryList( &urb->trasac_list ); + InitializeListHead( &urb->trasac_list ); + + lock_td_pool( &uhci->td_pool, TRUE ); + free_tds( &uhci->td_pool, ( ( PTD_EXTENSION )pthis )->ptd ); + unlock_td_pool( &uhci->td_pool, TRUE ); + return STATUS_NO_MORE_ENTRIES; + + } + usb_endp_busy_count_inc( urb->pendp ); + uhci_insert_urb_to_schedule( uhci, urb, ret ); + if( ret == FALSE ) + { + usb_endp_busy_count_dec( urb->pendp ); + RemoveEntryList( &urb->trasac_list ); + + lock_td_pool( &uhci->td_pool, TRUE ); + free_tds( &uhci->td_pool, ( ( PTD_EXTENSION )pthis )->ptd ); + unlock_td_pool( &uhci->td_pool, TRUE ); + uhci_claim_bandwidth( uhci, urb, FALSE ); + return STATUS_UNSUCCESSFUL; + } + + return STATUS_SUCCESS; +} + +static BOOL +uhci_is_xfer_finished( +PURB urb +) +// runs in uhci_isr +{ + PLIST_ENTRY pthis, pnext; + PUHCI_TD ptd; + PUHCI_QH pqh; + BOOL ret; + PTD_EXTENSION ptde; + + if( urb->last_finished_td == NULL ) + { + urb->last_finished_td = &urb->trasac_list; + } + + if( &urb->trasac_list == urb->last_finished_td ) + ListFirst( &urb->trasac_list, pthis ) + else + ListNext( &urb->trasac_list, urb->last_finished_td, pthis ); + + while( pthis ) + { + if( ( ( ( PTD_EXTENSION )pthis )->flags & UHCI_ITEM_FLAG_TYPE ) + != UHCI_ITEM_FLAG_TD ) + { + ListNext( &urb->trasac_list, pthis, pnext ); + pthis = pnext; + continue; + } + else + { + ptde = ( PTD_EXTENSION )pthis; + ptd = ptde->ptd; + ASSERT( ptd != NULL ); + + if( ptd->status & TD_CTRL_ACTIVE ) + { + //still active + ret = FALSE; + break; + } + //let's see whether error occured + if( ( ptd->status & TD_CTRL_ANY_ERROR ) == 0 ) + { + urb->last_finished_td = pthis; + ListNext( &urb->trasac_list, pthis, pnext ); + pthis = pnext; + continue; + } + else + { + urb->status = ptd->status; + pthis = NULL; + continue; + } + } + + } + + if( pthis == NULL ) + ret = TRUE; + + return ret; +} + +static BOOL +uhci_remove_urb_from_schedule( +PUHCI_DEV uhci, +PURB urb +) +// executed in isr, and have frame_list_lock acquired, so +// never try to acquire any spin-lock +// remove the bulk urb from schedule, and mark it not in +// the schedule +{ + BOOL ret = FALSE; + { + switch( urb->pipe & USB_ENDPOINT_XFERTYPE_MASK ) + { + case USB_ENDPOINT_XFER_BULK: + { + ret = uhci_remove_bulk_from_schedule( uhci, urb ); + break; + } + case USB_ENDPOINT_XFER_CONTROL: + { + ret = uhci_remove_ctrl_from_schedule( uhci, urb ); + break; + } + case USB_ENDPOINT_XFER_INT: + { + ret = uhci_remove_int_from_schedule( uhci, urb ); + break; + } + case USB_ENDPOINT_XFER_ISOC: + { + ret = uhci_remove_iso_from_schedule( uhci, urb ); + break; + } + } + } + return ret; +} + +static BOOL +uhci_remove_bulk_from_schedule( +PUHCI_DEV uhci, +PURB urb +) +// executed in isr, and have frame_list_lock acquired, so +// never try to acquire any spin-lock +// remove the bulk urb from schedule, and mark it not in +// the schedule +{ + + PUHCI_QH pqh, pnext_qh, pprev_qh; + PLIST_ENTRY pthis, pnext, pprev; + LONG i; + + if( uhci == NULL || urb == NULL ) + return FALSE; + + ListFirst( &urb->trasac_list, pthis ); + pqh = ( ( PQH_EXTENSION )pthis )->pqh; + + ListFirst( &pqh->pqhe->hori_link, pnext ); + ListFirstPrev( &pqh->pqhe->hori_link, pprev ); + + if( pprev == NULL || pnext == NULL ) + return FALSE; + + pnext_qh = struct_ptr( pnext, QH_EXTENSION, hori_link )->pqh; + pprev_qh = struct_ptr( pprev, QH_EXTENSION, hori_link )->pqh; + + if( pprev != pnext ) + { + //not the last one + pprev_qh->link = pnext_qh->phy_addr; + } + else + { + //only two qhs in the list + for( i = 0; i < UHCI_MAX_SKELQHS; i++ ) + { + if( pprev_qh == uhci->skel_qh[ i ] ) + { + break; + } + } + ASSERT( i < UHCI_MAX_SKELQHS - 1 ); + pprev_qh->link = uhci->skel_qh[ i + 1 ]->phy_addr; + } + RemoveEntryList( &pqh->pqhe->hori_link ); + + urb->flags &= ~URB_FLAG_IN_SCHEDULE; + + if( ( urb->pipe & USB_DEV_FLAG_LOW_SPEED ) == 0 ) + uhci_drop_fsbr( uhci ); + + return TRUE; +} + +static BOOL +uhci_remove_iso_from_schedule( +PUHCI_DEV uhci, +PURB urb +) +{ + PUHCI_TD ptd, pnext_td, pprev_td; + PLIST_ENTRY pthis, pnext, pprev; + int i, idx; + + if( uhci == NULL || urb == NULL ) + return FALSE; + + ListFirst( &urb->trasac_list, pthis ); + + for( i = 0; i < urb->iso_frame_count && pthis; i++ ) + { + ptd = ( ( PTD_EXTENSION )pthis )->ptd; + idx = ( urb->iso_start_frame + i ) & ( UHCI_MAX_FRAMES - 1 ); + + ListFirstPrev( &ptd->ptde->hori_link, pprev ); + + if( pprev == NULL ) + return FALSE; + + if( pprev == &uhci->frame_list_cpu[ idx ].td_link ) + { + uhci->frame_list[ idx ] = ptd->link; + } + else + { + pprev_td = struct_ptr( pprev, TD_EXTENSION, hori_link )->ptd; + pprev_td->link = ptd->link; + } + + RemoveEntryList( &ptd->ptde->hori_link ); + ListNext( &urb->trasac_list, pthis, pnext ); + pthis = pnext; + } + + urb->flags &= ~URB_FLAG_IN_SCHEDULE; + return TRUE; +} + +static BOOL +uhci_remove_int_from_schedule( +PUHCI_DEV uhci, +PURB urb +) +{ + PUHCI_TD ptd, pnext_td, pprev_td; + PLIST_ENTRY pthis, pnext, pprev; + LONG i; + + if( uhci == NULL || urb == NULL ) + return FALSE; + + ListFirst( &urb->trasac_list, pthis ); + ptd = ( ( PTD_EXTENSION )pthis )->ptd; + ListFirst( &ptd->ptde->hori_link, pnext ); + ListFirstPrev( &ptd->ptde->hori_link, pprev ); + + if( pprev == NULL || pnext == NULL ) + return FALSE; + + pnext_td = struct_ptr( pnext, TD_EXTENSION, hori_link )->ptd; + pprev_td = struct_ptr( pprev, TD_EXTENSION, hori_link )->ptd; + + if( pprev_td != pnext_td ) + pprev_td->link = pnext_td->phy_addr; + else + { + //the last one + for( i = UHCI_MAX_SKELTDS - 2; i >= 0; i-- ) + { + //UHCI_MAX_SKELTDS -1 skel tds for int transfer + if( pprev_td == uhci->skel_td[ i ] ) + break; + } + + ASSERT( i >= 0 ); + if( i == 0 ) + { + pprev_td->link = uhci->skel_qh[ 0 ]->phy_addr; + } + else + { + pprev_td->link = uhci->skel_td[ i - 1 ]->phy_addr; + } + } + RemoveEntryList( &ptd->ptde->hori_link ); + + urb->flags &= ~URB_FLAG_IN_SCHEDULE; + return TRUE; +} + +static BOOL +uhci_insert_tds_qh( +PUHCI_QH pqh, +PUHCI_TD td_chain +) +{ + if( pqh == NULL || td_chain == NULL ) + return FALSE; + + InsertTailList( &td_chain->ptde->vert_link, &pqh->pqhe->vert_link ); + pqh->element = td_chain->phy_addr; + return TRUE; +} + +static BOOL +uhci_insert_qh_urb( +PURB urb, +PUHCI_QH qh_chain +) +{ + if( urb == NULL || qh_chain == NULL ) + return FALSE; + + InsertTailList( &qh_chain->pqhe->vert_link, &urb->trasac_list ); + qh_chain->pqhe->purb = urb; + return TRUE; +} + +static BOOL +uhci_insert_urb_schedule( +PUHCI_DEV uhci, +PURB urb +) +// must have dev_lock and frame_list_lock acquired +{ + PUHCI_QH pqh, pskel_qh, pnext_qh; + PUHCI_TD ptd, plast_td; + PLIST_ENTRY pthis, pnext; + int i; + + if( uhci == NULL || urb == NULL ) + return FALSE; + + ListFirst( &urb->trasac_list, pthis ); + if( pthis == NULL ) + return FALSE; + + InsertTailList( &uhci->urb_list, ( PLIST_ENTRY )urb ); + + urb->flags &= ~URB_FLAG_STATE_MASK; + urb->flags |= URB_FLAG_STATE_IN_PROCESS | URB_FLAG_IN_SCHEDULE; + + + switch( endp_type( urb->pendp ) ) + { + case USB_ENDPOINT_XFER_CONTROL: + { + pqh = ( ( PQH_EXTENSION )pthis )->pqh; + + if( ( dev_from_endp( urb->pendp )->flags & USB_DEV_FLAG_LOW_SPEED ) == 0 ) + { + pskel_qh = uhci->skel_hs_control_qh; + pnext_qh = uhci->skel_bulk_qh; + } + else + { + pskel_qh = uhci->skel_ls_control_qh; + pnext_qh = uhci->skel_hs_control_qh; + } + + ListFirstPrev( &pskel_qh->pqhe->hori_link, + pthis ); + + if( pthis == NULL ) + pthis = &pskel_qh->pqhe->hori_link; + + InsertTailList( &pskel_qh->pqhe->hori_link, &pqh->pqhe->hori_link ); + pqh->link = pnext_qh->phy_addr; + struct_ptr( pthis, QH_EXTENSION, hori_link )->pqh->link = pqh->phy_addr; + + //full speed band reclaimation + if( ( urb->pipe & USB_DEV_FLAG_LOW_SPEED ) == 0 ) + { + uhci->fsbr_cnt++; + if( uhci->fsbr_cnt == 1 ) + { + uhci->skel_term_qh->link = uhci->skel_hs_control_qh->phy_addr; + } + } + return TRUE; + } + case USB_ENDPOINT_XFER_BULK: + { + pqh = ( ( PQH_EXTENSION )pthis )->pqh; + + ListFirstPrev( &uhci->skel_bulk_qh->pqhe->hori_link, + pthis ); + + if( pthis == NULL ) + pthis = &uhci->skel_bulk_qh->pqhe->hori_link; + + InsertTailList( &uhci->skel_bulk_qh->pqhe->hori_link, + &pqh->pqhe->hori_link ); + + pqh->link = uhci->skel_term_qh->phy_addr; + struct_ptr( pthis, QH_EXTENSION, hori_link )->pqh->link = + pqh->phy_addr; + + //full speed band reclaimation + uhci->fsbr_cnt++; + if( uhci->fsbr_cnt == 1 ) + { + uhci->skel_term_qh->link = uhci->skel_hs_control_qh->phy_addr;; + } + + return TRUE; + } + case USB_ENDPOINT_XFER_INT: + { + //bandwidth claim is done outside + ptd = ( ( PTD_EXTENSION ) pthis )->ptd; + + get_int_idx( urb, i ); + + ListFirstPrev( &uhci->skel_td[ i ]->ptde->hori_link, pthis ); + if( pthis == NULL ) + pthis = &uhci->skel_td[ i ]->ptde->hori_link; + + InsertTailList( &uhci->skel_td[ i ]->ptde->hori_link, &ptd->ptde->hori_link ); + + if( i > 0 ) + { + ptd->link = uhci->skel_td[ i - 1 ]->phy_addr; + } + else if( i == 0 ) + { + ptd->link = uhci->skel_qh[ 0 ]->phy_addr; + } + //finally link the previous td to this td + struct_ptr( pthis, TD_EXTENSION, hori_link )->ptd->link = ptd->phy_addr; + return TRUE; + } + case USB_ENDPOINT_XFER_ISOC: + { + + for( i = 0; i < urb->iso_frame_count; i++ ) + { + ptd = ( ( PTD_EXTENSION ) pthis )->ptd; + InsertTailList( &uhci->frame_list_cpu[ ( urb->iso_start_frame + i ) & 0x3ff ].td_link, + &ptd->ptde->hori_link ); + + if( IsListEmpty( &uhci->frame_list_cpu[ ( urb->iso_start_frame + i ) & 0x3ff ].td_link ) == TRUE ) + { + ptd->link = uhci->frame_list[ ( urb->iso_start_frame + i ) & 0x3ff ]; + uhci->frame_list[ i ] = ptd->phy_addr; + } + else + { + ListFirstPrev( &uhci->frame_list_cpu[ ( urb->iso_start_frame + i ) & 0x3ff ].td_link, pnext ); + plast_td = struct_ptr( pnext, TD_EXTENSION, hori_link )->ptd; + ptd->link = plast_td->link; + plast_td->link = ptd->phy_addr; + } + + ListNext( &urb->trasac_list, pthis, pnext ); + pthis = pnext; + } + return TRUE; + + } + } + return FALSE; +} + +BOOLEAN +uhci_sync_insert_urb_schedule( +PVOID context +) +//this function used as the KeSynchronizeExecution param to delegate control to uhci_insert_urb_schedule +{ + PSYNC_PARAM sync_param; + PUHCI_DEV uhci; + PURB purb; + + sync_param = ( PSYNC_PARAM ) context; + if( sync_param == NULL ) + return FALSE; + + uhci = sync_param->uhci; + purb = ( PURB )sync_param->context; + + if( uhci == NULL || purb == NULL ) + return ( UCHAR )sync_param->ret = FALSE; + + return ( UCHAR )( sync_param->ret = uhci_insert_urb_schedule( uhci, purb ) ); +} + +static BOOL +uhci_claim_bandwidth( +PUHCI_DEV uhci, +PURB urb, +BOOL claim_bw //true to claim band-width, false to free band-width +) +// be sure pending_endp_list_lock acuqired +{ + + UCHAR type; + BOOL ls, can_alloc; + LONG bus_time, us; + LONG i, idx, j, start_frame, interval; + + if( urb == NULL ) + return FALSE; + + can_alloc = TRUE; + + type = ( UCHAR )( urb->pipe & USB_ENDPOINT_XFERTYPE_MASK ); + if( type == USB_ENDPOINT_XFER_BULK || type == USB_ENDPOINT_XFER_CONTROL ) + { + return FALSE; + } + + ls = ( urb->pipe & USB_DEV_FLAG_LOW_SPEED ) + ? TRUE : FALSE; + + if( type == USB_ENDPOINT_XFER_INT ) + { + start_frame = 0; + i = urb->data_length; + bus_time = usb_calc_bus_time( ls, FALSE, FALSE, i ); + us = ns_to_us( bus_time ); + + i = ( urb->pipe >> 24 ); //polling interval + + for( interval = 0, j = 0; j < 8; j++ ) + { + if( i & ( 1 << j ) ) + { + interval = j; + } + } + + interval = 1 << interval; + start_frame = interval - 1; + + if( claim_bw ) + { + + for( idx = 0; idx < UHCI_MAX_FRAMES; idx += interval ) + { + if( uhci->frame_bw[ idx ] < us ) + { + can_alloc = FALSE; + break; + } + } + + if( !can_alloc ) + { + return FALSE; + } + + for( idx = start_frame; idx < UHCI_MAX_FRAMES; idx += interval ) + { + uhci->frame_bw[ idx ] -= us; + } + } + else + { + for( idx = start_frame; idx < UHCI_MAX_FRAMES; idx += interval ) + { + uhci->frame_bw[ idx ] += us; + } + } + + } + else if( type == USB_ENDPOINT_XFER_ISOC ) + { + if( claim_bw ) + { + for( i = 0; i < urb->iso_frame_count; i++ ) + { + bus_time = usb_calc_bus_time( + FALSE, + ( urb->pipe & USB_DIR_IN ) + ? TRUE : FALSE, + TRUE, + urb->iso_packet_desc[ i ].length ); + + urb->iso_packet_desc[ i ].bus_time = ns_to_us( bus_time ); + } + + for( i = 0; i < urb->iso_frame_count ; i++ ) + { + if( uhci->frame_bw[ ( urb->iso_start_frame + i ) & 0x3ff ] + < urb->iso_packet_desc[ i ].bus_time ) + { + can_alloc = FALSE; + break; + } + } + + if( !can_alloc ) + { + return FALSE; + } + + for( i = 0; i < urb->iso_frame_count ; i++ ) + { + uhci->frame_bw[ ( urb->iso_start_frame + i ) & 0x3ff ] + -= urb->iso_packet_desc[ i ].bus_time; + } + } + else + { + for( i = 0; i < urb->iso_frame_count ; i++ ) + { + uhci->frame_bw[ ( urb->iso_start_frame + i ) & 0x3ff ] + += urb->iso_packet_desc[ i ].bus_time; + } + } + + } + + return TRUE; +} + + +BOOLEAN +uhci_sync_cancel_urb( +PVOID context +) +{ + //cancel a single urb + PUHCI_DEV uhci; + PSYNC_PARAM sync_param; + PURB purb2, dest_urb; + PLIST_ENTRY pthis, pnext; + BOOL found = FALSE; + + if( context == NULL ) + return FALSE; + + sync_param = ( PSYNC_PARAM )context; + uhci = sync_param->uhci; + dest_urb = ( PURB )sync_param->context; + + if( uhci == NULL || dest_urb == NULL ) + return ( UCHAR )sync_param->ret = FALSE; + + ListFirst( &uhci->urb_list, pthis ); + while( pthis ) + { + purb2 = ( PURB ) pthis; + if( purb2 == dest_urb ) + { + found = TRUE; + purb2->flags |= URB_FLAG_FORCE_CANCEL; + break; + } + ListNext( &uhci->urb_list, pthis, pnext ); + pthis = pnext; + } + if( found ) + uhci->skel_term_td->status |= TD_CTRL_IOC; + + return ( UCHAR )( sync_param->ret = found ); + +} + +NTSTATUS +uhci_cancel_urb( +PUHCI_DEV uhci, +PUSB_DEV pdev, +PUSB_ENDPOINT pendp, +PURB purb +) +//note any fields of the purb can not be referenced unless it is found in some queue +{ + + NTSTATUS status; + PLIST_ENTRY pthis, pnext; + BOOL found; + PURB purb2; + + SYNC_PARAM sync_param; + + USE_IRQL; + + if( uhci == NULL || purb == NULL || pdev == NULL || pendp == NULL ) + return STATUS_INVALID_PARAMETER; + + lock_dev( pdev, FALSE ); + + if( dev_state( pdev ) == USB_DEV_STATE_ZOMB ) + { + unlock_dev( pdev, FALSE ); + //delegate to remove device for this job + return STATUS_DEVICE_DOES_NOT_EXIST; + } + + if( dev_from_endp( pendp ) != pdev ) + { + unlock_dev( pdev, FALSE ); + return STATUS_INVALID_PARAMETER; + } + + if( endp_state( pendp ) == USB_ENDP_FLAG_STALL ) + { + //it will be canceled in uhci_process_pending_endp + unlock_dev( pdev, FALSE ); + return USB_STATUS_ENDPOINT_HALTED; + } + + found = FALSE; + ListFirst( &pendp->urb_list, pthis ); + while( pthis ) + { + purb2 = ( PURB ) pthis; + if( purb2 == purb ) + { + found = TRUE; + RemoveEntryList( pthis ); + InitializeListHead( pthis ); + break; + } + ListNext( &pendp->urb_list, pthis, pnext ); + pthis = pnext; + } + unlock_dev( pdev, FALSE ); + + if( found ) + { + purb->status = STATUS_CANCELLED; + + uhci_generic_urb_completion( purb, purb->context ); + + lock_dev( pdev, FALSE ); + pdev->ref_count --; + unlock_dev( pdev, FALSE ); + return STATUS_SUCCESS; + } + + // search the urb in the urb-list and try to cancel + sync_param.uhci = uhci; + sync_param.context = purb; + + KeSynchronizeExecution( uhci->pdev_ext->uhci_int, uhci_sync_cancel_urb, &sync_param ); + + found = sync_param.ret; + + if( found ) + return USB_STATUS_CANCELING; + + return STATUS_INVALID_PARAMETER; +} + +VOID +uhci_generic_urb_completion( +PURB purb, +PVOID context +) +{ + PUSB_DEV pdev; + KIRQL cur_irql; + USE_IRQL; + + old_irql = KeGetCurrentIrql(); + if( old_irql > DISPATCH_LEVEL ) + TRAP(); + + if( old_irql < DISPATCH_LEVEL ) + KeRaiseIrql(DISPATCH_LEVEL, &old_irql); + + pdev = purb->pdev; + if( purb == NULL ) + return; + + if( pdev == NULL ) + return; + + lock_dev( pdev, TRUE ); + + if( dev_state( pdev ) == USB_DEV_STATE_ZOMB ) + { + unlock_dev( pdev, TRUE ); + goto LBL_CLIENT_PROCESS; + } + if( usb_error( purb->status ) ) + { + pdev->error_count++; + } + + if( purb->pendp == &pdev->default_endp ) + { + if( usb_halted( purb->status ) ) + { + pdev->time_out_count++; + if( pdev->time_out_count > 3 ) + { + dev_set_state( pdev, USB_DEV_STATE_ZOMB ); + uhci_dbg_print( DBGLVL_MAXIMUM, ("uhci_generic_urb_completion(): contiguous error 3 times, dev 0x%x is deactivated\n", pdev ) ); + } + } + else + pdev->time_out_count = 0; + + } + unlock_dev( pdev, TRUE ); + + LBL_CLIENT_PROCESS: + if( purb->completion ) + purb->completion( purb, context ); + + if( old_irql < DISPATCH_LEVEL ) + KeLowerIrql( old_irql ); + + return; +} + + +NTSTATUS +uhci_rh_submit_urb( +PUSB_DEV pdev, +PURB purb +) +{ + PUSB_DEV_MANAGER dev_mgr; + PTIMER_SVC ptimer; + PUSB_CTRL_SETUP_PACKET psetup; + PUHCI_DEV uhci; + NTSTATUS status; +#ifndef INCLUDE_EHCI + PHUB_EXTENSION hub_ext; +#else + PHUB2_EXTENSION hub_ext; +#endif + PUSB_PORT_STATUS ps, psret; + LONG i; + + USE_IRQL; + if( pdev == NULL || purb == NULL ) + return STATUS_INVALID_PARAMETER; + + dev_mgr = dev_mgr_from_dev( pdev ); + + KeAcquireSpinLock( &dev_mgr->timer_svc_list_lock, &old_irql ); + lock_dev( pdev, FALSE ); + if( dev_state( pdev ) == USB_DEV_STATE_ZOMB ) + { + unlock_dev( pdev, FALSE ); + KeReleaseSpinLock( &dev_mgr->timer_svc_list_lock, old_irql ); + return STATUS_DEVICE_DOES_NOT_EXIST; + } + + uhci = uhci_from_hcd( pdev->hcd ); + psetup = ( PUSB_CTRL_SETUP_PACKET ) purb->setup_packet; + +#ifndef INCLUDE_EHCI + hub_ext = ( ( PHUB_EXTENSION )pdev->dev_ext ); +#else + hub_ext = ( ( PHUB2_EXTENSION )pdev->dev_ext ); +#endif + + switch( endp_type( purb->pendp ) ) + { + case USB_ENDPOINT_XFER_CONTROL: + { + if( psetup->bmRequestType == 0xa3 \ + && psetup->bRequest == USB_REQ_GET_STATUS ) + { + //get-port-status + if( psetup->wIndex == 0 \ + || psetup->wIndex > 2 \ + || psetup->wLength < 4 ) + { + purb->status = STATUS_INVALID_PARAMETER; + break; + } + if( psetup->wIndex == 1 ) + { + status = READ_PORT_USHORT( ( PUSHORT )( uhci->port_base + USBPORTSC1 ) ); + ps = &hub_ext->rh_port1_status; + } + else + { + status = READ_PORT_USHORT( ( PUSHORT )( uhci->port_base + USBPORTSC2 ) ); + ps = &hub_ext->rh_port2_status; + } + + psret = ( PUSB_PORT_STATUS )purb->data_buffer; + ps->wPortStatus = 0; + + if( status & USBPORTSC_CCS ) + { + ps->wPortStatus |= USB_PORT_STAT_CONNECTION; + } + if( status & USBPORTSC_PE ) + { + ps->wPortStatus |= USB_PORT_STAT_ENABLE; + } + if( status & USBPORTSC_PR ) + { + ps->wPortStatus |= USB_PORT_STAT_RESET; + } + if( status & USBPORTSC_SUSP ) + { + ps->wPortStatus |= USB_PORT_STAT_SUSPEND; + } + if( status & USBPORTSC_LSDA ) + { + ps->wPortStatus |= USB_PORT_STAT_LOW_SPEED; + } + + //always power on + ps->wPortStatus |= USB_PORT_STAT_POWER; + + //now set change field + if( status & USBPORTSC_CSC ) + { + ps->wPortChange |= USB_PORT_STAT_C_CONNECTION; + } + if( status & USBPORTSC_PEC ) + { + ps->wPortChange |= USB_PORT_STAT_C_ENABLE; + } + + //don't touch other fields, will be filled by + //other function + + usb_dbg_print( DBGLVL_MAXIMUM, ( \ + "uhci_rh_submit_urb(): get port status, wPortStatus=0x%x, wPortChange=0x%x, address=0x%x\n", \ + ps->wPortStatus, + ps->wPortChange, + ps ) ); + + psret->wPortChange = ps->wPortChange; + psret->wPortStatus = ps->wPortStatus; + + purb->status = STATUS_SUCCESS; + + break; + } + else if( psetup->bmRequestType == 0x23 \ + && psetup->bRequest == USB_REQ_CLEAR_FEATURE ) + { + //clear-port-feature + if( psetup->wIndex == 0 || psetup->wIndex > 2 ) + { + purb->status = STATUS_INVALID_PARAMETER; + break; + } + if( psetup->wIndex == 1 ) + { + i = USBPORTSC1; + ps = &hub_ext->rh_port1_status; + } + else + { + i = USBPORTSC2; + ps = &hub_ext->rh_port2_status; + } + + purb->status = STATUS_SUCCESS; + switch( psetup->wValue ) + { + case USB_PORT_FEAT_C_CONNECTION: + { + ps->wPortChange &= ~USB_PORT_STAT_C_CONNECTION; + SET_RH_PORTSTAT( i, USBPORTSC_CSC ); + status = READ_PORT_USHORT( ( PUSHORT ) ( uhci->port_base + i ) ); + usb_dbg_print( DBGLVL_MAXIMUM, ( "uhci_rh_submit_urb(): clear csc, port%d=0x%x\n", psetup->wIndex, status ) ); + break; + } + case USB_PORT_FEAT_C_ENABLE: + { + ps->wPortChange &= ~USB_PORT_STAT_C_ENABLE; + SET_RH_PORTSTAT( i, USBPORTSC_PEC ); + status = READ_PORT_USHORT( ( PUSHORT ) ( uhci->port_base + i ) ); + usb_dbg_print( DBGLVL_MAXIMUM, ( "uhci_rh_submit_urb(): clear pec, port%d=0x%x\n", psetup->wIndex, status ) ); + break; + } + case USB_PORT_FEAT_C_RESET: + { + ps->wPortChange &= ~USB_PORT_STAT_C_RESET; + //the reset signal is down in rh_timer_svc_reset_port_completion + //so enable the port here + status = READ_PORT_USHORT( ( PUSHORT ) ( uhci->port_base + i ) ); + usb_dbg_print( DBGLVL_MAXIMUM, ( "uhci_rh_submit_urb(): clear pr, enable pe, port%d=0x%x\n", psetup->wIndex, status ) ); + break; + } + case USB_PORT_FEAT_ENABLE: + { + ps->wPortStatus &= ~USB_PORT_STAT_ENABLE; + CLR_RH_PORTSTAT( i, USBPORTSC_PE ); + status = READ_PORT_USHORT( ( PUSHORT ) ( uhci->port_base + i ) ); + usb_dbg_print( DBGLVL_MAXIMUM, ( "uhci_rh_submit_urb(): clear pe, port%d=0x%x\n", psetup->wIndex, status ) ); + break; + } + default: + purb->status = STATUS_UNSUCCESSFUL; + } + break; + } + else if( psetup->bmRequestType == 0xd3 \ + && psetup->bRequest == HUB_REQ_GET_STATE ) + { + // get bus state + if( psetup->wIndex == 0 ||\ + psetup->wIndex > 2 ||\ + psetup->wLength == 0 ) + { + purb->status = STATUS_INVALID_PARAMETER; + break; + } + + if( psetup->wIndex == 1 ) + { + i = USBPORTSC1; + } + else + { + i = USBPORTSC2; + } + status = READ_PORT_USHORT( ( PUSHORT )( uhci->port_base + i ) ); + purb->data_buffer[ 0 ] = ( status & USBPORTSC_LS ); + + // reverse the order + purb->data_buffer[ 0 ] ^= 0x3; + purb->status = STATUS_SUCCESS; + break; + } + else if( psetup->bmRequestType == 0x23 \ + && psetup->bRequest == USB_REQ_SET_FEATURE ) + { + //reset port + if( psetup->wValue != USB_PORT_FEAT_RESET ) + { + purb->status = STATUS_INVALID_PARAMETER; + uhci_dbg_print( DBGLVL_MAXIMUM, ("uhci_rh_submit_urb(): set feature with wValue=0x%x\n", psetup->wValue ) ); + break; + } + if( psetup->wIndex == 1 ) + { + i = USBPORTSC1; + } + else + { + i = USBPORTSC2; + } + + ptimer = alloc_timer_svc( &dev_mgr->timer_svc_pool, 1 ); + ptimer->threshold = 0; // within [ 50ms, 60ms ], one tick is 10 ms + ptimer->context = ( ULONG )purb; + ptimer->pdev = pdev; + ptimer->func = rh_timer_svc_reset_port_completion; + + //start the timer + pdev->ref_count += 2; //one for timer and one for urb + + status = READ_PORT_USHORT( ( PUSHORT ) ( uhci->port_base + i ) ); + usb_dbg_print( DBGLVL_MAXIMUM, ( "uhci_rh_submit_urb(): reset port, port%d=0x%x\n", psetup->wIndex, status ) ); + InsertTailList(&dev_mgr->timer_svc_list, &ptimer->timer_svc_link ); + purb->status = STATUS_PENDING; + } + else + { + purb->status = STATUS_INVALID_PARAMETER; + } + break; + } + case USB_ENDPOINT_XFER_INT: + { + ptimer = alloc_timer_svc( &dev_mgr->timer_svc_pool, 1 ); + ptimer->threshold = RH_INTERVAL; + ptimer->context = ( ULONG )purb; + ptimer->pdev = pdev; + ptimer->func = rh_timer_svc_int_completion; + + //start the timer + InsertTailList(&dev_mgr->timer_svc_list, &ptimer->timer_svc_link ); + + usb_dbg_print( DBGLVL_MAXIMUM, ( "uhci_rh_submit_urb(): current rh's ref_count=0x%x\n", pdev->ref_count ) ); + pdev->ref_count += 2; //one for timer and one for urb + + purb->status = STATUS_PENDING; + break; + } + case USB_ENDPOINT_XFER_BULK: + case USB_ENDPOINT_XFER_ISOC: + default: + { + purb->status = STATUS_INVALID_PARAMETER; + break; + } + } + unlock_dev( pdev, FALSE ); + KeReleaseSpinLock( &dev_mgr->timer_svc_list_lock, old_irql ); + return purb->status; +} + +//must have rh dev_lock acquired +BOOL +uhci_rh_reset_port( +PHCD hcd, +UCHAR port_idx +) +{ + LONG i; + PUHCI_DEV uhci; + ULONG status; + + if( port_idx != 1 && port_idx != 2 ) + return FALSE; + + if( hcd == NULL ) + return FALSE; + + if( port_idx == 1 ) + { + i = USBPORTSC1; + } + else + { + i = USBPORTSC2; + } + + uhci = uhci_from_hcd( hcd ); + //assert the reset signal,(implicitly disable the port) + SET_RH_PORTSTAT( i, USBPORTSC_PR ); + usb_wait_ms_dpc( 50 ); + //clear the reset signal, delay port enable till clearing port feature + CLR_RH_PORTSTAT( i, USBPORTSC_PR ); + usb_wait_us_dpc( 10 ); + SET_RH_PORTSTAT( i, USBPORTSC_PE ); + //recovery time 10ms + usb_wait_ms_dpc( 10 ); + SET_RH_PORTSTAT( i, 0x0a ); + + status = READ_PORT_USHORT( ( PUSHORT )( uhci->port_base + i ) ); + usb_dbg_print( DBGLVL_MAXIMUM, ( "uhci_rh_reset_port(): status after written=0x%x\n", status ) ); + + return TRUE; +} + +NTSTATUS +uhci_dispatch_irp( +IN PDEVICE_OBJECT DeviceObject, +IN PIRP irp +) +{ + PDEVICE_EXTENSION pdev_ext; + PUSB_DEV_MANAGER dev_mgr; + PUHCI_DEV uhci; + + pdev_ext = DeviceObject->DeviceExtension; + uhci = pdev_ext->uhci; + + dev_mgr = uhci->hcd_interf.hcd_get_dev_mgr( &uhci->hcd_interf ); + return dev_mgr_dispatch( dev_mgr, irp ); +} + +VOID +uhci_unload( +IN PDRIVER_OBJECT DriverObject +) +{ + PDEVICE_OBJECT pdev; + PDEVICE_EXTENSION pdev_ext; + PUSB_DEV_MANAGER dev_mgr; + LONG i; + + pdev = DriverObject->DeviceObject; + + if( pdev == NULL ) + return; + + pdev_ext = pdev->DeviceExtension; + if( pdev_ext == NULL ) + return; + + dev_mgr = &g_dev_mgr; + if( dev_mgr == NULL ) + return; + // + // set the termination flag + // + dev_mgr->term_flag = TRUE; + // + // wake up the thread if it is + // + KeSetEvent( &dev_mgr->wake_up_event, 0, FALSE ); + KeWaitForSingleObject( + dev_mgr->pthread, + Executive, + KernelMode, + TRUE, + NULL); + ObDereferenceObject( dev_mgr->pthread ); + dev_mgr->pthread = NULL; + // for( i = 0; i < dev_mgr->hcd_count; i++ ) + // dev_mgr->hcd_array[ i ]->hcd_release( dev_mgr->hcd_array[ i ]); + dev_mgr_release_hcd( dev_mgr ); + + return; +} + +//the following are for hcd interface methods +VOID +uhci_set_dev_mgr( +struct _HCD* hcd, +PUSB_DEV_MANAGER dev_mgr ) +{ + hcd->dev_mgr = dev_mgr; +} + +PUSB_DEV_MANAGER +uhci_get_dev_mgr( +struct _HCD* hcd +) +{ + return hcd->dev_mgr; +} + +ULONG +uhci_get_type( +struct _HCD* hcd +) +{ + return ( hcd->flags & HCD_TYPE_MASK ); +} + +VOID +uhci_set_id( +struct _HCD* hcd, +UCHAR id +) +{ + hcd->flags &= ~HCD_ID_MASK; + hcd->flags |= ( HCD_ID_MASK & id ); +} + +UCHAR +uhci_get_id( +struct _HCD* hcd +) +{ + return ( UCHAR )( hcd->flags & HCD_ID_MASK ); +} + + +UCHAR +uhci_alloc_addr( +struct _HCD* hcd +) +{ + LONG i; + if( hcd == NULL ) + return 0; + + for( i = 1; i < MAX_DEVS; i ++ ) + { + if( hcd->dev_addr_map[ i >> 3 ] & ( 1 << ( i & 7 ) ) ) + { + continue; + } + else + { + break; + } + } + + if( i >= MAX_DEVS ) + return 0xff; + + hcd->dev_addr_map[ i >> 3 ] |= ( 1 << ( i & 7 ) ); + hcd->conn_count++; + return ( BYTE )i; +} +VOID +uhci_free_addr( +struct _HCD* hcd, +UCHAR addr +) +{ + if( addr & 0x80 ) + return; + + if( hcd == NULL ) + return; + + hcd->dev_addr_map[ addr >> 3 ] &= ~( 1 << ( addr & 7 ) ); + return; + +} + +NTSTATUS +uhci_submit_urb2( +struct _HCD* hcd, +PUSB_DEV pdev, +PUSB_ENDPOINT pendp, +PURB purb ) +{ + return uhci_submit_urb( uhci_from_hcd( hcd ), pdev, pendp, purb ); +} + +PUSB_DEV +uhci_get_root_hub( +struct _HCD* hcd +) +{ + return uhci_from_hcd( hcd )->root_hub; +} +VOID +uhci_set_root_hub( +struct _HCD* hcd, +PUSB_DEV root_hub +) +{ + if( hcd == NULL || root_hub == NULL ) + return; + uhci_from_hcd( hcd )->root_hub = root_hub; + return; +} + +BOOL +uhci_remove_device2( +struct _HCD* hcd, +PUSB_DEV pdev +) +{ + if( hcd == NULL || pdev == NULL ) + return FALSE; + + return uhci_remove_device( uhci_from_hcd( hcd ), pdev ); +} + +BOOL uhci_hcd_release( +struct _HCD* hcd +) +{ + PUHCI_DEV uhci; + PDEVICE_EXTENSION pdev_ext; + + if( hcd == NULL ) + return FALSE; + + + uhci = uhci_from_hcd( hcd ); + pdev_ext = uhci->pdev_ext; + + return uhci_release( pdev_ext->pdev_obj ); +} + +NTSTATUS +uhci_cancel_urb2( +struct _HCD* hcd, +PUSB_DEV pdev, +PUSB_ENDPOINT pendp, +PURB purb +) +{ + PUHCI_DEV uhci; + if( hcd == NULL ) + return STATUS_INVALID_PARAMETER; + + uhci = uhci_from_hcd( hcd ); + return uhci_cancel_urb( uhci, pdev, pendp, purb ); +} + +BOOL +uhci_rh_get_dev_change( +PHCD hcd, +PBYTE buf +) +{ + PUHCI_DEV uhci; + ULONG status; + + if( hcd == NULL || buf == NULL ) + return FALSE; + + uhci = uhci_from_hcd( hcd ); + status = READ_PORT_USHORT( ( PUSHORT )( uhci->port_base + USBPORTSC1 ) ); + usb_dbg_print( DBGLVL_MAXIMUM, ( "uhci_rh_get_dev_change(): rh port1 status=0x%x\n", status ) ); + + if( ( status & USBPORTSC_PEC ) || ( status & USBPORTSC_CSC ) ) + { + buf[ 0 ] |= ( 1 << 1 ); + } + + status = READ_PORT_USHORT( ( PUSHORT )( uhci->port_base + USBPORTSC2 ) ); + usb_dbg_print( DBGLVL_MAXIMUM, ( "rh_timer_svc_int_completion(): rh port2 status=0x%x\n", status ) ); + + if( ( status & USBPORTSC_PEC ) || ( status & USBPORTSC_CSC ) ) + { + buf[ 0 ] |= ( 1 << 2 ); + } + return TRUE; +} + +NTSTATUS +uhci_dispatch( +PHCD hcd, +LONG disp_code, +PVOID param ) // locking depends on type of code +{ + ULONG status; + + if( hcd == NULL ) + return FALSE; + + switch( disp_code ) + { + case HCD_DISP_READ_PORT_COUNT: + { + if( param == NULL ) + return STATUS_INVALID_PARAMETER; + *( ( PUCHAR )param ) = 2; + return STATUS_SUCCESS; + } + case HCD_DISP_READ_RH_DEV_CHANGE: + { + if( uhci_rh_get_dev_change( hcd, param ) == FALSE ) + return STATUS_INVALID_PARAMETER; + return STATUS_SUCCESS; + } + } + return STATUS_NOT_IMPLEMENTED; + +} +VOID +uhci_init_hcd_interface( +PUHCI_DEV uhci +) +{ + uhci->hcd_interf.hcd_set_dev_mgr = uhci_set_dev_mgr; + uhci->hcd_interf.hcd_get_dev_mgr = uhci_get_dev_mgr; + uhci->hcd_interf.hcd_get_type = uhci_get_type; + uhci->hcd_interf.hcd_set_id = uhci_set_id; + uhci->hcd_interf.hcd_get_id = uhci_get_id; + uhci->hcd_interf.hcd_alloc_addr = uhci_alloc_addr; + uhci->hcd_interf.hcd_free_addr = uhci_free_addr; + uhci->hcd_interf.hcd_submit_urb = uhci_submit_urb2; + uhci->hcd_interf.hcd_generic_urb_completion = uhci_generic_urb_completion; + uhci->hcd_interf.hcd_get_root_hub = uhci_get_root_hub; + uhci->hcd_interf.hcd_set_root_hub = uhci_set_root_hub; + uhci->hcd_interf.hcd_remove_device = uhci_remove_device2; + uhci->hcd_interf.hcd_rh_reset_port = uhci_rh_reset_port; + uhci->hcd_interf.hcd_release = uhci_hcd_release; + uhci->hcd_interf.hcd_cancel_urb = uhci_cancel_urb2; + uhci->hcd_interf.hcd_start = uhci_start; + uhci->hcd_interf.hcd_dispatch = uhci_dispatch; + + uhci->hcd_interf.flags = HCD_TYPE_UHCI; //hcd types | hcd id +} + + +NTSTATUS +generic_dispatch_irp( +IN PDEVICE_OBJECT dev_obj, +IN PIRP irp +) +{ + PDEVEXT_HEADER dev_ext; + + dev_ext = ( PDEVEXT_HEADER )dev_obj->DeviceExtension; + + if( dev_ext && dev_ext->dispatch ) + return dev_ext->dispatch( dev_obj, irp ); + + irp->IoStatus.Information = 0; + + EXIT_DISPATCH( STATUS_UNSUCCESSFUL, irp ); +} + + +VOID +generic_start_io( +IN PDEVICE_OBJECT dev_obj, +IN PIRP irp +) +{ + PDEVEXT_HEADER dev_ext; + + KIRQL old_irql; + + IoAcquireCancelSpinLock( &old_irql ); + if (irp != dev_obj->CurrentIrp || irp->Cancel) + { + IoReleaseCancelSpinLock(old_irql); + return; + } + else + { + IoSetCancelRoutine(irp, NULL); + IoReleaseCancelSpinLock(old_irql); + } + + dev_ext = ( PDEVEXT_HEADER )dev_obj->DeviceExtension; + + if( dev_ext && dev_ext->start_io ) + { + dev_ext->start_io( dev_obj, irp ); + return; + } + + irp->IoStatus.Information = 0; + irp->IoStatus.Status = STATUS_UNSUCCESSFUL; + + IoStartNextPacket( dev_obj, FALSE ); + IoCompleteRequest( irp, IO_NO_INCREMENT ); +} + +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ) +{ + NTSTATUS ntStatus = STATUS_SUCCESS; + PDEVICE_OBJECT dev_obj = NULL; + BOOLEAN fRes; + +#if DBG + // should be done before any debug output is done. + // read our debug verbosity level from the registry + //NetacOD_GetRegistryDword( NetacOD_REGISTRY_PARAMETERS_PATH, //absolute registry path + // L"DebugLevel", // REG_DWORD ValueName + // &gDebugLevel ); // Value receiver + + // debug_level = DBGLVL_MAXIMUM; +#endif + + uhci_dbg_print_cond( DBGLVL_MINIMUM , DEBUG_UHCI, ("Entering DriverEntry(), RegistryPath=\n %ws\n", RegistryPath->Buffer )); + + // Remember our driver object, for when we create our child PDO + usb_driver_obj = DriverObject; + + // + // Create dispatch points for create, close, unload + DriverObject->MajorFunction[ IRP_MJ_CREATE ] = generic_dispatch_irp; + DriverObject->MajorFunction[ IRP_MJ_CLOSE ] = generic_dispatch_irp; + DriverObject->DriverUnload = uhci_unload; + + // User mode DeviceIoControl() calls will be routed here + DriverObject->MajorFunction[ IRP_MJ_DEVICE_CONTROL ] = generic_dispatch_irp; + DriverObject->MajorFunction[ IRP_MJ_INTERNAL_DEVICE_CONTROL ] = generic_dispatch_irp; + + // User mode ReadFile()/WriteFile() calls will be routed here + DriverObject->MajorFunction[ IRP_MJ_WRITE ] = generic_dispatch_irp; + DriverObject->MajorFunction[ IRP_MJ_READ ] = generic_dispatch_irp; + + DriverObject->MajorFunction[ IRP_MJ_SHUTDOWN ] = generic_dispatch_irp; + DriverObject->MajorFunction[ IRP_MJ_SCSI ] = generic_dispatch_irp; + DriverObject->MajorFunction[ IRP_MJ_FLUSH_BUFFERS ] = generic_dispatch_irp; + + DriverObject->DriverStartIo = generic_start_io; + // routines for handling system PNP and power management requests + //DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = generic_dispatch_irp; + + // The Functional Device Object (FDO) will not be created for PNP devices until + // this routine is called upon device plug-in. + RtlZeroMemory( &g_dev_mgr, sizeof( USB_DEV_MANAGER ) ); + g_dev_mgr.usb_driver_obj = DriverObject; + +#ifdef INCLUDE_EHCI + ehci_probe( DriverObject, RegistryPath, &g_dev_mgr ); +#endif + + uhci_probe( DriverObject, RegistryPath, &g_dev_mgr ); + + if( dev_mgr_strobe( &g_dev_mgr ) == FALSE ) + { + + dev_mgr_release_hcd( &g_dev_mgr ); + return STATUS_UNSUCCESSFUL; + } + + dev_mgr_start_hcd( &g_dev_mgr ); + + uhci_dbg_print_cond( DBGLVL_DEFAULT, DEBUG_UHCI, ("DriverEntry(): exiting... (%x)\n", ntStatus)); + return STATUS_SUCCESS; +} + +//note: the initialization will be in the following order +// uhci_probe +// dev_mgr_strobe +// uhci_start + +// to kill dev_mgr_thread: +// dev_mgr->term_flag = TRUE; +// KeSetEvent( &dev_mgr->wake_up_event ); +// this piece of code must run at passive-level diff --git a/reactos/drivers/usb/nt4compat/usbdriver/uhciver.h b/reactos/drivers/usb/nt4compat/usbdriver/uhciver.h new file mode 100644 index 00000000000..4ea9e812556 --- /dev/null +++ b/reactos/drivers/usb/nt4compat/usbdriver/uhciver.h @@ -0,0 +1,13 @@ +#ifndef __UHCIVER_H__ +#define __UHCIVER_H__ + +//#define _MULTI_UHCI +//#define _TIANSHENG_DRIVER + +#ifdef _MULTI_UHCI +#define UHCI_VER_STR "m564.a\0" +#else +#define UHCI_VER_STR "0564.d\0" +#endif + +#endif diff --git a/reactos/drivers/usb/nt4compat/usbdriver/umss.c b/reactos/drivers/usb/nt4compat/usbdriver/umss.c new file mode 100644 index 00000000000..76ee2799711 --- /dev/null +++ b/reactos/drivers/usb/nt4compat/usbdriver/umss.c @@ -0,0 +1,2443 @@ +/** + * umss.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 "uhciver.h" +#include "ntddk.h" +#include "stdio.h" +#include "debug.h" +#include "umss.h" +#include "usb.h" +#include "hub.h" +#include "storage\inc\srb.h" +#include "storage\inc\ntddscsi.h" +#include "storage\inc\scsi.h" + +#define UMSS_EXIT_DISPATCH( dev_OBJ, staTUS, iRp ) \ +{\ + iRp->IoStatus.Status = staTUS;\ + if( staTUS != STATUS_PENDING)\ + {\ + IoCompleteRequest( iRp, IO_NO_INCREMENT);\ + return staTUS;\ + }\ + IoMarkIrpPending( iRp );\ + IoStartPacket( dev_OBJ, iRp, NULL, NULL ); \ + return STATUS_PENDING;\ +} + +#define UMSS_COMPLETE_START_IO( dev_OBJ, staTUS, iRP ) \ +{\ + iRP->IoStatus.Status = staTUS;\ + if( staTUS != STATUS_PENDING )\ + {\ + IoStartNextPacket( dev_OBJ, FALSE );\ + IoCompleteRequest( iRP, IO_NO_INCREMENT );\ + }\ + return;\ +} + +extern VOID +gendrv_startio( +IN PDEVICE_OBJECT dev_obj, +IN PIRP irp +); + +NTSYSAPI +NTSTATUS +NTAPI +ZwLoadDriver( +IN PUNICODE_STRING DriverServiceName +); + +NTSYSAPI +NTSTATUS +NTAPI +ObOpenObjectByName( +IN POBJECT_ATTRIBUTES ObjectAttributes, +IN POBJECT_TYPE ObjectType OPTIONAL, +IN KPROCESSOR_MODE AccessMode, +IN OUT PACCESS_STATE AccessState OPTIONAL, +IN ACCESS_MASK DesiredAccess OPTIONAL, +IN OUT PVOID ParseContext OPTIONAL, +OUT PHANDLE Handle +); + +VOID +umss_start_io( +IN PDEVICE_OBJECT dev_obj, +IN PIRP irp +); + +NTSTATUS +umss_port_dispatch_routine( +PDEVICE_OBJECT pdev_obj, +PIRP irp +); + +BOOL +umss_connect( +PCONNECT_DATA dev_mgr, +DEV_HANDLE dev_handle +); + +BOOL +umss_disconnect( +PUSB_DEV_MANAGER dev_mgr, +DEV_HANDLE dev_handle +); + +BOOL +umss_stop( +PUSB_DEV_MANAGER dev_mgr, +DEV_HANDLE dev_handle +); + +NTSTATUS +umss_dispatch_routine( +PDEVICE_OBJECT pdev_obj, +PIRP irp +); + +VOID +umss_set_cfg_completion( +PURB purb, +PVOID pcontext +); + +VOID +umss_start_create_device( +IN PVOID Parameter +); + +BOOL +umss_delete_device( +PUSB_DEV_MANAGER dev_mgr, +PUSB_DRIVER pdrvr, +PDEVICE_OBJECT dev_obj, +BOOL is_if +); + +BOOL +umss_stop( +PUSB_DEV_MANAGER dev_mgr, +DEV_HANDLE dev_handle +); + +NTSTATUS +umss_process_srb( +PDEVICE_OBJECT dev_obj, +PIRP irp ); + +VOID +umss_load_class_driver( +PVOID context +); + +BOOL +umss_tsc_to_sff( +PIO_PACKET io_packet +); + +VOID +umss_fix_sff_result( +PIO_PACKET io_packet, +SCSI_REQUEST_BLOCK *srb +); + +PDEVICE_OBJECT +umss_create_port_device( +PUSB_DEV_MANAGER dev_mgr, +PUSB_DRIVER pdriver +) +{ + + // currently a port device is a connection point + // and upper driver use this to register itself + // with umss driver for future notification of + // pnp event. 2004-03-22 23:12:41 + UCHAR dev_name[ 64 ]; + STRING string; + NTSTATUS status; + PDEVICE_OBJECT pdev; + UNICODE_STRING name_string, symb_link; + PUMSS_PORT_DEV_EXT pdev_ext; + + sprintf( dev_name, "\\Device\\usbPort_%d", ( int )0 ); + + RtlInitString( &string, dev_name ); + RtlAnsiStringToUnicodeString( &name_string, &string, TRUE ); + pdev = NULL; + + status = IoCreateDevice( + dev_mgr->usb_driver_obj, + sizeof( UMSS_PORT_DEV_EXT ), + &name_string, + FILE_USB_DEV_TYPE, + 0, + TRUE, + &pdev); + + if( status == STATUS_SUCCESS ) + { + // + // We do buffered io + // + pdev->Flags |= DO_BUFFERED_IO; + + pdev->Flags &= ~DO_DEVICE_INITIALIZING; + pdev->StackSize = 2; //one for fdo, one for file device obj + + pdev_ext = ( PUMSS_PORT_DEV_EXT )pdev->DeviceExtension; + + pdev_ext->dev_ext_hdr.type = NTDEV_TYPE_CLIENT_DEV; + pdev_ext->dev_ext_hdr.dispatch = umss_port_dispatch_routine; + pdev_ext->dev_ext_hdr.start_io = NULL; + pdev_ext->dev_ext_hdr.dev_mgr = dev_mgr; + pdev_ext->pdriver = pdriver; + + sprintf( dev_name, "\\DosDevices\\usbPort%d", ( int )0 ); + + RtlInitString( &string, dev_name ); + RtlAnsiStringToUnicodeString( &symb_link, &string, TRUE ); + IoCreateSymbolicLink( &symb_link, &name_string ); + RtlFreeUnicodeString( &symb_link ); + + } + + RtlFreeUnicodeString( &name_string ); + return pdev; +} + +BOOL +umss_delete_port_device( +PDEVICE_OBJECT dev_obj +) +{ + UCHAR dev_name[ 64 ]; + STRING string; + UNICODE_STRING symb_link; + + if( dev_obj == NULL ) + return FALSE; + + // remove the symbolic link + sprintf( dev_name, "\\DosDevices\\usbPort%d", ( int )0 ); + RtlInitString( &string, dev_name ); + RtlAnsiStringToUnicodeString( &symb_link, &string, TRUE ); + IoDeleteSymbolicLink( &symb_link ); + RtlFreeUnicodeString( &symb_link ); + + if( dev_obj->ReferenceCount == 0 ) + { + IoDeleteDevice( dev_obj ); + usb_dbg_print( DBGLVL_MAXIMUM, ( "umss_delete_port_device(): port device object is removed\n" ) ); + } + return TRUE; +} + +NTSTATUS +umss_port_dispatch_routine( +PDEVICE_OBJECT pdev_obj, +PIRP irp +) +// FIXME!!! there can not be sent IOCTL_SUBMIT_URB_XXX while +// the IOCTL_SUBMIT_CDB_XXX are active. may confuse the device. +// not resolved yet. +// 2004-03-22 23:12:26 +{ + KIRQL irql; + ULONG ctrl_code; + NTSTATUS status; + PIO_STACK_LOCATION irp_stack; + PUMSS_PORT_DEV_EXT pdev_ext; + PUMSS_DRVR_EXTENSION pdrvr_ext; + + USE_IRQL; + + if( pdev_obj == NULL || irp == NULL ) + return STATUS_INVALID_PARAMETER; + + status = STATUS_SUCCESS; + irp_stack = IoGetCurrentIrpStackLocation( irp ); + ctrl_code = irp_stack->Parameters.DeviceIoControl.IoControlCode; + + pdev_ext = ( PUMSS_PORT_DEV_EXT )pdev_obj->DeviceExtension; + + switch( irp_stack->MajorFunction ) + { + case IRP_MJ_INTERNAL_DEVICE_CONTROL: + { + switch( ctrl_code ) + { + case IOCTL_REGISTER_DRIVER: + { + PCLASS_DRV_REG_INFO pcdri; + if( irp_stack->Parameters.DeviceIoControl.InputBufferLength < sizeof( CLASS_DRV_REG_INFO ) ) + { + EXIT_DISPATCH( STATUS_INVALID_PARAMETER, irp ); + } + + pcdri = irp->AssociatedIrp.SystemBuffer; + if( pcdri->fdo_driver == NULL || pcdri->add_device == NULL || pcdri->pnp_dispatch == NULL ) + { + EXIT_DISPATCH( STATUS_INVALID_PARAMETER, irp ); + } + pdrvr_ext = ( PUMSS_DRVR_EXTENSION )pdev_ext->pdriver->driver_ext; + pdrvr_ext->class_driver_info.fdo_driver = pcdri->fdo_driver; + pdrvr_ext->class_driver_info.add_device = pcdri->add_device; + pdrvr_ext->class_driver_info.pnp_dispatch = pcdri->pnp_dispatch; + EXIT_DISPATCH( STATUS_SUCCESS, irp ); + } + case IOCTL_REVOKE_DRIVER: + { + pdrvr_ext = ( PUMSS_DRVR_EXTENSION )pdev_ext->pdriver->driver_ext; + pdrvr_ext->class_driver_info.fdo_driver = NULL; + pdrvr_ext->class_driver_info.add_device = NULL; + pdrvr_ext->class_driver_info.pnp_dispatch = NULL; + EXIT_DISPATCH( STATUS_SUCCESS, irp ); + } + } + } + case IRP_MJ_CREATE: + case IRP_MJ_CLOSE: + { + EXIT_DISPATCH( STATUS_SUCCESS, irp ); + } + } + EXIT_DISPATCH( STATUS_INVALID_DEVICE_REQUEST, irp ); +} + +BOOL +umss_driver_init( +PUSB_DEV_MANAGER dev_mgr, +PUSB_DRIVER pdriver +) +{ + PUMSS_DRVR_EXTENSION pdrvr_ext; + UNICODE_STRING unicode_string; + NTSTATUS status; + + if( dev_mgr == NULL || pdriver == NULL ) + return FALSE; + + //init driver structure, no PNP table functions + pdriver->driver_desc.flags = USB_DRIVER_FLAG_DEV_CAPABLE; + pdriver->driver_desc.vendor_id = 0x0dd8; // USB Vendor ID + pdriver->driver_desc.product_id = 0x0003; // USB Product ID. + pdriver->driver_desc.release_num = 0x100; // Release Number of Device + + pdriver->driver_desc.config_val = 1; // Configuration Value + pdriver->driver_desc.if_num = 1; // Interface Number + pdriver->driver_desc.if_class = USB_CLASS_MASS_STORAGE; // Interface Class + pdriver->driver_desc.if_sub_class = 0; // Interface SubClass + pdriver->driver_desc.if_protocol = 0; // Interface Protocol + + pdriver->driver_desc.driver_name = "USB Mass Storage driver"; // Driver name for Name Registry + pdriver->driver_desc.dev_class = USB_CLASS_MASS_STORAGE; + pdriver->driver_desc.dev_sub_class = 0; // Device Subclass + pdriver->driver_desc.dev_protocol = 0; // Protocol Info. + + pdriver->driver_ext = usb_alloc_mem( NonPagedPool, sizeof( UMSS_DRVR_EXTENSION ) ); + pdriver->driver_ext_size = sizeof( UMSS_DRVR_EXTENSION ); + + RtlZeroMemory( pdriver->driver_ext, sizeof( UMSS_DRVR_EXTENSION ) ); + pdrvr_ext = ( PUMSS_DRVR_EXTENSION )pdriver->driver_ext; + pdrvr_ext->dev_count = 0; + InitializeListHead( &pdrvr_ext->dev_list ); + ExInitializeFastMutex( &pdrvr_ext->dev_list_mutex ); + + pdriver->disp_tbl.version = 1; + pdriver->disp_tbl.dev_connect = umss_connect; + pdriver->disp_tbl.dev_disconnect = umss_disconnect; + pdriver->disp_tbl.dev_stop = umss_stop; + pdriver->disp_tbl.dev_reserved = NULL; + + if( ( pdrvr_ext->port_dev_obj = umss_create_port_device( dev_mgr, pdriver ) ) == NULL ) + { + usb_free_mem( pdriver->driver_ext ); + pdriver->driver_ext = NULL; + pdriver->driver_ext_size = 0; + pdriver->disp_tbl.dev_connect = NULL; + pdriver->disp_tbl.dev_stop = NULL; + pdriver->disp_tbl.dev_disconnect = NULL; + return FALSE; + } + + umss_load_class_driver( NULL ); + + // umss_schedule_workitem( NULL, umss_load_class_driver, NULL, 0 ); + usb_dbg_print( DBGLVL_MAXIMUM, ( "umss_driver_init(): umss driver is initialized\n") ); + return TRUE; +} + +BOOL +umss_driver_destroy( +PUSB_DEV_MANAGER dev_mgr, +PUSB_DRIVER pdriver +) +{ + PUMSS_DRVR_EXTENSION pdrvr_ext; + if( dev_mgr == NULL || pdriver == NULL ) + return FALSE; + + pdrvr_ext = ( PUMSS_DRVR_EXTENSION ) pdriver->driver_ext; + umss_delete_port_device( pdrvr_ext->port_dev_obj ); + pdrvr_ext->port_dev_obj = NULL; + + ASSERT( IsListEmpty( &pdrvr_ext->dev_list ) == TRUE ); + usb_free_mem( pdriver->driver_ext ); + pdriver->driver_ext = NULL; + pdriver->driver_ext_size = 0; + usb_dbg_print( DBGLVL_MAXIMUM, ( "umss_driver_destroy(): umss driver is destroyed\n" ) ); + return TRUE; +} + +PDEVICE_OBJECT +umss_create_device( +PUSB_DEV_MANAGER dev_mgr, +PUSB_DRIVER umss_drvr, +DEV_HANDLE dev_handle, +BOOL is_if +) +{ + + UCHAR dev_name[ 64 ], dev_id; + STRING string; + NTSTATUS status; + PDEVICE_OBJECT pdev; + UNICODE_STRING name_string, symb_link; + PUMSS_DRVR_EXTENSION pdrvr_ext; + PUMSS_DEVICE_EXTENSION pdev_ext; + + usb_dbg_print( DBGLVL_MAXIMUM, ( "umss_create_device(): entering...\n" ) ); + pdrvr_ext = ( PUMSS_DRVR_EXTENSION )umss_drvr->driver_ext; + dev_id = ( UCHAR )dev_id_from_handle( dev_handle ); // pdrvr_ext->dev_count; + + if( is_if == FALSE ) + sprintf( dev_name, "\\Device\\umssdev_%d", ( int )dev_id ); + else + sprintf( dev_name, "\\Device\\umssifdev_%d", ( int )dev_id ); + + RtlInitString( &string, dev_name ); + RtlAnsiStringToUnicodeString( &name_string, &string, TRUE ); + pdev = NULL; + + status = IoCreateDevice( + dev_mgr->usb_driver_obj, + sizeof( UMSS_DEVICE_EXTENSION ), + &name_string, + FILE_USB_DEV_TYPE, + 0, + TRUE, + &pdev); + + if( status == STATUS_SUCCESS ) + { + // + // We do direct io + // + pdev->Flags |= DO_DIRECT_IO; + + pdev->Flags &= ~DO_DEVICE_INITIALIZING; + pdev->StackSize = 2; //one for fdo, one for file device obj + + pdev_ext = ( PUMSS_DEVICE_EXTENSION )pdev->DeviceExtension; + + //may be accessed by other thread + ExAcquireFastMutex( &pdrvr_ext->dev_list_mutex ); + InsertTailList( &pdrvr_ext->dev_list, &pdev_ext->dev_obj_link ); + pdrvr_ext->dev_count++; + ExReleaseFastMutex( &pdrvr_ext->dev_list_mutex ); + + if( is_if ) + pdev_ext->flags |= UMSS_DEV_FLAG_IF_DEV; + + pdev_ext->umss_dev_id = dev_id; + pdev_ext->pdo = pdev; + pdev_ext->dev_handle = dev_handle; + pdev_ext->dev_mgr = dev_mgr; + pdev_ext->pdriver = umss_drvr; + + pdev_ext->dev_ext_hdr.type = NTDEV_TYPE_CLIENT_DEV; + pdev_ext->dev_ext_hdr.dispatch = umss_dispatch_routine; + pdev_ext->dev_ext_hdr.start_io = umss_start_io; + pdev_ext->dev_ext_hdr.dev_mgr = dev_mgr; + + if( is_if == FALSE ) + sprintf( dev_name, "\\DosDevices\\umssdev%d", ( int )dev_id ); + else + sprintf( dev_name, "\\DosDevices\\umssifdev%d", ( int )dev_id ); + + RtlInitString( &string, dev_name ); + RtlAnsiStringToUnicodeString( &symb_link, &string, TRUE ); + IoCreateSymbolicLink( &symb_link, &name_string ); + RtlFreeUnicodeString( &symb_link ); + KeInitializeEvent( &pdev_ext->sync_event, SynchronizationEvent, FALSE ); + KeInitializeSpinLock( &pdev_ext->dev_lock ); + + } + RtlFreeUnicodeString( &name_string ); + return pdev; +} + +BOOL +umss_connect( +PCONNECT_DATA param, +DEV_HANDLE dev_handle +) +{ + PURB purb; + NTSTATUS status; + PUSB_CTRL_SETUP_PACKET psetup; + PUSB_DEV_MANAGER dev_mgr; + PUSB_DRIVER pdrvr; + + usb_dbg_print( DBGLVL_MAXIMUM, ( "umss_connect(): entering...\n" ) ); + + dev_mgr = param->dev_mgr; + pdrvr = param->pdriver; + + //directly set the configuration + purb = usb_alloc_mem( NonPagedPool, sizeof( URB ) ); + if( purb == NULL ) + return FALSE; + + psetup = ( PUSB_CTRL_SETUP_PACKET )( purb )->setup_packet; + urb_init( ( purb ) ); + + purb->endp_handle = dev_handle | 0xffff; + purb->data_buffer = NULL; + purb->data_length = 0; + purb->completion = umss_set_cfg_completion; + purb->context = dev_mgr; + purb->reference = ( LONG )pdrvr; + psetup->bmRequestType = 0; + psetup->bRequest = USB_REQ_SET_CONFIGURATION; + psetup->wValue = 1; + psetup->wIndex = 0; + psetup->wLength = 0; + + status = usb_submit_urb( dev_mgr, purb ); + if( status != STATUS_PENDING ) + { + usb_free_mem( purb ); + return FALSE; + } + return TRUE; +} + +VOID +umss_set_cfg_completion( +PURB purb, +PVOID pcontext +) +{ + PUSB_CTRL_SETUP_PACKET psetup; + PUCHAR buf; + PWORK_QUEUE_ITEM pwork_item; + PUMSS_CREATE_DATA pcd; + DEV_HANDLE dev_handle; + NTSTATUS status; + PUSB_DEV_MANAGER dev_mgr; + PUSB_DRIVER pdrvr; + + if( purb == NULL || pcontext == NULL ) + return; + + dev_mgr = ( PUSB_DEV_MANAGER ) pcontext; + pdrvr = ( PUSB_DRIVER )purb->reference; + dev_handle = purb->endp_handle & ~0xffff; + + + if( purb->status != STATUS_SUCCESS ) + { + usb_free_mem( purb ); + return; + } + + buf = usb_alloc_mem( NonPagedPool, 512 ); + if( buf == NULL ) + { + usb_free_mem( purb ); + return; + } + + //now let's get the descs, one configuration, one interface and two endpoint + psetup = ( PUSB_CTRL_SETUP_PACKET )( purb )->setup_packet; + purb->data_buffer = buf; + purb->data_length = 512; + purb->completion = NULL; //this is an immediate request, no needs completion + purb->context = dev_mgr; + purb->reference = 0; + psetup->bmRequestType = 0x80; + psetup->bRequest = USB_REQ_GET_DESCRIPTOR; + psetup->wValue = USB_DT_CONFIG << 8; + psetup->wIndex = 0; + psetup->wLength = 512; + + status = usb_submit_urb( dev_mgr, purb ); + if( status == STATUS_PENDING ) + { + TRAP(); + } + usb_free_mem( purb ); + purb = NULL; + + if( status != STATUS_SUCCESS ) + { + usb_free_mem( buf ); + buf = NULL; + return; + } + + pcd = usb_alloc_mem( NonPagedPool, sizeof( WORK_QUEUE_ITEM ) + sizeof( UMSS_CREATE_DATA ) ); + if( pcd == NULL ) + { + usb_free_mem( buf ); + buf = NULL; + return; + } + + pcd->desc_buf = buf; + pcd->dev_handle = dev_handle; + pcd->dev_mgr = dev_mgr; + pcd->pdriver = pdrvr; + pwork_item = ( PWORK_QUEUE_ITEM )( &pcd[ 1 ] ); + + ExInitializeWorkItem( pwork_item, umss_start_create_device, ( PVOID )pcd ); + ExQueueWorkItem( pwork_item, DelayedWorkQueue ); +} + +VOID +umss_start_create_device( +IN PVOID Parameter +) +{ + LONG i; + PUCHAR desc_buf; + NTSTATUS status; + PUSB_DEV pdev; + DEV_HANDLE dev_handle; + PUSB_DRIVER pdrvr; + PDEVICE_OBJECT pdev_obj; + PUSB_DEV_MANAGER dev_mgr; + PUMSS_CREATE_DATA pcd; + PUSB_INTERFACE_DESC pif_desc; + PUSB_ENDPOINT_DESC pendp_desc; + PUMSS_DEVICE_EXTENSION pdev_ext; + PUSB_CONFIGURATION_DESC pconfig_desc; + + USE_IRQL; + + if( Parameter == NULL ) + return; + + pcd = ( PUMSS_CREATE_DATA )Parameter; + desc_buf = pcd->desc_buf; + dev_mgr = pcd->dev_mgr; + dev_handle = pcd->dev_handle; + pdrvr = pcd->pdriver; + usb_free_mem( pcd ); + pcd = NULL; + + status = usb_query_and_lock_dev( dev_mgr, dev_handle, &pdev ); + if( status != STATUS_SUCCESS ) + { + usb_free_mem( desc_buf ); + return; + } + + pdev_obj = umss_create_device( dev_mgr, pdrvr, dev_handle, FALSE ); + + lock_dev( pdev, FALSE ); + if( pdev_obj == NULL || \ + dev_state( pdev ) == USB_DEV_STATE_ZOMB || \ + dev_mgr_set_driver( dev_mgr, dev_handle, pdrvr, pdev ) == FALSE ) + { + usb_free_mem( desc_buf ); + unlock_dev( pdev, FALSE ); + + if( pdev_obj ) + umss_delete_device( dev_mgr, pdrvr, pdev_obj, FALSE ); + + usb_unlock_dev( pdev ); + return; + } + unlock_dev( pdev, FALSE ); + + pdev_ext = ( PUMSS_DEVICE_EXTENSION )pdev_obj->DeviceExtension; + + pdev_ext->desc_buf = desc_buf; + pdev_ext->pif_desc = NULL; + pdev_ext->pin_endp_desc = pdev_ext->pout_endp_desc = NULL; + + pconfig_desc = ( PUSB_CONFIGURATION_DESC )desc_buf; + pif_desc = ( PUSB_INTERFACE_DESC )( &pconfig_desc[ 1 ] ); + //search for our if + for( i = 0; ( ( UCHAR ) i ) < pconfig_desc->bNumInterfaces; i++ ) + { + if( pif_desc->bLength == sizeof( USB_INTERFACE_DESC ) \ + && pif_desc->bDescriptorType == USB_DT_INTERFACE ) + { + if( pif_desc->bInterfaceClass == USB_CLASS_MASS_STORAGE ) + { + pdev_ext->pif_desc = pif_desc; + pdev_ext->if_idx = ( UCHAR )i; + break; + } + else + { + if( usb_skip_if_and_altif( ( PUCHAR* )&pif_desc ) == FALSE ) + break; + } + } + else + { + break; + } + } + + if( pdev_ext->pif_desc ) + { + pendp_desc = ( PUSB_ENDPOINT_DESC )&pif_desc[ 1 ]; + for( i = 0; ( ( UCHAR )i ) < pif_desc->bNumEndpoints; i++ ) + { + if( pendp_desc->bDescriptorType == USB_DT_ENDPOINT \ + && pendp_desc->bLength == sizeof( USB_ENDPOINT_DESC ) ) + { + if( ( pendp_desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK ) \ + == USB_ENDPOINT_XFER_INT ) + { + pdev_ext->pint_endp_desc = pendp_desc; + pdev_ext->int_endp_idx = ( UCHAR )i; + } + else if( ( pendp_desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK ) \ + == USB_ENDPOINT_XFER_BULK ) + { + if( pendp_desc->bEndpointAddress & USB_DIR_IN ) + { + pdev_ext->pin_endp_desc = pendp_desc; + pdev_ext->in_endp_idx = ( UCHAR )i; + } + else + { + pdev_ext->pout_endp_desc = pendp_desc; + pdev_ext->out_endp_idx = ( UCHAR )i; + } + } + pendp_desc = &pendp_desc[ 1 ]; + } + else + break; + } + } + usb_unlock_dev( pdev ); + return; +} + +BOOL +umss_stop( +PUSB_DEV_MANAGER dev_mgr, +DEV_HANDLE dev_handle +) +{ + return TRUE; +} + +BOOL +umss_disconnect( +PUSB_DEV_MANAGER dev_mgr, +DEV_HANDLE dev_handle +) +{ + PDEVICE_OBJECT dev_obj; + NTSTATUS status; + PUSB_DEV pdev; + PUSB_DRIVER pdrvr; + + if( dev_mgr == NULL || dev_handle == 0 ) + return FALSE; + + pdev = NULL; + //special use of the lock dev, simply use this routine to get the dev + status = usb_query_and_lock_dev( dev_mgr, dev_handle, &pdev ); + if( pdev == NULL ) + { + return FALSE; + } + if( status == STATUS_SUCCESS ) + { + // must be a bug + TRAP(); + usb_unlock_dev( pdev ); + } + pdrvr = pdev->dev_driver; + dev_obj = pdev->dev_obj; + pdev = NULL; + + return umss_delete_device( dev_mgr, pdrvr, dev_obj, FALSE ); +} + +VOID +umss_deferred_delete_device( +PVOID context +) +{ + PDEVICE_OBJECT dev_obj; + PUMSS_DEVICE_EXTENSION pdev_ext; + PUMSS_DRVR_EXTENSION pdrvr_ext; + LARGE_INTEGER interval; + + if( context == NULL ) + return; + + dev_obj = ( PDEVICE_OBJECT )context; + pdev_ext = dev_obj->DeviceExtension; + pdrvr_ext = ( PUMSS_DRVR_EXTENSION )pdev_ext->pdriver->driver_ext; + + interval.QuadPart = -20000; //two ms + + for( ; ; ) + { + if( dev_obj->ReferenceCount ) + KeDelayExecutionThread( KernelMode, TRUE, &interval ); + else + { + KeDelayExecutionThread( KernelMode, TRUE, &interval ); + if( dev_obj->ReferenceCount == 0 ) + break; + } + } + usb_dbg_print( DBGLVL_MAXIMUM, ( "umss_deferred_delete_device(): delete device, 0x%x\n", dev_obj ) ); + + ExAcquireFastMutex( &pdrvr_ext->dev_list_mutex ); + RemoveEntryList( &pdev_ext->dev_obj_link ); + pdrvr_ext->dev_count--; + ExReleaseFastMutex( &pdrvr_ext->dev_list_mutex ); + + IoDeleteDevice( dev_obj ); + return; +} + +BOOL +umss_delete_device( +PUSB_DEV_MANAGER dev_mgr, +PUSB_DRIVER pdrvr, +PDEVICE_OBJECT dev_obj, +BOOL is_if +) +{ + UCHAR dev_name[ 64 ]; + STRING string; + UNICODE_STRING symb_link; + + NTSTATUS status; + PUMSS_DEVICE_EXTENSION pdev_ext; + PUMSS_DRVR_EXTENSION pdrvr_ext; + + if( dev_obj == NULL ) + return FALSE; + + if( pdrvr == NULL || dev_mgr == NULL ) + return FALSE; + + pdev_ext = ( PUMSS_DEVICE_EXTENSION )dev_obj->DeviceExtension; + pdrvr_ext = ( PUMSS_DRVR_EXTENSION )pdrvr->driver_ext; + if( is_if == FALSE ) + sprintf( dev_name, "\\DosDevices\\umssdev%d", ( int )pdev_ext->umss_dev_id ); + else + sprintf( dev_name, "\\DosDevices\\umssifdev%d", ( int )pdev_ext->umss_dev_id ); + + RtlInitString( &string, dev_name ); + RtlAnsiStringToUnicodeString( &symb_link, &string, TRUE ); + IoDeleteSymbolicLink( &symb_link ); + RtlFreeUnicodeString( &symb_link ); + + if( pdev_ext->desc_buf ) + { + usb_dbg_print( DBGLVL_MAXIMUM, ( "umss_delete_device(): delete desc_buf\n" ) ); + usb_free_mem( pdev_ext->desc_buf ); + pdev_ext->desc_buf = NULL; + + } + + if( dev_obj->ReferenceCount == 0 ) + { + ExAcquireFastMutex( &pdrvr_ext->dev_list_mutex ); + RemoveEntryList( &pdev_ext->dev_obj_link ); + pdrvr_ext->dev_count--; + ExReleaseFastMutex( &pdrvr_ext->dev_list_mutex ); + + IoDeleteDevice( dev_obj ); + return TRUE; + } + + // + // FIXME: how if the driver unloading happens + // since this is called in dev_mgr_disconnect_dev, umss_schedule_workitem + // can not protect the USB_DEV object from be deleted. so the workitem + // can not access anything relative to the USB_DEV object. In this case + // we will tollerate the usb_query_and_lock_dev failure since it will + // never success when come to this point, and we won't pass dev_mgr + // and pdev to the function. But other scenarios, usb_query_and_lock_dev + // can not fail if dev_mgr and pdev are passed valid. + // When driver is unloading, don't know. Wish NT will unload the driver + // only when all the devices to the driver are deleted. + // + umss_schedule_workitem( dev_obj, umss_deferred_delete_device, NULL, 0 ); + return TRUE; +} + +VOID +umss_submit_io_packet( +PDEVICE_OBJECT dev_obj, +PIO_PACKET io_packet +) +{ + NTSTATUS status; + PUMSS_DEVICE_EXTENSION pdev_ext; + PUSB_DEV pdev; + + pdev_ext = dev_obj->DeviceExtension; + + // lock the dev, the pdev_ext->pif_desc won't go away. + if( ( status = usb_query_and_lock_dev( pdev_ext->dev_mgr, pdev_ext->dev_handle, &pdev ) ) + != STATUS_SUCCESS ) + { + usb_dbg_print( DBGLVL_MAXIMUM, ( "umss_start_io(): error, device is not valid\n" ) ); + UMSS_COMPLETE_START_IO( dev_obj, status, io_packet->pirp ); + return; + } + + if( pdev_ext->pif_desc->bInterfaceProtocol == PROTOCOL_BULKONLY ) + { + status = umss_bulkonly_startio( pdev_ext, io_packet ); + } + else if( pdev_ext->pif_desc->bInterfaceProtocol == PROTOCOL_CB + || pdev_ext->pif_desc->bInterfaceProtocol == PROTOCOL_CBI ) + { + status = umss_cbi_startio( pdev_ext, io_packet ); + } + else + { + status = STATUS_DEVICE_PROTOCOL_ERROR; + } + usb_unlock_dev( pdev ); + UMSS_COMPLETE_START_IO( dev_obj, status, io_packet->pirp ); + return; +} + +VOID +umss_start_io( +IN PDEVICE_OBJECT dev_obj, +IN PIRP irp +) +{ + KIRQL irql; + ULONG ctrl_code; + NTSTATUS status; + PIO_STACK_LOCATION irp_stack; + PUMSS_DEVICE_EXTENSION pdev_ext; + IO_PACKET io_packet; + PUSER_IO_PACKET user_io_packet; + + USE_IRQL; + + if( dev_obj == NULL || irp == NULL ) + return; + + status = STATUS_SUCCESS; + + irp_stack = IoGetCurrentIrpStackLocation( irp ); + ctrl_code = irp_stack->Parameters.DeviceIoControl.IoControlCode; + pdev_ext = ( PUMSS_DEVICE_EXTENSION )dev_obj->DeviceExtension; + + if( irp_stack->MajorFunction == IRP_MJ_SCSI ) + { + umss_process_srb( dev_obj, irp ); + return; + } + + if( irp_stack->MajorFunction != IRP_MJ_DEVICE_CONTROL ) + { + UMSS_COMPLETE_START_IO( dev_obj, STATUS_INVALID_DEVICE_REQUEST, irp ); + } + + switch( ctrl_code ) + { + case IOCTL_UMSS_SUBMIT_CDB_IN: + case IOCTL_UMSS_SUBMIT_CDB_OUT: + case IOCTL_UMSS_SUBMIT_CDB: + { + PUSB_DEV pdev; + + if( irp_stack->Parameters.DeviceIoControl.InputBufferLength < sizeof( USER_IO_PACKET ) ) + { + UMSS_COMPLETE_START_IO( dev_obj, STATUS_INVALID_PARAMETER, irp ); + } + + user_io_packet = ( PUSER_IO_PACKET )irp->AssociatedIrp.SystemBuffer; + + if( user_io_packet->sub_class != pdev_ext->pif_desc->bInterfaceSubClass ) + { + // not agree with the dev's subclass + UMSS_COMPLETE_START_IO( dev_obj, STATUS_DEVICE_PROTOCOL_ERROR, irp ); + } + + RtlZeroMemory( &io_packet, sizeof( io_packet ) ); + io_packet.cdb_length = user_io_packet->cdb_length; + io_packet.lun = user_io_packet->lun; + + RtlCopyMemory( io_packet.cdb, user_io_packet->cdb, MAX_CDB_LENGTH ); + + if( ctrl_code == IOCTL_UMSS_SUBMIT_CDB_IN ) + io_packet.flags |= USB_DIR_IN; + + if( ctrl_code != IOCTL_UMSS_SUBMIT_CDB ) + { + if( irp_stack->Parameters.DeviceIoControl.OutputBufferLength == 0 ) + UMSS_COMPLETE_START_IO( dev_obj, STATUS_BUFFER_TOO_SMALL, irp ); + + io_packet.data_buffer = MmGetSystemAddressForMdl( irp->MdlAddress ); + io_packet.data_length = irp_stack->Parameters.DeviceIoControl.OutputBufferLength; + + if( io_packet.data_length > MAX_BULK_TRANSFER_LENGTH ) + UMSS_COMPLETE_START_IO( dev_obj, STATUS_BUFFER_TOO_SMALL, irp ); + + //synchronize the buffer + if( io_packet.flags & USB_DIR_IN ) + KeFlushIoBuffers( irp->MdlAddress, TRUE, TRUE ); + else + KeFlushIoBuffers( irp->MdlAddress, FALSE, TRUE ); + } + + io_packet.pirp = irp; + umss_submit_io_packet( dev_obj, &io_packet ); + return; + } + case IOCTL_SCSI_PASS_THROUGH: + { + PSCSI_PASS_THROUGH pass_through; + IO_PACKET io_packet; + + pass_through = irp->AssociatedIrp.SystemBuffer; + + if( pass_through->DataTransferLength && + pass_through->DataBufferOffset != sizeof( SCSI_PASS_THROUGH ) ) + UMSS_COMPLETE_START_IO( dev_obj, STATUS_INVALID_PARAMETER, irp ); + + if( pass_through->SenseInfoLength && + ( pass_through->SenseInfoOffset != + pass_through->DataBufferOffset + pass_through->DataTransferLength ) ) + UMSS_COMPLETE_START_IO( dev_obj, STATUS_INVALID_PARAMETER, irp ); + + if( irp_stack->Parameters.DeviceIoControl.InputBufferLength < + ( sizeof( SCSI_PASS_THROUGH ) + + pass_through->SenseInfoLength + + pass_through->DataTransferLength ) ) + UMSS_COMPLETE_START_IO( dev_obj, STATUS_BUFFER_TOO_SMALL, irp ); + + RtlZeroMemory( &io_packet, sizeof( io_packet ) ); + + io_packet.flags |= IOP_FLAG_SCSI_CTRL_TRANSFER; + if( pass_through->DataIn ) + io_packet.flags |= IOP_FLAG_DIR_IN; + + io_packet.data_buffer = ( PVOID )&pass_through[ 1 ]; + io_packet.data_length = pass_through->DataTransferLength; + + if( pass_through->SenseInfoLength ) + { + io_packet.sense_data = ( ( PUCHAR )pass_through ) + pass_through->SenseInfoOffset; + io_packet.sense_data_length = pass_through->SenseInfoLength; + io_packet.flags |= IOP_FLAG_REQ_SENSE; + } + + io_packet.cdb_length = pass_through->CdbLength; + RtlCopyMemory( io_packet.cdb, pass_through->Cdb, sizeof( io_packet.cdb ) ); + io_packet.lun = 0; + io_packet.pirp = irp; + umss_submit_io_packet( dev_obj, &io_packet ); + return; + } + case IOCTL_SCSI_PASS_THROUGH_DIRECT: + { + PSCSI_PASS_THROUGH_DIRECT pass_through_direct; + IO_PACKET io_packet; + + pass_through_direct = irp->AssociatedIrp.SystemBuffer; + + if( pass_through_direct->SenseInfoLength && + pass_through_direct->SenseInfoOffset != sizeof( SCSI_PASS_THROUGH_DIRECT ) ) + UMSS_COMPLETE_START_IO( dev_obj, STATUS_INVALID_PARAMETER, irp ); + + if( irp_stack->Parameters.DeviceIoControl.InputBufferLength < + sizeof( SCSI_PASS_THROUGH_DIRECT ) + pass_through_direct->SenseInfoLength ) + UMSS_COMPLETE_START_IO( dev_obj, STATUS_BUFFER_TOO_SMALL, irp ); + + RtlZeroMemory( &io_packet, sizeof( io_packet ) ); + + io_packet.flags |= IOP_FLAG_SCSI_CTRL_TRANSFER; + if( pass_through_direct->DataIn ) + io_packet.flags |= IOP_FLAG_DIR_IN; + + io_packet.data_buffer = pass_through_direct->DataBuffer; + io_packet.data_length = pass_through_direct->DataTransferLength; + + if( pass_through_direct->SenseInfoLength ) + { + io_packet.sense_data = ( ( PUCHAR )pass_through_direct ) + pass_through_direct->SenseInfoOffset; + io_packet.sense_data_length = pass_through_direct->SenseInfoLength; + io_packet.flags |= IOP_FLAG_REQ_SENSE; + } + + io_packet.cdb_length = pass_through_direct->CdbLength; + RtlCopyMemory( io_packet.cdb, pass_through_direct->Cdb, sizeof( io_packet.cdb ) ); + io_packet.lun = 0; + io_packet.pirp = irp; + umss_submit_io_packet( dev_obj, &io_packet ); + return; + } + case IOCTL_SUBMIT_URB_RD: + case IOCTL_SUBMIT_URB_NOIO: + case IOCTL_SUBMIT_URB_WR: + { + gendrv_startio( dev_obj, irp ); + return; + } + default: + UMSS_COMPLETE_START_IO( dev_obj, STATUS_INVALID_DEVICE_REQUEST, irp ); + } + return; +} + +NTSTATUS +umss_dispatch_routine( +PDEVICE_OBJECT pdev_obj, +PIRP irp +) +// bugbug!!! there can not be sent IOCTL_SUBMIT_URB_XXX while +// the IOCTL_SUBMIT_CDB_XXX are active. may confuse the device. +// not resolved yet. +{ + KIRQL irql; + ULONG ctrl_code; + NTSTATUS status; + PIO_STACK_LOCATION irp_stack; + PUMSS_DEVICE_EXTENSION pdev_ext; + + USE_IRQL; + + if( pdev_obj == NULL || irp == NULL ) + return STATUS_INVALID_PARAMETER; + + status = STATUS_SUCCESS; + irp_stack = IoGetCurrentIrpStackLocation (irp); + ctrl_code = irp_stack->Parameters.DeviceIoControl.IoControlCode; + + pdev_ext = ( PUMSS_DEVICE_EXTENSION )pdev_obj->DeviceExtension; + + switch( irp_stack->MajorFunction ) + { + case IRP_MJ_CREATE: + case IRP_MJ_CLOSE: + { + return dev_mgr_dispatch( pdev_ext->dev_mgr, irp ); + } + case IRP_MJ_INTERNAL_DEVICE_CONTROL: + { + // function code to receive scsi request + UMSS_EXIT_DISPATCH( pdev_obj, STATUS_PENDING, irp ); + } + case IRP_MJ_DEVICE_CONTROL: + { + switch( ctrl_code ) + { + case IOCTL_UMSS_SET_FDO: + { + PDEVICE_OBJECT fdo; + PUSB_DEV pdev; + USE_IRQL; + + if( irp_stack->Parameters.DeviceIoControl.InputBufferLength < sizeof( PDEVICE_OBJECT ) ) { + EXIT_DISPATCH( STATUS_INVALID_PARAMETER, irp ); + } + + fdo = ( PDEVICE_OBJECT )( ( PULONG )irp->AssociatedIrp.SystemBuffer )[ 0 ]; + if( fdo == NULL ) + EXIT_DISPATCH( STATUS_INVALID_PARAMETER, irp ); + // + // we have to test the usb dev's state to determine whether set or not the fdo + // + + if( usb_query_and_lock_dev( pdev_ext->dev_mgr, pdev_ext->dev_handle, &pdev ) != STATUS_SUCCESS ) + EXIT_DISPATCH( STATUS_DEVICE_DOES_NOT_EXIST, irp ); + + lock_dev( pdev, FALSE ); + + if( dev_state( pdev ) >= USB_DEV_STATE_BEFORE_ZOMB || + dev_state( pdev ) == USB_DEV_STATE_ZOMB ) + { + unlock_dev( pdev, FALSE ); + usb_unlock_dev( pdev ); + EXIT_DISPATCH( STATUS_DEVICE_DOES_NOT_EXIST, irp ); + } + + pdev_ext->fdo = fdo; + unlock_dev( pdev, FALSE ); + usb_unlock_dev( pdev ); + irp->IoStatus.Information = 0; + EXIT_DISPATCH( STATUS_SUCCESS, irp ); + } + + case IOCTL_GET_DEV_DESC: + { + PGET_DEV_DESC_REQ pgddr; + if( irp_stack->Parameters.DeviceIoControl.InputBufferLength < sizeof( GET_DEV_DESC_REQ ) ) + { + EXIT_DISPATCH( STATUS_INVALID_PARAMETER, irp ); + } + pgddr = irp->AssociatedIrp.SystemBuffer; + if( pgddr->dev_handle != ( pdev_ext->dev_handle & 0xffff0000 ) ) + { + EXIT_DISPATCH( STATUS_INVALID_PARAMETER, irp ); + } + // an immediate request + return dev_mgr_dispatch( pdev_ext->dev_mgr, irp ); + } + case IOCTL_SUBMIT_URB_RD: + case IOCTL_SUBMIT_URB_NOIO: + case IOCTL_SUBMIT_URB_WR: + { + PURB purb; + DEV_HANDLE endp_handle; + + 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( !default_endp_handle( endp_handle ) ) + { + //no permit to other interface if interface dev + if( ( pdev_ext->flags & UMSS_DEV_FLAG_IF_DEV ) + && if_idx_from_handle( endp_handle ) != pdev_ext->if_idx ) + EXIT_DISPATCH( STATUS_INVALID_PARAMETER, irp ); + } + // FIXME: this is dangeous + // return dev_mgr_dispatch( pdev_ext->dev_mgr, irp ); + UMSS_EXIT_DISPATCH( pdev_obj, STATUS_PENDING, irp ); + } + case IOCTL_GET_DEV_HANDLE: + { + PUCHAR user_buffer; + ULONG user_buffer_length; + if( irp_stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof( LONG ) ) + EXIT_DISPATCH( STATUS_INVALID_PARAMETER, irp ); + + *( ( PLONG )irp->AssociatedIrp.SystemBuffer ) = pdev_ext->dev_handle; + irp->IoStatus.Information = sizeof( LONG ); + EXIT_DISPATCH( STATUS_SUCCESS, irp ); + } + + // + // request from scsi class driver + // + case IOCTL_SCSI_PASS_THROUGH: + case IOCTL_SCSI_PASS_THROUGH_DIRECT: + // + // direct cdb request + // + case IOCTL_UMSS_SUBMIT_CDB: + case IOCTL_UMSS_SUBMIT_CDB_OUT: + case IOCTL_UMSS_SUBMIT_CDB_IN: + { + UMSS_EXIT_DISPATCH( pdev_obj, STATUS_PENDING, irp ); + } + case IOCTL_SCSI_GET_INQUIRY_DATA: + { + PSCSI_ADAPTER_BUS_INFO adapter_info; + PSCSI_BUS_DATA bus_data; + PSCSI_INQUIRY_DATA inq_dat; + PINQUIRYDATA inq; + IO_PACKET io_packet; + ULONG required_size; + + required_size = sizeof( SCSI_ADAPTER_BUS_INFO ) + + sizeof( SCSI_BUS_DATA ) + + sizeof( SCSI_INQUIRY_DATA ) + + INQUIRYDATABUFFERSIZE; + + if( irp_stack->Parameters.DeviceIoControl.OutputBufferLength < required_size ) + UMSS_EXIT_DISPATCH( pdev_obj, STATUS_BUFFER_TOO_SMALL, irp ); + + RtlZeroMemory( &io_packet, sizeof( io_packet ) ); + + adapter_info = irp->AssociatedIrp.SystemBuffer; + adapter_info->NumberOfBuses = 1; + bus_data = &adapter_info->BusData[ 0 ]; + bus_data->NumberOfLogicalUnits = 1; + bus_data->InitiatorBusId = 0; + bus_data->InquiryDataOffset = sizeof( SCSI_ADAPTER_BUS_INFO ); + inq_dat = ( PVOID )&bus_data[ 1 ]; + inq_dat->PathId = 0; + inq_dat->TargetId = pdev_ext->umss_dev_id; + // + // this is the dev_id for usb dev_manager + // + inq_dat->Lun = ( UCHAR )( pdev_ext->dev_handle >> 16 ); + inq_dat->DeviceClaimed = FALSE; + inq_dat->InquiryDataLength = 36; + inq_dat->NextInquiryDataOffset = 0; + inq = ( PINQUIRYDATA )inq_dat->InquiryData; + + RtlZeroMemory( inq, sizeof( INQUIRYDATA ) ); + inq->DeviceType = DIRECT_ACCESS_DEVICE; + inq->DeviceTypeQualifier = 0; + inq->RemovableMedia = 1; + + // + // pretend to comply scsi primary 2 command set + // + + inq->Versions = 0x04; + + // + // the format is in scsi-2 format + // + + inq->ResponseDataFormat = 0x02; + + // + // we are the poor scsi device + // + + inq->AdditionalLength = 31; + inq->SoftReset = 0; + inq->CommandQueue = 0; + inq->LinkedCommands = 0; + inq->RelativeAddressing = 0; + RtlCopyMemory( &inq->VendorId, "Unknown", 7 ); + RtlCopyMemory( &inq->ProductId, "USB Mass Storage", 16 ); + irp->IoStatus.Information = required_size; + EXIT_DISPATCH( STATUS_SUCCESS, irp ); + } + case IOCTL_SCSI_GET_CAPABILITIES: + { + PIO_SCSI_CAPABILITIES port_cap; + + if( irp_stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof( IO_SCSI_CAPABILITIES ) ) + EXIT_DISPATCH( STATUS_INVALID_PARAMETER, irp ); + + port_cap = ( PIO_SCSI_CAPABILITIES ) irp->AssociatedIrp.SystemBuffer; + port_cap->Length = sizeof( IO_SCSI_CAPABILITIES ); + port_cap->MaximumTransferLength = 65536; + port_cap->MaximumPhysicalPages = 65536 / PAGE_SIZE; + port_cap->SupportedAsynchronousEvents = 0; + port_cap->AlignmentMask = 0x10; + port_cap->TaggedQueuing = FALSE; + port_cap->AdapterScansDown = FALSE; + port_cap->AdapterUsesPio = FALSE; + irp->IoStatus.Information = sizeof( IO_SCSI_CAPABILITIES ); + EXIT_DISPATCH( STATUS_SUCCESS, irp ); + } + case IOCTL_SCSI_GET_ADDRESS: + { + PSCSI_ADDRESS paddr; + if( irp_stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof( SCSI_ADDRESS ) ) + EXIT_DISPATCH( STATUS_INVALID_PARAMETER, irp ); + + paddr = ( PSCSI_ADDRESS )irp->AssociatedIrp.SystemBuffer; + + paddr->Length = sizeof( SCSI_ADDRESS ); + paddr->PortNumber = 0; + paddr->PathId = 0; + paddr->TargetId = pdev_ext->umss_dev_id; + paddr->Lun = ( UCHAR )( pdev_ext->dev_handle >> 16 ); + irp->IoStatus.Information = sizeof( SCSI_ADDRESS ); + EXIT_DISPATCH( STATUS_SUCCESS, irp ); + } + case IOCTL_SCSI_RESCAN_BUS: + { + irp->IoStatus.Information = 0; + EXIT_DISPATCH( STATUS_SUCCESS, irp ); + } + default: + { + EXIT_DISPATCH( STATUS_INVALID_DEVICE_REQUEST, irp ); + } + } + } + } + EXIT_DISPATCH( STATUS_NOT_SUPPORTED, irp ); +} + +VOID +umss_reset_pipe_completion( +PURB purb, +PVOID context +) +{ + PUMSS_DEVICE_EXTENSION pdev_ext; + if( context == NULL ) + return; + + pdev_ext = ( PUMSS_DEVICE_EXTENSION )context; + pdev_ext->reset_pipe_status = purb->status; + KeSetEvent( &pdev_ext->sync_event, 0, FALSE ); + return; +} + +NTSTATUS +umss_reset_pipe( +PUMSS_DEVICE_EXTENSION pdev_ext, +DEV_HANDLE endp_handle +) +//can only be called at passive level +{ + NTSTATUS status; + PUSB_DEV pdev; + PUSB_ENDPOINT pendp; + + if( pdev_ext == NULL ) + return STATUS_INVALID_PARAMETER; + + status = usb_query_and_lock_dev( pdev_ext->dev_mgr, pdev_ext->dev_handle, &pdev ); + + if( status != STATUS_SUCCESS ) + return STATUS_UNSUCCESSFUL; + + status = usb_reset_pipe_ex( + pdev_ext->dev_mgr, + endp_handle, + umss_reset_pipe_completion, + pdev_ext ); + + if( status == STATUS_PENDING ) + { + KeWaitForSingleObject( + &pdev_ext->sync_event, + Executive, + KernelMode, + TRUE, + NULL ); + status = pdev_ext->reset_pipe_status; + } + usb_unlock_dev( pdev ); + return status; +} + +BOOL +umss_gen_result_srb( +PIO_PACKET io_packet, +PSCSI_REQUEST_BLOCK srb, +NTSTATUS status +) +{ + + if( srb == NULL || io_packet == NULL ) + { + return FALSE; + } + if( status == STATUS_SUCCESS ) + { + PULONG dest_buf, src_buf; + ULONG i; + + srb->SrbStatus = SRB_STATUS_SUCCESS; + + io_packet->pirp->IoStatus.Information = srb->DataTransferLength; + if( ( io_packet->pirp->Flags & IRP_READ_OPERATION ) && !( io_packet->pirp->Flags & IRP_PAGING_IO ) ) + { + src_buf = ( PULONG )io_packet->data_buffer; + dest_buf = ( PULONG )srb->DataBuffer; + if( src_buf && dest_buf ) + { + for( i = 0; i < ( srb->DataTransferLength >> 2 ); i++ ) + { + dest_buf[ i ] = src_buf[ i ]; + } + } + } + } + else if( status == STATUS_DEVICE_DOES_NOT_EXIST ) + { + PSENSE_DATA sense_buf; + srb->SrbStatus = SRB_STATUS_NO_DEVICE; + srb->ScsiStatus = SCSISTAT_CHECK_CONDITION; + + // + // let's build the srb status for class driver + // + + srb->SrbStatus |= SRB_STATUS_AUTOSENSE_VALID; + sense_buf = ( PSENSE_DATA )srb->SenseInfoBuffer; + + if( !( srb->SrbFlags & SRB_FLAGS_DISABLE_AUTOSENSE ) ) + { + RtlZeroMemory( srb->SenseInfoBuffer, srb->SenseInfoBufferLength ); + sense_buf->ErrorCode = 0x70; + sense_buf->Valid = 1; + sense_buf->SenseKey = SCSI_SENSE_NOT_READY; + sense_buf->AdditionalSenseCode = SCSI_ADSENSE_NO_MEDIA_IN_DEVICE; + sense_buf->AdditionalSenseLength = 10; + } + } + else if( status == USB_STATUS_STALL_PID || status == USB_STATUS_CRC || + status == USB_STATUS_BTSTUFF || status == USB_STATUS_DATA_OVERRUN ) + { + PSENSE_DATA sense_buf; + srb->SrbStatus = SRB_STATUS_ERROR; + srb->ScsiStatus = SCSISTAT_CHECK_CONDITION; + + srb->SrbStatus |= SRB_STATUS_AUTOSENSE_VALID; + sense_buf = ( PSENSE_DATA )srb->SenseInfoBuffer; + + if( !( srb->SrbFlags & SRB_FLAGS_DISABLE_AUTOSENSE ) ) + { + RtlZeroMemory( srb->SenseInfoBuffer, srb->SenseInfoBufferLength ); + sense_buf->ErrorCode = 0x70; + sense_buf->Valid = 1; + sense_buf->SenseKey = SCSI_SENSE_HARDWARE_ERROR; + sense_buf->AdditionalSenseCode = 0; + sense_buf->AdditionalSenseLength = 10; + } + } + else + { + srb->SrbStatus = SRB_STATUS_ERROR; + } + + if( ( io_packet->pirp->Flags & ( IRP_READ_OPERATION | IRP_WRITE_OPERATION ) ) && !( io_packet->pirp->Flags & IRP_PAGING_IO ) ) + { + if( io_packet->data_buffer ) + { + usb_free_mem( io_packet->data_buffer ); + io_packet->data_buffer = NULL; + } + } + return TRUE; +} + +BOOL +umss_gen_result_ctrl( +PDEVICE_OBJECT dev_obj, +PIRP irp, +NTSTATUS status +) +{ + PIO_STACK_LOCATION irp_stack; + ULONG ctrl_code; + PUMSS_DEVICE_EXTENSION pdev_ext; + + if( irp == NULL ) + return FALSE; + + irp->IoStatus.Information = 0; + irp_stack = IoGetCurrentIrpStackLocation ( irp ); + ctrl_code = irp_stack->Parameters.DeviceIoControl.IoControlCode; + pdev_ext = dev_obj->DeviceExtension; + + switch( ctrl_code ) + { + case IOCTL_SCSI_PASS_THROUGH: + { + PSCSI_PASS_THROUGH pass_through; + pass_through = irp->AssociatedIrp.SystemBuffer; + irp->IoStatus.Status = status; + + // we have set these two value in bulkonly.c when data transfer complete + // pass_through_direct->DataTransferLength = pdev_ext->io_packet.data_length; + // pass_through_direct->SenseInfoLength = pdev_ext->io_packet.sense_data_length; + + if( status == STATUS_SUCCESS ) + irp->IoStatus.Information = pass_through->SenseInfoOffset + pass_through->SenseInfoLength; + else + pass_through->ScsiStatus = SCSISTAT_CHECK_CONDITION; + return TRUE; + } + case IOCTL_SCSI_PASS_THROUGH_DIRECT: + { + PSCSI_PASS_THROUGH_DIRECT pass_through_direct; + + pass_through_direct = irp->AssociatedIrp.SystemBuffer; + pass_through_direct->ScsiStatus = 0; + irp->IoStatus.Status = status; + + // we have set these two value in bulkonly.c when data transfer complete + // pass_through_direct->DataTransferLength = pdev_ext->io_packet.data_length; + // pass_through_direct->SenseInfoLength = pdev_ext->io_packet.sense_data_length; + + if( status == STATUS_SUCCESS ) + irp->IoStatus.Information = pass_through_direct->SenseInfoOffset + pass_through_direct->SenseInfoLength; + else + pass_through_direct->ScsiStatus = SCSISTAT_CHECK_CONDITION; + + return TRUE; + } + } + return FALSE; +} + + +VOID +umss_complete_request( +PUMSS_DEVICE_EXTENSION pdev_ext, +NTSTATUS status +) +{ + PIRP pirp; + KIRQL old_irql; + + PDEVICE_OBJECT dev_obj; + PIO_STACK_LOCATION irp_stack; + + usb_dbg_print( DBGLVL_MAXIMUM, ( "umss_complete_request(): entering...\n" ) ); + + pirp = pdev_ext->io_packet.pirp; + dev_obj = pdev_ext->pdo; + + irp_stack = IoGetCurrentIrpStackLocation ( pirp ); + + if( pdev_ext->io_packet.flags & IOP_FLAG_SRB_TRANSFER ) + { + if( pdev_ext->pif_desc->bInterfaceSubClass == UMSS_SUBCLASS_SFF8070I ) + { + umss_fix_sff_result( &pdev_ext->io_packet, irp_stack->Parameters.Scsi.Srb ); + } + umss_gen_result_srb( &pdev_ext->io_packet, irp_stack->Parameters.Scsi.Srb, status ); + } + else if( pdev_ext->io_packet.flags & IOP_FLAG_SCSI_CTRL_TRANSFER ) + umss_gen_result_ctrl( dev_obj, pirp, status ); + + //this device has its irp queued + if( status == STATUS_CANCELLED ) + { + IoAcquireCancelSpinLock( &old_irql ); + if( dev_obj->CurrentIrp == pirp ) + { + IoReleaseCancelSpinLock( old_irql ); + IoStartNextPacket( dev_obj, FALSE ); + } + else + { + KeRemoveEntryDeviceQueue( &dev_obj->DeviceQueue, &pirp->Tail.Overlay.DeviceQueueEntry ); + IoReleaseCancelSpinLock( old_irql ); + } + } + else + // all requests come to this point from the irp queue + IoStartNextPacket( dev_obj, FALSE ); + + pirp->IoStatus.Status = status; + + if( status != STATUS_SUCCESS ) + pirp->IoStatus.Information = 0; + + IoCompleteRequest( pirp, IO_NO_INCREMENT ); + return; +} + +BOOL +umss_if_connect( +PCONNECT_DATA params, +DEV_HANDLE if_handle +) +{ + PURB purb; + LONG if_idx, i; + PUCHAR desc_buf; + NTSTATUS status; + PUSB_DEV pdev; + PUSB_DRIVER pdrvr; + PUSB_INTERFACE_DESC pif_desc; + PUSB_CTRL_SETUP_PACKET psetup; + PUMSS_DEVICE_EXTENSION pdev_ext; + PUSB_CONFIGURATION_DESC pconfig_desc; + PUSB_DEV_MANAGER dev_mgr; + PUSB_ENDPOINT_DESC pendp_desc; + PUMSS_DRVR_EXTENSION pdrvr_ext; + + PDEVICE_OBJECT pdev_obj; + USE_IRQL; + //configuration is already set + purb = NULL; + desc_buf = NULL; + pdev = NULL; + + usb_dbg_print( DBGLVL_MAXIMUM, ( "umss_if_connect(): entering...\n" ) ); + + if( params == NULL ) + return FALSE; + + dev_mgr = params->dev_mgr; + pdrvr = params->pdriver; + + if_idx = if_idx_from_handle( if_handle ); + + purb = usb_alloc_mem( NonPagedPool, sizeof( URB ) ); + if( purb == NULL ) + goto ERROR_OUT; + + desc_buf = usb_alloc_mem( NonPagedPool, 512 ); + if( desc_buf == NULL ) + goto ERROR_OUT; + + psetup = ( PUSB_CTRL_SETUP_PACKET )( purb )->setup_packet; + urb_init( ( purb ) ); + + // now let's get the descs, one configuration, one interface and two endpoint + psetup = ( PUSB_CTRL_SETUP_PACKET )( purb )->setup_packet; + purb->endp_handle = if_handle | 0xffff; + purb->data_buffer = desc_buf; + purb->data_length = 512; + purb->completion = NULL; // this is an immediate request, no needs completion + purb->context = dev_mgr; + purb->reference = 0; + psetup->bmRequestType = 0x80; + psetup->bRequest = USB_REQ_GET_DESCRIPTOR; + psetup->wValue = USB_DT_CONFIG << 8; + psetup->wIndex = 0; + psetup->wLength = 512; + + status = usb_submit_urb( dev_mgr, purb ); + if( status == STATUS_PENDING ) + { + TRAP(); + } + usb_free_mem( purb ); + purb = NULL; + + if( status != STATUS_SUCCESS ) + { + goto ERROR_OUT; + } + + status = usb_query_and_lock_dev( dev_mgr, if_handle, &pdev ); + if( status != STATUS_SUCCESS ) + { + goto ERROR_OUT; + } + +#ifdef _TIANSHENG_DRIVER + if( !( ( pdev->pusb_dev_desc->idVendor == 0x03eb && pdev->pusb_dev_desc->idProduct == 0x2002 ) + ||( pdev->pusb_dev_desc->idVendor == 0x0ea0 && pdev->pusb_dev_desc->idProduct == 0x6803 ) + ||( pdev->pusb_dev_desc->idVendor == 0x0ef5 && pdev->pusb_dev_desc->idProduct == 0x2202 ) ) ) + { + // check TianSheng's product + goto ERROR_OUT; + } +#endif + + pdev_obj = umss_create_device( dev_mgr, pdrvr, if_handle, TRUE ); + if( pdev_obj == NULL ) + { + goto ERROR_OUT; + } + + lock_dev( pdev, FALSE ); + if( dev_state( pdev ) == USB_DEV_STATE_ZOMB || + dev_mgr_set_if_driver( dev_mgr, if_handle, pdrvr, pdev ) == FALSE ) + { + unlock_dev( pdev, FALSE ); + if( pdev_obj ) + { + umss_delete_device( dev_mgr, pdrvr, pdev_obj, TRUE ); + } + goto ERROR_OUT; + } + + if( pdev->usb_config ) + { + pdev->usb_config->interf[ if_idx ].if_ext = pdev_obj; + pdev->usb_config->interf[ if_idx ].if_ext_size = 0; + } + // olympus dev needs special care + if( UMSS_OLYMPUS_VENDOR_ID == pdev->pusb_dev_desc->idVendor ) + status = TRUE; + else + status = FALSE; + + unlock_dev( pdev, FALSE ); + + pdev_ext = ( PUMSS_DEVICE_EXTENSION )pdev_obj->DeviceExtension; + + pdev_ext->desc_buf = desc_buf; + pdev_ext->pif_desc = NULL; + pdev_ext->pin_endp_desc = pdev_ext->pout_endp_desc = NULL; + pconfig_desc = ( PUSB_CONFIGURATION_DESC )desc_buf; + pif_desc = ( PUSB_INTERFACE_DESC )( &pconfig_desc[ 1 ] ); + + if( status ) + pdev_ext->flags |= UMSS_DEV_FLAG_OLYMPUS_DEV; + + //search for our if + for( i = 0; ( ( UCHAR ) i ) < if_idx; i++ ) + { + if( usb_skip_if_and_altif( ( PUCHAR* )&pif_desc ) == FALSE ) + break; + } + pdev_ext->pif_desc = pif_desc; + + if( pdev_ext->pif_desc ) + { + pendp_desc = ( PUSB_ENDPOINT_DESC )&pif_desc[ 1 ]; + for( i = 0; ( ( UCHAR )i ) < pif_desc->bNumEndpoints; i++ ) + { + if( pendp_desc->bDescriptorType == USB_DT_ENDPOINT \ + && pendp_desc->bLength == sizeof( USB_ENDPOINT_DESC ) ) + { + if( ( pendp_desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK ) \ + == USB_ENDPOINT_XFER_INT ) + { + pdev_ext->pint_endp_desc = pendp_desc; + pdev_ext->int_endp_idx = ( UCHAR )i; + } + else if( ( pendp_desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK ) \ + == USB_ENDPOINT_XFER_BULK ) + { + if( pendp_desc->bEndpointAddress & USB_DIR_IN ) + { + pdev_ext->pin_endp_desc = pendp_desc; + pdev_ext->in_endp_idx = ( UCHAR )i; + } + else + { + pdev_ext->pout_endp_desc = pendp_desc; + pdev_ext->out_endp_idx = ( UCHAR )i; + } + } + pendp_desc = &pendp_desc[ 1 ]; + } + else + break; + } + } + + // notify the class driver, some device comes + pdrvr_ext = ( PUMSS_DRVR_EXTENSION )pdrvr->driver_ext; + if( pdrvr_ext && + pdrvr_ext->class_driver_info.add_device && + pdrvr_ext->class_driver_info.fdo_driver ) + pdrvr_ext->class_driver_info.add_device( pdrvr_ext->class_driver_info.fdo_driver, pdev_obj ); + + usb_unlock_dev( pdev ); + return TRUE; + +ERROR_OUT: + + if( desc_buf ) + usb_free_mem( desc_buf ); + + if( purb ) + usb_free_mem( purb ); + + usb_unlock_dev( pdev ); + + desc_buf = NULL; + purb = NULL; + + return FALSE; +} + +BOOL +umss_if_disconnect( +PUSB_DEV_MANAGER dev_mgr, +DEV_HANDLE if_handle +) +{ + LONG if_idx; + NTSTATUS status; + PUSB_DEV pdev; + PUSB_DRIVER pdrvr; + PDEVICE_OBJECT dev_obj; + PUMSS_DRVR_EXTENSION pdrvr_ext; + PUMSS_DEVICE_EXTENSION pdev_ext; + + if( dev_mgr == NULL || if_handle == 0 ) + return FALSE; + + pdev = NULL; + if_idx = if_idx_from_handle( if_handle ); + // + // special use of the lock dev, simply use this routine to get the dev + // + status = usb_query_and_lock_dev( dev_mgr, if_handle, &pdev ); + if( pdev == NULL ) + { + return FALSE; + } + if( status == STATUS_SUCCESS ) + { + // must be a bug + TRAP(); + } + if( pdev->usb_config ) + { + pdrvr = pdev->usb_config->interf[ if_idx ].pif_drv; + dev_obj = ( PDEVICE_OBJECT )pdev->usb_config->interf[ if_idx ].if_ext; + } + pdev = NULL; + + // notify the class driver, some device gone + pdrvr_ext = ( PUMSS_DRVR_EXTENSION )pdrvr->driver_ext; + pdev_ext = dev_obj->DeviceExtension; + if( pdrvr_ext && pdrvr_ext->class_driver_info.pnp_dispatch ) + pdrvr_ext->class_driver_info.pnp_dispatch( dev_obj, UMSS_PNPMSG_DISCONNECT, NULL ); + + // no need to unlock the dev + return umss_delete_device( dev_mgr, pdrvr, dev_obj, TRUE ); +} + +BOOL +umss_if_stop( +PUSB_DEV_MANAGER dev_mgr, +DEV_HANDLE if_handle +) +{ + LONG if_idx; + NTSTATUS status; + PUSB_DEV pdev; + PUSB_DRIVER pdrvr; + PDEVICE_OBJECT dev_obj; + PUMSS_DRVR_EXTENSION pdrvr_ext; + PUMSS_DEVICE_EXTENSION pdev_ext; + + USE_IRQL; + + if( dev_mgr == NULL || if_handle == 0 ) + return FALSE; + + pdev = NULL; + if_idx = if_idx_from_handle( if_handle ); + + // special use of the lock dev, simply use this routine to get the dev + status = usb_query_and_lock_dev( dev_mgr, if_handle, &pdev ); + if( status != STATUS_SUCCESS ) + { + return FALSE; + } + + lock_dev( pdev, FALSE ); + if( dev_state( pdev ) == USB_DEV_STATE_ZOMB ) + { + return FALSE; + } + + if( pdev->usb_config ) + { + pdrvr = pdev->usb_config->interf[ if_idx ].pif_drv; + dev_obj = ( PDEVICE_OBJECT )pdev->usb_config->interf[ if_idx ].if_ext; + } + unlock_dev( pdev, FALSE ); + + // notify the class driver, some device stops + pdev_ext = dev_obj->DeviceExtension; + pdrvr_ext = ( PUMSS_DRVR_EXTENSION )pdrvr->driver_ext; + if( pdrvr_ext && pdrvr_ext->class_driver_info.pnp_dispatch ) + pdrvr_ext->class_driver_info.pnp_dispatch( dev_obj, UMSS_PNPMSG_STOP, NULL ); + + usb_unlock_dev( pdev ); + return TRUE; +} + +VOID +umss_load_class_driver( +PVOID context +) +{ + NTSTATUS status; + UNICODE_STRING unicode_string; + + // + // let's load the class driver + // + RtlInitUnicodeString(&unicode_string, L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\usbstor"); + status = ZwLoadDriver( &unicode_string ); + usb_dbg_print( DBGLVL_MAXIMUM, ( "umss_load_class_driver(): try to load class driver, status=0x%x\n", status ) ); +} +BOOL +umss_if_driver_init( +PUSB_DEV_MANAGER dev_mgr, +PUSB_DRIVER pdriver +) +{ + PUMSS_DRVR_EXTENSION pdrvr_ext; + UNICODE_STRING unicode_string; + NTSTATUS status; + + if( dev_mgr == NULL || pdriver == NULL ) + return FALSE; + + //init driver structure, no PNP table functions + + pdriver->driver_desc.flags = USB_DRIVER_FLAG_IF_CAPABLE; + pdriver->driver_desc.vendor_id = 0x0000; // USB Vendor ID + pdriver->driver_desc.product_id = 0x0000; // USB Product ID. + pdriver->driver_desc.release_num = 0x100; // Release Number of Device + + pdriver->driver_desc.config_val = 1; // Configuration Value + pdriver->driver_desc.if_num = 1; // Interface Number + pdriver->driver_desc.if_class = USB_CLASS_MASS_STORAGE; // Interface Class + pdriver->driver_desc.if_sub_class = 0; // Interface SubClass + pdriver->driver_desc.if_protocol = 0; // Interface Protocol + + pdriver->driver_desc.driver_name = "USB Mass Storage interface driver"; // Driver name for Name Registry + pdriver->driver_desc.dev_class = USB_CLASS_PER_INTERFACE; + pdriver->driver_desc.dev_sub_class = 0; // Device Subclass + pdriver->driver_desc.dev_protocol = 0; // Protocol Info. + + pdriver->driver_ext = usb_alloc_mem( NonPagedPool, sizeof( UMSS_DRVR_EXTENSION ) ); + pdriver->driver_ext_size = sizeof( UMSS_DRVR_EXTENSION ); + + RtlZeroMemory( pdriver->driver_ext, sizeof( UMSS_DRVR_EXTENSION ) ); + + pdrvr_ext = ( PUMSS_DRVR_EXTENSION )pdriver->driver_ext; + pdrvr_ext->dev_count = 0; + InitializeListHead( &pdrvr_ext->dev_list ); + ExInitializeFastMutex( &pdrvr_ext->dev_list_mutex ); + + pdriver->disp_tbl.version = 1; + pdriver->disp_tbl.dev_connect = umss_if_connect; + pdriver->disp_tbl.dev_disconnect = umss_if_disconnect; + pdriver->disp_tbl.dev_stop = umss_if_stop; + pdriver->disp_tbl.dev_reserved = NULL; + + if( ( pdrvr_ext->port_dev_obj = umss_create_port_device( dev_mgr, pdriver ) ) == NULL ) + { + usb_free_mem( pdriver->driver_ext ); + pdriver->driver_ext = NULL; + pdriver->driver_ext_size = 0; + pdriver->disp_tbl.dev_connect = NULL; + pdriver->disp_tbl.dev_stop = NULL; + pdriver->disp_tbl.dev_disconnect = NULL; + return FALSE; + } + + // + // let's load the class driver + // + + umss_load_class_driver( NULL ); + + // umss_schedule_workitem( NULL, umss_load_class_driver, NULL, 0 ); + usb_dbg_print( DBGLVL_MAXIMUM, ( "umss_if_driver_init(): umss driver is initialized\n" ) ); + + return TRUE; +} + +PCLASS_DRV_REG_INFO +umss_get_if_driver_info( +PUSB_DEV_MANAGER dev_mgr, +PUSB_DEV pdev, +DEV_HANDLE if_handle +) +// get the driver reg information for pnp notification to class +// driver. +// bug??? how if the driver info is returned while the driver +// is being unloaded. +// So the routine must be called when usb_query_and_lock_dev is +// called. +{ + PUMSS_DRVR_EXTENSION drvr_ext; + ULONG if_idx; + USE_IRQL; + + if_idx = if_idx_from_handle( if_handle ); + if( if_idx >= 4 ) // max interfaces per config supports. defined in td.h + return NULL; + + ASSERT( pdev != NULL ); + + lock_dev( pdev, FALSE ); + if( dev_state( pdev ) == USB_DEV_STATE_ZOMB ) + { + unlock_dev( pdev, FALSE ); + usb_unlock_dev( pdev ); + return NULL; + } + + drvr_ext = NULL; + + if( pdev->usb_config->interf[ if_idx ].pif_drv ) + drvr_ext = ( PUMSS_DRVR_EXTENSION )pdev->usb_config->interf[ if_idx ].pif_drv->driver_ext; + else + TRAP(); + + unlock_dev( pdev, FALSE ); + + if( drvr_ext == NULL ) + { + return NULL; + } + + return &drvr_ext->class_driver_info; +} + +VOID +umss_worker( +IN PVOID reference +) +{ + PUMSS_WORKER_PACKET worker_packet; + PUSB_DEV pdev; + + usb_dbg_print( DBGLVL_MAXIMUM, ( "umss_worker(): entering...\n" ) ); + worker_packet = ( PUMSS_WORKER_PACKET )reference; + worker_packet->completion( worker_packet->context ); + if( worker_packet->dev_mgr && worker_packet->pdev ) + { + pdev = ( PUSB_DEV ) worker_packet->pdev; + usb_unlock_dev( pdev ); + pdev = NULL; + } + usb_free_mem( worker_packet ); + usb_dbg_print( DBGLVL_MAXIMUM, ( "umss_worker(): exit\n" ) ); +} + +BOOL +umss_schedule_workitem( +PVOID context, +UMSS_WORKER_ROUTINE completion, +PUSB_DEV_MANAGER dev_mgr, +DEV_HANDLE dev_handle +) +/*++ +Routine Description: + + Wrapper for handling worker thread callbacks, it is importent to + lock the dev from being deleted by calling usb_query_and_lock_dev + and in umss_worker, call the usb_unlock_dev to release the ref + count. One exception is that the umss_if_disconnect call this + function to delete the device object that is still held by some + others, and deferred deletion is required. + + Arguments: + + Routine - Routine to be called when this work-item is processed + Context - Value to be passed to worker routine + +Return Value: + TRUE if work item queued + FALSE if work item not queued + +--*/ + +{ + BOOL ret_val = TRUE; + PWORK_QUEUE_ITEM workitem; + PUMSS_WORKER_PACKET worker_packet; + + worker_packet = usb_alloc_mem( NonPagedPool, sizeof( WORK_QUEUE_ITEM ) + sizeof( UMSS_WORKER_PACKET ) ); + RtlZeroMemory( worker_packet, sizeof( WORK_QUEUE_ITEM ) + sizeof( UMSS_WORKER_PACKET ) ); + + if ( worker_packet ) + { + workitem = ( PWORK_QUEUE_ITEM )&worker_packet[ 1 ]; + worker_packet->completion = completion; + worker_packet->context = context; + + if( dev_mgr != NULL && dev_handle != 0 ) + { + PUSB_DEV pdev; + // lock the device until the workitem is executed. + if( usb_query_and_lock_dev( dev_mgr, dev_handle, &pdev ) == STATUS_SUCCESS ) + { + worker_packet->dev_mgr = dev_mgr; + worker_packet->pdev = pdev; + } + else + { + usb_free_mem( worker_packet ); + return FALSE; + } + } + // Initialize the work-item + ExInitializeWorkItem( + workitem, + umss_worker, + worker_packet + ); + + // Schedule the work-item + ExQueueWorkItem( + workitem, + DelayedWorkQueue + ); + + usb_dbg_print( DBGLVL_MINIMUM,( "umss_schedule_workitem(): work-item queued\n" ) ); + } + else + { + usb_dbg_print( DBGLVL_MINIMUM,("umss_schedule_workitem(): Failed to allocate work-item\n")); + ret_val = FALSE; + } + + return ret_val; +} + +NTSTATUS +umss_process_srb( +PDEVICE_OBJECT dev_obj, +PIRP irp +) +{ + NTSTATUS status; + PUSB_DEV pdev; + PIO_STACK_LOCATION cur_stack; + PUMSS_DEVICE_EXTENSION pdev_ext; + PSCSI_REQUEST_BLOCK srb; + + if( dev_obj == NULL || irp == NULL ) + return STATUS_INVALID_PARAMETER; + + pdev = NULL; + cur_stack = IoGetCurrentIrpStackLocation( irp ); + srb = cur_stack->Parameters.Scsi.Srb; + + if( srb == NULL || srb->DataTransferLength > 65536 ) + { + status = STATUS_INVALID_PARAMETER; + goto ERROR_OUT; + } + + irp->IoStatus.Status = STATUS_SUCCESS; + irp->IoStatus.Information = 0; + + pdev_ext = ( PUMSS_DEVICE_EXTENSION )dev_obj->DeviceExtension; + if( ( status = usb_query_and_lock_dev( pdev_ext->dev_mgr, pdev_ext->dev_handle, &pdev ) ) != STATUS_SUCCESS ) + { + PSENSE_DATA sense_buf; + srb->SrbStatus = SRB_STATUS_NO_DEVICE; + + // + // let's build the srb status for class driver + // + + srb->SrbStatus |= SRB_STATUS_AUTOSENSE_VALID; + RtlZeroMemory( srb->SenseInfoBuffer, srb->SenseInfoBufferLength ); + if( !( srb->SrbFlags & SRB_FLAGS_DISABLE_AUTOSENSE ) ) + { + sense_buf = ( PSENSE_DATA )srb->SenseInfoBuffer; + sense_buf->ErrorCode = 0x70; + sense_buf->Valid = 1; + sense_buf->SenseKey = SCSI_SENSE_NOT_READY; + sense_buf->AdditionalSenseCode = SCSI_ADSENSE_NO_MEDIA_IN_DEVICE; + sense_buf->AdditionalSenseLength = 10; + } + goto ERROR_OUT; + } + + switch( srb->Function ) + { + case SRB_FUNCTION_EXECUTE_SCSI: + { + IO_PACKET io_packet; + RtlZeroMemory( &io_packet, sizeof( io_packet ) ); + + io_packet.flags |= IOP_FLAG_SRB_TRANSFER; + if( srb->SrbFlags & SRB_FLAGS_DATA_IN ) + io_packet.flags |= IOP_FLAG_DIR_IN; + if( !( srb->SrbFlags & SRB_FLAGS_DISABLE_AUTOSENSE ) ) + io_packet.flags |= IOP_FLAG_REQ_SENSE; + + io_packet.cdb_length = srb->CdbLength; + RtlCopyMemory( io_packet.cdb, srb->Cdb, sizeof( io_packet.cdb ) ); + io_packet.lun = 0; + + if( srb->SrbFlags & SRB_FLAGS_NO_DATA_TRANSFER ) + { + io_packet.data_buffer = NULL; + io_packet.data_length = 0; + } + else + { + if( ( irp->Flags & ( IRP_READ_OPERATION | IRP_WRITE_OPERATION ) ) && !( irp->Flags & IRP_PAGING_IO ) ) + { + // + // since these operations does not allign the buffer on page boundary + // and some unknown traps in window NT, we have to copy to a buffer + // we allocated + io_packet.data_buffer = usb_alloc_mem( NonPagedPool, srb->DataTransferLength ); + if( irp->Flags & IRP_WRITE_OPERATION ) + { + PULONG dest_buf, src_buf; + ULONG i; + + dest_buf = ( PULONG )io_packet.data_buffer; + src_buf = ( PULONG )srb->DataBuffer; + + if( src_buf && dest_buf ) + { + for( i = 0; i < ( srb->DataTransferLength >> 2 ); i++ ) + { + dest_buf[ i ] = src_buf[ i ]; + } + } + } + } + else + io_packet.data_buffer = srb->DataBuffer; + + io_packet.data_length = srb->DataTransferLength; + } + + if( io_packet.flags & IOP_FLAG_REQ_SENSE ) + { + io_packet.sense_data = srb->SenseInfoBuffer; + io_packet.sense_data_length = srb->SenseInfoBufferLength; + } + + io_packet.pirp = irp; + + // do some conversions + if( pdev_ext->pif_desc->bInterfaceSubClass == UMSS_SUBCLASS_SFF8070I ) + { + if( umss_tsc_to_sff( &io_packet ) == FALSE ) + { + status = STATUS_DEVICE_PROTOCOL_ERROR; + + usb_dbg_print( DBGLVL_MAXIMUM, ( "umss_process_srb(): error converting to sff proto, 0x%x\n", + status ) ); + srb->SrbStatus = SRB_STATUS_ERROR; + break; + } + } + + if( pdev_ext->pif_desc->bInterfaceProtocol == PROTOCOL_BULKONLY ) + { + // + // currently we support only transparent scsi command set + // + if( pdev_ext->pif_desc->bInterfaceSubClass == UMSS_SUBCLASS_SCSI_TCS || \ + pdev_ext->pif_desc->bInterfaceSubClass == UMSS_SUBCLASS_SFF8070I ) + status = umss_bulkonly_startio( pdev_ext, &io_packet ); + else + status = STATUS_DEVICE_PROTOCOL_ERROR; + } + else if( pdev_ext->pif_desc->bInterfaceProtocol == PROTOCOL_CB + || pdev_ext->pif_desc->bInterfaceProtocol == PROTOCOL_CBI ) + { + status = umss_cbi_startio( pdev_ext, &io_packet ); + } + else + { + status = STATUS_DEVICE_PROTOCOL_ERROR; + } + + if( status != STATUS_PENDING && status != STATUS_SUCCESS ) + { + // error occured + usb_dbg_print( DBGLVL_MAXIMUM, ( "umss_process_srb(): error sending request, 0x%x\n", + status ) ); + srb->SrbStatus = SRB_STATUS_ERROR; + } + break; + } + case SRB_FUNCTION_CLAIM_DEVICE: + { + srb->DataBuffer = ( PVOID )dev_obj; + } + case SRB_FUNCTION_SHUTDOWN: + case SRB_FUNCTION_FLUSH: + case SRB_FUNCTION_RESET_BUS: + case SRB_FUNCTION_FLUSH_QUEUE: + case SRB_FUNCTION_RELEASE_QUEUE: + case SRB_FUNCTION_RELEASE_DEVICE: + default: + { + // for usb flash disk, they are luxurious + + usb_dbg_print( DBGLVL_MAXIMUM, ( "umss_process_srb(): current srb->Function=0x%x\n", + srb->Function ) ); + + status = STATUS_SUCCESS; + srb->SrbStatus = SRB_STATUS_SUCCESS; + break; + } + } + + usb_unlock_dev( pdev ); + pdev = NULL; + +ERROR_OUT: + + irp->IoStatus.Status = status; + if( status != STATUS_PENDING ) + { + IoStartNextPacket( dev_obj, FALSE ); + IoCompleteRequest( irp, IO_NO_INCREMENT ); + } + // + // UMSS_COMPLETE_START_IO( dev_obj, status, irp ); + // + return status; +} + +BOOL +umss_tsc_to_sff( +PIO_PACKET io_packet +) +{ + if( io_packet == NULL ) + return FALSE; + + io_packet->cdb_length = 12; + if( io_packet->cdb[ 0 ] == SCSIOP_MODE_SENSE ) + { + io_packet->cdb[ 0 ] = 0x5a; // mode sense( 10 ) + io_packet->cdb[ 8 ] = io_packet->cdb[ 4 ]; + io_packet->cdb[ 4 ] = 0; + if( io_packet->cdb[ 8 ] < 8 ) + io_packet->cdb[ 8 ] = 8; + + io_packet->data_length = 8; + return TRUE; + } + if( io_packet->cdb[ 0 ] == SCSIOP_REASSIGN_BLOCKS || \ + io_packet->cdb[ 0 ] == SCSIOP_RESERVE_UNIT || \ + io_packet->cdb[ 0 ] == SCSIOP_RELEASE_UNIT ) + return FALSE; + + return TRUE; +} +VOID +umss_fix_sff_result( +PIO_PACKET io_packet, +SCSI_REQUEST_BLOCK *srb ) +{ + PBYTE buf; + if( io_packet->cdb[ 0 ] != 0x5a ) + return; + // the following is not right since it has to be 0x3f, return all pages + // if( io_packet->cdb[ 2 ] != 0 ) + // return; + srb->DataTransferLength = 4; + buf = io_packet->data_buffer; + // convert the mode param to scsi II + buf[ 0 ] = buf[ 1 ]; + buf[ 1 ] = buf[ 2 ]; + buf[ 2 ] = buf[ 3 ]; + buf[ 3 ] = 0; + return; +} diff --git a/reactos/drivers/usb/nt4compat/usbdriver/umss.h b/reactos/drivers/usb/nt4compat/usbdriver/umss.h new file mode 100644 index 00000000000..1adda3cfee7 --- /dev/null +++ b/reactos/drivers/usb/nt4compat/usbdriver/umss.h @@ -0,0 +1,353 @@ +#ifndef __UMSS_H__ +#define __UMSS_H__ + +#include "td.h" +#include "hub.h" + +#define MAX_BULK_TRANSFER_LENGTH 0x100000 + +#define PROTOCOL_CBI 0x00 +#define PROTOCOL_CB 0x01 +#define PROTOCOL_BULKONLY 0x50 + +#define PROTOCOL_UNDEFINED 0xFF // Not in spec + +#define UMSS_SUBCLASS_RBC 0x01 +#define UMSS_SUBCLASS_SFF8020I 0X02 +#define UMSS_SUBCLASS_QIC157 0x03 +#define UMSS_SUBCLASS_UFI 0x04 +#define UMSS_SUBCLASS_SFF8070I 0x05 +#define UMSS_SUBCLASS_SCSI_TCS 0x06 + +#define ACCEPT_DEVICE_SPECIFIC_COMMAND 0 + +#define BULK_ONLY_MASS_STORAGE_RESET 0xFF +#define BULK_ONLY_GET_MAX_LUN 0xFE + +#define CBW_SIGNATURE 0x43425355L +#define CSW_SIGNATURE 0x53425355L +#define CSW_OLYMPUS_SIGNATURE 0x55425355L + +#define CSW_STATUS_PASSED 0x00 +#define CSW_STATUS_FAILED 0x01 +#define CSW_STATUS_PHASE_ERROR 0x02 + +#define IOCTL_UMSS_SUBMIT_CDB CTL_CODE( FILE_USB_DEV_TYPE, 4200, METHOD_BUFFERED, FILE_ANY_ACCESS ) +// for request with no other input and output +// input buffer is a _USER_IO_PACKET and input_buffer_length is length of the _USER_IO_PACKET +// output_buffer is NULL, and output_buffer_length is zero + +#define IOCTL_UMSS_SUBMIT_CDB_IN CTL_CODE( FILE_USB_DEV_TYPE, 4201, METHOD_IN_DIRECT, FILE_ANY_ACCESS ) +// for request to read in data +// input_buffer is a _USER_IO_PACKET and input_buffer_length is length to the _USER_IO_PACKET +// output_buffer is a buffer to receive the data from dev, and +// output_buffer_length is the size of the buffer + +#define IOCTL_UMSS_SUBMIT_CDB_OUT CTL_CODE( FILE_USB_DEV_TYPE, 4202, METHOD_OUT_DIRECT, FILE_ANY_ACCESS ) +// for request to write data to device +// input_buffer is a _USER_IO_PACKET and input_buffer_length is length to the _USER_IO_PACKET +// output_buffer is data to send to the device, and +// output_buffer_length is the size of the buffer + +#define IOCTL_REGISTER_DRIVER CTL_CODE( FILE_USB_DEV_TYPE, 4203, METHOD_BUFFERED, FILE_ANY_ACCESS ) +// input_buffer is a CLASS_DRV_REG_INFO, and input_buffer_length is equal to or greater than +// sizeof( CLASS_DRV_REG_INFO ); the output_buffer is null and no output_buffer_length, +// only the following fields in urb can be accessed, others must be zeroed. + +#define IOCTL_REVOKE_DRIVER CTL_CODE( FILE_USB_DEV_TYPE, 4204, METHOD_BUFFERED, FILE_ANY_ACCESS ) +// tell the umss driver to clear the information in the drivers registry +// no other parameters + +#define IOCTL_UMSS_SUBMIT_SRB CTL_CODE( FILE_USB_DEV_TYPE, 4205, METHOD_BUFFERED, FILE_ANY_ACCESS ) +// irpStack->Parameters.Scsi.Srb points to an Srb structure and all the data buffer and buffer +// size are stored in the srb + + +#define IOCTL_UMSS_SET_FDO CTL_CODE( FILE_USB_DEV_TYPE, 4206, METHOD_BUFFERED, FILE_ANY_ACCESS ) +// input_buffer is a pointer to PDEVICE_OBJECT, and input_buffer_length should be +// no less than sizeof( PDEVICE_OBJECT ) +// output buffer is NULL, and output_buffer_length is zero +// if the deivce is accessable, the fdo is set, else, the fdo is not set and return +// STATUS_DEVICE_DOES_NOT_EXIST + +#define SFF_FORMAT_UNIT 0x04 +#define SFF_INQUIRY 0x12 +#define SFF_MODE_SELECT 0x55 +#define SFF_MODE_SENSE 0x5a +#define SFF_ALLOW_REMOVE 0x1e +#define SFF_READ10 0x28 +#define SFF_READ12 0xa8 +#define SFF_READ_CAPACITY 0x25 +#define SFF_REQUEST_SENSEE 0x03 +#define SFF_SEEK 0x2b +#define SFF_START_STOP 0x1b +#define SFF_TUR 0x00 +#define SFF_VERIFY 0x2f +#define SFF_WRITE10 0x2a +#define SFF_WRITE12 0xaa +#define SFF_READ_FMT_CAPACITY 0x23 +#define SFF_WRITE_VERIFY 0x2e + +#define MAX_CDB_LENGTH 0x10 + +typedef struct _USER_IO_PACKET +{ + UCHAR sub_class; + UCHAR lun; + UCHAR cdb_length; + UCHAR cdb[ MAX_CDB_LENGTH ]; + +} USER_IO_PACKET, *PUSER_IO_PACKET; + +//flags for IO_PACKET::flags +#define IOP_FLAG_REQ_SENSE 0x80000000 // sense data would be fetched if error occurs +#define IOP_FLAG_SRB_TRANSFER 0x40000000 // current tranfer is initiated by an srb request, the srb is held by the irp +#define IOP_FLAG_SCSI_CTRL_TRANSFER 0x20000000 // current transfer is initiated by an scsi ioctrl request + +#define IOP_FLAG_DIR_IN USB_DIR_IN +#define IOP_FLAG_STAGE_MASK 0x03 +#define IOP_FLAG_STAGE_NORMAL 0x00 +#define IOP_FLAG_STAGE_SENSE 0x01 + +typedef struct _IO_PACKET +{ + ULONG flags; + UCHAR cdb_length; + UCHAR cdb[ MAX_CDB_LENGTH ]; + UCHAR lun; + PVOID data_buffer; + ULONG data_length; + PVOID sense_data; + ULONG sense_data_length; + PIRP pirp; + +} IO_PACKET, *PIO_PACKET; + +#pragma pack( 1 ) + +typedef struct _COMMAND_BLOCK_WRAPPER +{ + ULONG dCBWSignature; + ULONG dCBWTag; + ULONG dCBWDataTransferLength; + UCHAR bmCBWFlags; + UCHAR bCBWLun; + UCHAR bCBWLength; + UCHAR CBWCB[ MAX_CDB_LENGTH ]; + +} COMMAND_BLOCK_WRAPPER, *PCOMMAND_BLOCK_WRAPPER; + +typedef struct _COMMAND_STATUS_WRAPPER +{ + ULONG dCSWSignature; + ULONG dCSWTag; + ULONG dCSWDataResidue; + UCHAR bCSWStatus; + +} COMMAND_STATUS_WRAPPER, *PCOMMAND_STATUS_WRAPPER; + + +typedef struct _INTERRUPT_DATA_BLOCK +{ + UCHAR bType; + UCHAR bValue; + +} INTERRUPT_DATA_BLOCK, *PINTERRUPT_DATA_BLOCK; + +#pragma pack() + +#define UMSS_PNPMSG_STOP 0x01 +#define UMSS_PNPMSG_DISCONNECT 0x02 + +typedef NTSTATUS ( *PCLASS_DRVR_PNP_DISP )( PDEVICE_OBJECT pdo, ULONG ctrl_code, PVOID context ); + // pdo is the device object umss created + +typedef PDEVICE_OBJECT ( *PCLASS_DRIVER_ADD_DEV )( PDRIVER_OBJECT fdo_drvr, PDEVICE_OBJECT pdo ); + // if the return value is not zero, it is a pointer to the + // fdo sitting over the pdo of this driver. if it is null, + // the add_device failed, and initialization process should + // stall. + +typedef struct _CLASS_DRV_REGISTRY_INFO +{ + // class driver will pass this structure to umss port + // driver after loaded + PDRIVER_OBJECT fdo_driver; + PCLASS_DRIVER_ADD_DEV add_device; + PCLASS_DRVR_PNP_DISP pnp_dispatch; + +} CLASS_DRV_REG_INFO, *PCLASS_DRV_REG_INFO; + +typedef struct _UMSS_PORT_DEVICE_EXTENSION +{ + // this structure is the device extension for port dev_obj + // it is used to has class driver pass CLASS_DRV_REG_INFO + // to our umss driver. + DEVEXT_HEADER dev_ext_hdr; + PUSB_DRIVER pdriver; + +} UMSS_PORT_DEV_EXT, *PUMSS_PORT_DEV_EXT; + +typedef struct _UMSS_DRVR_EXTENSION +{ + LIST_HEAD dev_list; + FAST_MUTEX dev_list_mutex; + UCHAR dev_count; + CLASS_DRV_REG_INFO class_driver_info; + PDEVICE_OBJECT port_dev_obj; // we use this obj as a connection point for class driver, its name usbPort0 + +} UMSS_DRVR_EXTENSION, *PUMSS_DRVR_EXTENSION; + +#define UMSS_DEV_FLAG_IF_DEV 0x01 +#define UMSS_DEV_FLAG_OLYMPUS_DEV 0x02 + +#define UMSS_OLYMPUS_VENDOR_ID 0x07b4 + +typedef struct _UMSS_DEVICE_EXTENSION +{ + //this structure is the device extension for dev_obj + //created for the device. + DEVEXT_HEADER dev_ext_hdr; + + ULONG flags; + LIST_ENTRY dev_obj_link; // this link is used by the driver object to track the existing dev_objs + + PDEVICE_OBJECT pdo; // this is the pdo + PDEVICE_OBJECT fdo; // driver object for the dev_obj + + DEV_HANDLE dev_handle; // handle to the usb_dev under + + PUCHAR desc_buf; + UCHAR umss_dev_id; // used to build symbolic link + + PUSB_INTERFACE_DESC pif_desc; + PUSB_ENDPOINT_DESC pout_endp_desc, pin_endp_desc, pint_endp_desc; + UCHAR if_idx, out_endp_idx, in_endp_idx, int_endp_idx; + + struct _USB_DEV_MANAGER *dev_mgr; + + //working data + COMMAND_BLOCK_WRAPPER cbw; + union + { + INTERRUPT_DATA_BLOCK idb; + COMMAND_STATUS_WRAPPER csw; + }; + + KEVENT sync_event; //for umss_sync_submit_urb + KSPIN_LOCK dev_lock; + IO_PACKET io_packet; + BOOL retry; + + PUSB_DRIVER pdriver; //used by umss_delete_device + NTSTATUS reset_pipe_status; +} UMSS_DEVICE_EXTENSION, *PUMSS_DEVICE_EXTENSION; + +// for device creation workitem +typedef struct _UMSS_CREATE_DATA +{ + DEV_HANDLE dev_handle; + PUCHAR desc_buf; + PUSB_DEV_MANAGER dev_mgr; + PUSB_DRIVER pdriver; + +} UMSS_CREATE_DATA, *PUMSS_CREATE_DATA; + +// for reset pipe item +//typedef void ( _stdcall *COMPLETION_HANDLER )( PVOID ); +typedef void ( *UMSS_WORKER_ROUTINE )( PVOID ); + +typedef struct _UMSS_WORKER_PACKET +{ + UMSS_WORKER_ROUTINE completion; + PVOID context; + PUSB_DEV_MANAGER dev_mgr; + PVOID pdev; + +} UMSS_WORKER_PACKET, *PUMSS_WORKER_PACKET; + +BOOL +umss_driver_init( +PUSB_DEV_MANAGER dev_mgr, +PUSB_DRIVER pdriver +); + +BOOL +umss_if_driver_init( +PUSB_DEV_MANAGER dev_mgr, +PUSB_DRIVER pdriver +); + +BOOL +umss_driver_destroy( +PUSB_DEV_MANAGER dev_mgr, +PUSB_DRIVER pdriver +); + +#define umss_if_driver_destroy umss_driver_destroy + +BOOL +umss_if_driver_destroy( +PUSB_DEV_MANAGER dev_mgr, +PUSB_DRIVER pdriver +); + +VOID +umss_complete_request( +PUMSS_DEVICE_EXTENSION pdev_ext, +NTSTATUS status +); + +NTSTATUS +umss_reset_pipe( +PUMSS_DEVICE_EXTENSION pdev_ext, +DEV_HANDLE endp_handle +); + +PVOID +umss_get_buffer( +PUMSS_DEVICE_EXTENSION pdev_ext, +ULONG* buf_length +); + +NTSTATUS +umss_bulk_transfer( +IN PUMSS_DEVICE_EXTENSION pdev_ext, +IN UCHAR trans_dir, +IN PVOID buf, +IN ULONG buf_length, +IN PURBCOMPLETION completion +); + +BOOL +umss_schedule_workitem( +PVOID context, +UMSS_WORKER_ROUTINE completion, +PUSB_DEV_MANAGER dev_mgr, +DEV_HANDLE dev_handle +); + +NTSTATUS +umss_bulkonly_startio( +IN PUMSS_DEVICE_EXTENSION pdev_ext, +IN PIO_PACKET io_packet +); + +NTSTATUS +umss_cbi_startio( +IN PUMSS_DEVICE_EXTENSION pdev_ext, +IN PIO_PACKET io_packet +); + +#define UMSS_FORGE_GOOD_SENSE( sense_BUF ) \ +{\ + int i;\ + PUCHAR buf = ( PUCHAR )( sense_BUF ); \ + for( i = 0; i < 18; i++)\ + {\ + buf[i] = 0;\ + }\ + buf[7] = 10;\ +} + +#endif diff --git a/reactos/drivers/usb/nt4compat/usbdriver/usb.c b/reactos/drivers/usb/nt4compat/usbdriver/usb.c new file mode 100644 index 00000000000..06e97a0deda --- /dev/null +++ b/reactos/drivers/usb/nt4compat/usbdriver/usb.c @@ -0,0 +1,1518 @@ +/** + * usb.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 "ntddk.h" +#include "usb.h" +#include "hub.h" +#include "debug.h" + +LONG g_alloc_cnt = 0; +ULONG cpu_clock_freq = 0; + +NTSTATUS +usb_get_descriptor( +PUSB_DEV pdev, +PURB purb +); + +VOID +usb_set_interface_completion( +PURB purb, +PVOID context +); + +NTSTATUS +usb_set_interface( +PURB purb +); + +PVOID +usb_alloc_mem( +POOL_TYPE pool_type, +LONG size +) +{ + PVOID ret; + g_alloc_cnt++; + ret = ExAllocatePool( pool_type, size ); + usb_dbg_print( DBGLVL_MAXIMUM, ( "usb_alloc_mem(): alloced=0x%x\n", g_alloc_cnt ) ); + return ret; +} + +VOID +usb_free_mem( +PVOID pbuf +) +{ + g_alloc_cnt--; + usb_dbg_print( DBGLVL_MAXIMUM, ( "usb_free_mem(): alloced=0x%x\n", g_alloc_cnt ) ); + ExFreePool( pbuf); +} + +VOID +usb_config_dev_completion( +PURB purb, +PVOID context +); + +LONG +usb_calc_bus_time( +LONG speed, +LONG input_dir, +LONG is_iso, +LONG byte_count +) +//shamelessly pasted from linux's usb.c +{ + LONG tmp; + + switch( speed & 0x3 ) /* no isoc. here */ + { + case USB_SPEED_LOW: + { + if (input_dir) + { + tmp = (67667L * (31L + 10L * bit_time (byte_count))) / 1000L; + return (64060L + (2 * BW_HUB_LS_SETUP) + BW_HOST_DELAY + tmp); + } + else + { + tmp = (66700L * (31L + 10L * bit_time (byte_count))) / 1000L; + return (64107L + (2 * BW_HUB_LS_SETUP) + BW_HOST_DELAY + tmp); + } + break; + } + /* for full-speed: */ + case USB_SPEED_FULL: + { + if (!is_iso) /* Input or Output */ + { + tmp = (8354L * (31L + 10L * bit_time (byte_count))) / 1000L; + return (9107L + BW_HOST_DELAY + tmp); + } /* end not Isoc */ + + /* for isoc: */ + + tmp = (8354L * (31L + 10L * bit_time (byte_count))) / 1000L; + return (((input_dir) ? 7268L : 6265L) + BW_HOST_DELAY + tmp); + } + case USB_SPEED_HIGH: + { + if( !is_iso ) + { + tmp = ( 999 + 926520 + 2083 * ( ( LONG )( ( 19 + 7 * 8 * byte_count ) / 6 ) ) ) / 1000; + } + else + { + tmp = ( 999 + 633232 + 2083 * ( ( LONG )( ( 19 + 7 * 8 * byte_count ) / 6 ) ) ) / 1000; + } + return tmp + USB2_HOST_DELAY; + } + default: + { + break; + } + } + return 125001; +} + +NTSTATUS +usb_query_and_lock_dev( +PUSB_DEV_MANAGER dev_mgr, +DEV_HANDLE dev_handle, +PUSB_DEV* ppdev +) +// +// if the dev is not in the list, return value is not success and the pointer is nulled +// if the dev is in the list but zomb, return value is error code and the pointer is the dev( no ref_count guarded ) +// if the dev is alive and in the list, return is success and the pointer is the dev. +// one must be aware of what his doing before he uses the ppdev +// +{ + int i; + PLIST_ENTRY pthis, pnext; + PUSB_DEV pdev; + BOOL valid_dev; + + USE_IRQL; + + *ppdev = NULL; + + if( dev_mgr == NULL || dev_handle == 0 ) + return STATUS_INVALID_PARAMETER; + + i = dev_id_from_handle( dev_handle ); + + KeAcquireSpinLock( &dev_mgr->dev_list_lock, &old_irql ); + ListFirst( &dev_mgr->dev_list, pthis ); + + while( pthis ) + { + pdev = ( PUSB_DEV ) pthis; + if( pdev->dev_id != ( ULONG )i ) + { + ListNext( &dev_mgr->dev_list, pthis, pnext ); + pthis = pnext; + continue; + } + else + break; + } + if( pthis == NULL ) + { + //no such device + KeReleaseSpinLock( &dev_mgr->dev_list_lock, old_irql ); + return STATUS_INVALID_PARAMETER; + } + + valid_dev = TRUE; + + lock_dev( pdev, TRUE ); + + if( dev_state( pdev ) == USB_DEV_STATE_ZOMB ) + { + valid_dev = FALSE; + } + else + pdev->ref_count ++; //guard the dev by increasing the ref count + + unlock_dev( pdev, TRUE ); + + KeReleaseSpinLock( &dev_mgr->dev_list_lock, old_irql ); + + *ppdev = pdev; + + if( !valid_dev ) + return STATUS_DEVICE_DOES_NOT_EXIST; + + return STATUS_SUCCESS; + +} + +NTSTATUS +usb_unlock_dev( +PUSB_DEV dev +) +{ + USE_IRQL; + + if( dev == NULL ) + return STATUS_INVALID_PARAMETER; + + lock_dev( dev, FALSE ); + dev->ref_count --; + if( dev->ref_count < 0 ) + dev->ref_count = 0; + unlock_dev( dev, FALSE ); + return STATUS_SUCCESS; +} + +NTSTATUS +usb_reset_pipe_ex( +PUSB_DEV_MANAGER dev_mgr, +DEV_HANDLE endp_handle, //endp handle to reset +PURBCOMPLETION reset_completion, //note: this reset completion has no right to delete the urb, that is only for reference +PVOID param +) +{ + NTSTATUS status; + PUSB_DEV pdev; + LONG if_idx, endp_idx; + PUSB_ENDPOINT pendp; + USE_IRQL; + + if( dev_mgr == NULL ) + return STATUS_INVALID_PARAMETER; + + status = usb_query_and_lock_dev( dev_mgr, ( endp_handle & 0xffff0000 ), &pdev ); + if( status != STATUS_SUCCESS ) + return STATUS_UNSUCCESSFUL; + + lock_dev( pdev, FALSE ); + if( dev_state( pdev ) == USB_DEV_STATE_ZOMB ) + { + status = STATUS_UNSUCCESSFUL; + goto LBL_OUT; + } + + if_idx = if_idx_from_handle( endp_handle ); + endp_idx = endp_idx_from_handle( endp_handle ); + + if( default_endp_handle( endp_handle ) ) + { + status = STATUS_UNSUCCESSFUL; + goto LBL_OUT; + } + + if( dev_state( pdev ) < USB_DEV_STATE_CONFIGURED ) + { + status = STATUS_DEVICE_NOT_READY; + goto LBL_OUT; + } + + pendp = &pdev->usb_config->interf[ if_idx ].endp[ endp_idx ]; + unlock_dev( pdev, FALSE ) + status = usb_reset_pipe( pdev, pendp, reset_completion, param ); + usb_unlock_dev( pdev ); + return status; + +LBL_OUT: + unlock_dev( pdev, FALSE ); + usb_unlock_dev( pdev ); + + return status; +} + +NTSTATUS +usb_reset_pipe( +PUSB_DEV pdev, +PUSB_ENDPOINT pendp, +PURBCOMPLETION client_reset_pipe_completion, +PVOID param +) +// caller must guarantee the pdev exist before the routine exit +{ + + PHCD hcd; + PURB purb; + BYTE endp_addr; + NTSTATUS status; + DEV_HANDLE dev_handle; + + USE_IRQL; + + if( pdev == NULL || pendp == NULL ) + return STATUS_INVALID_PARAMETER; + + lock_dev( pdev, FALSE ); + if( dev_state( pdev ) == USB_DEV_STATE_ZOMB ) + { + unlock_dev( pdev, FALSE ); + return STATUS_DEVICE_DOES_NOT_EXIST; + } + + hcd = pdev->hcd; + endp_addr = pendp->pusb_endp_desc->bEndpointAddress; + dev_handle = usb_make_handle( pdev->dev_id, 0, 0 ); + unlock_dev( pdev, FALSE ); + + purb = ( PURB )usb_alloc_mem( NonPagedPool, sizeof( URB ) + sizeof( PIRP ) ); + + if( purb == NULL ) + return STATUS_NO_MEMORY; + + UsbBuildResetPipeRequest( purb, + dev_handle, + endp_addr, + usb_reset_pipe_completion, + pendp, + ( LONG )client_reset_pipe_completion ); + + *( ( PULONG )&purb[ 1 ] ) = ( ULONG )param; + + if( ( status = hcd->hcd_submit_urb( hcd, pdev, &pdev->default_endp, purb ) ) != STATUS_PENDING ) + { + usb_free_mem( purb ); + purb = NULL; + } + return status; +} + +VOID +usb_reset_pipe_completion( +PURB purb, +PVOID context +) +{ + PUSB_DEV pdev; + PUSB_ENDPOINT pendp; + + USE_IRQL; + + if( purb == NULL || context == NULL ) + return; + + pdev = purb->pdev; + pendp = ( PUSB_ENDPOINT )context; + + lock_dev( pdev, TRUE ); + if( dev_state( pdev ) == USB_DEV_STATE_ZOMB ) + { + goto LBL_OUT; + } + + if( usb_error( purb->status ) ) + { + goto LBL_OUT; + } + //clear stall + pendp->flags &= ~USB_ENDP_FLAG_STAT_MASK; + + //reset toggle endp_type + if( ( pendp->pusb_endp_desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK ) == USB_ENDPOINT_XFER_BULK || \ + ( pendp->pusb_endp_desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK ) == USB_ENDPOINT_XFER_INT ) + { + pendp->flags &= ~USB_ENDP_FLAG_DATATOGGLE; + } + +LBL_OUT: + unlock_dev( pdev, TRUE ); + + if( purb->reference ) + ( ( PURBCOMPLETION )purb->reference )( purb, ( PVOID )( *( ( PULONG )&purb[ 1 ] ) ) ); + + usb_free_mem( purb ); + purb = NULL; + return; + +} + +void +usb_reset_pipe_from_dispatch_completion( +PURB purb, +PVOID param +) +{ + + PURB pclient_urb; + if( purb == NULL || param == NULL ) + TRAP(); + pclient_urb = ( PURB )param; + pclient_urb->status = purb->status; + + if( pclient_urb->completion ) + { + pclient_urb->completion( pclient_urb, pclient_urb->context ); + } + // the urb can not be freed here because it is owned by the reset + // pipe completion + return; +} + +BOOL +is_header_match( +PBYTE pbuf, +ULONG type +) //used to check descriptor validity +{ + BOOL ret; + PUSB_DESC_HEADER phdr; + phdr = ( PUSB_DESC_HEADER )pbuf; + + switch( type ) + { + case USB_DT_DEVICE: + { + ret = ( phdr->bLength == sizeof( USB_DEVICE_DESC ) + && phdr->bDescriptorType == USB_DT_DEVICE ); + break; + } + case USB_DT_CONFIG: + { + ret = ( phdr->bLength == sizeof( USB_CONFIGURATION_DESC ) + && phdr->bDescriptorType == USB_DT_CONFIG ); + break; + } + case USB_DT_INTERFACE: + { + ret = ( phdr->bLength == sizeof( USB_INTERFACE_DESC ) + && phdr->bDescriptorType == USB_DT_INTERFACE ); + break; + } + case USB_DT_ENDPOINT: + { + ret = ( phdr->bLength == sizeof( USB_ENDPOINT_DESC ) + && phdr->bDescriptorType == USB_DT_ENDPOINT ); + break; + } + default: + ret = FALSE; + } + return ret; +} + +BOOL +usb_skip_endp_desc( +PBYTE* pbUF, +LONG n +) +{ + if( is_header_match( *pbUF, USB_DT_ENDPOINT ) ) + { + ( *pbUF ) += sizeof( USB_ENDPOINT_DESC ) * n ; + return TRUE; + } + return FALSE; +} + +BOOL +usb_skip_if_desc( +PBYTE* pBUF +) +{ + BOOL ret; + PUSB_INTERFACE_DESC pif_desc = ( PUSB_INTERFACE_DESC )*pBUF; + LONG endp_count; + ret = is_header_match( ( PBYTE )*pBUF, USB_DT_INTERFACE ); + if( ret == TRUE ) + { + endp_count = pif_desc->bNumEndpoints; + if( endp_count < MAX_ENDPS_PER_IF ) + { + pif_desc ++; + ret = usb_skip_endp_desc( ( PBYTE* )&pif_desc, endp_count ); + if( ret ) + *( pBUF ) = ( PBYTE )pif_desc; + } + else + ret = FALSE; + } + return ret; +} + +BOOL +usb_skip_if_and_altif( +PUCHAR* pdesc_BUF +) +{ + BOOL ret; + PUSB_INTERFACE_DESC pif_desc1 = ( PUSB_INTERFACE_DESC )*pdesc_BUF; + ret = is_header_match( *pdesc_BUF, USB_DT_INTERFACE ); + if( ret == TRUE ) + { + if( pif_desc1->bAlternateSetting == 0 ) + ret = usb_skip_if_desc( ( PUCHAR* )&pif_desc1 ); + else + //no default interface + ret = FALSE; + + while( ret && pif_desc1->bAlternateSetting != 0 ) + ret = usb_skip_if_desc( ( PUCHAR* )&pif_desc1 ); + } + if( ret ) + *pdesc_BUF = ( PUCHAR )pif_desc1; + + return ret; +} +BOOL +usb_skip_one_config( +PUCHAR* pconfig_desc_BUF +) +{ + LONG if_count, i; + BOOL ret; + PUSB_CONFIGURATION_DESC pcfg_DESC = ( PUSB_CONFIGURATION_DESC )*pconfig_desc_BUF; + PUSB_INTERFACE_DESC pif_desc2 = ( PUSB_INTERFACE_DESC )&pcfg_DESC[ 1 ]; + + ret = is_header_match( ( PUCHAR )pcfg_DESC, USB_DT_CONFIG ); + if( ret ) + *pconfig_desc_BUF = &( ( BYTE* )pcfg_DESC )[ pcfg_DESC->wTotalLength ]; + return ret; + + ret = is_header_match( ( PUCHAR )pcfg_DESC, USB_DT_CONFIG ) + && is_header_match( ( PUCHAR ) pif_desc2, USB_DT_INTERFACE ); + + if( ret ) + { + if_count = pcfg_DESC->bNumInterfaces; + if( if_count < MAX_INTERFACES_PER_CONFIG ) + { + for( i = 0; i < if_count; i++ ) + { + ret = usb_skip_if_and_altif( ( PUCHAR* ) &pif_desc2 ); + if( ret == FALSE ) + break; + } + if( ret ) + *pconfig_desc_BUF = ( PUCHAR )pif_desc2; + } + } + return ret; +} + +PUSB_CONFIGURATION_DESC +usb_find_config_desc_by_idx( +PUCHAR pbuf, +LONG idx, +LONG cfg_count +) +{ + LONG i; + BOOL ret; + PUSB_CONFIGURATION_DESC pcfg_desc = ( PUSB_CONFIGURATION_DESC )pbuf; + if( pcfg_desc == NULL ) + return NULL; + + if( cfg_count > MAX_CONFIGS_PER_DEV ) + return NULL; + + if( idx > cfg_count ) + return NULL; + + if( idx == 0 ) + return pcfg_desc; + + for( i = 0; i < idx - 1; i++ ) + { + ret = usb_skip_one_config( ( PBYTE* )&pcfg_desc ); + if( ret == FALSE ) + return NULL; + } + return pcfg_desc; +} + +PUSB_CONFIGURATION_DESC +usb_find_config_desc_by_val( +PBYTE pbuf, +LONG val, +LONG cfg_count +) +{ + LONG i; + BOOL ret; + PUSB_CONFIGURATION_DESC pcfg_desc = ( PUSB_CONFIGURATION_DESC )pbuf; + if( pcfg_desc == NULL ) + return NULL; + + if( cfg_count > MAX_CONFIGS_PER_DEV ) + return NULL; + + for( i = 0; i < cfg_count; i++ ) + { + if( pcfg_desc->bConfigurationValue == val ) + return pcfg_desc; + + ret = usb_skip_one_config( ( PBYTE* )&pcfg_desc ); + if( ret == FALSE ) + return NULL; + } + + return NULL; +} + +#define if_from_handle( handle ) ( ( handle & 0xff00 ) >> 8 ) + +NTSTATUS +usb_submit_config_urb( +PURB purb +) +{ + PUSB_DEV pdev; + PUSB_DEV_MANAGER dev_mgr; + PUSB_ENDPOINT pendp; + PURB purb1; + PUSB_CTRL_SETUP_PACKET psetup; + NTSTATUS status; + PHCD hcd; + + USE_IRQL; + + if( purb == NULL ) + return STATUS_INVALID_PARAMETER; + + pdev = purb->pdev; + pendp = purb->pendp; + + lock_dev( pdev, FALSE ); + + dev_mgr = dev_mgr_from_dev( pdev ); + hcd = pdev->hcd; + + if( dev_state( pdev ) == USB_DEV_STATE_ZOMB ) + { + status = STATUS_DEVICE_DOES_NOT_EXIST; + goto LBL_OUT; + } + + if( dev_state( pdev ) == USB_DEV_STATE_FIRST_CONFIG + || dev_state( pdev ) == USB_DEV_STATE_RECONFIG ) + { + //outstanding request of set configuration exists in process + status = STATUS_UNSUCCESSFUL; + goto LBL_OUT; + } + + psetup = ( PUSB_CTRL_SETUP_PACKET )purb->setup_packet; + + if( dev_state( pdev ) == USB_DEV_STATE_CONFIGURED + && pdev->usb_config->pusb_config_desc->bConfigurationValue == ( BYTE )psetup->wValue ) + { + //already the current config + status = STATUS_SUCCESS; + goto LBL_OUT; + } + + + if( dev_state( pdev ) == USB_DEV_STATE_CONFIGURED ) + { + // not support re-configuration yet + status = STATUS_NOT_SUPPORTED; + goto LBL_OUT; + } + + psetup = ( PUSB_CTRL_SETUP_PACKET )purb->setup_packet; + purb1 = usb_alloc_mem( NonPagedPool, sizeof( URB ) ); + if( purb1 == NULL ) + { + status = STATUS_NO_MEMORY; + goto LBL_OUT; + } + + UsbBuildSelectConfigurationRequest( purb1, + usb_make_handle( pdev->dev_id, 0, 0 ) | 0xffff, + psetup->wValue, + usb_config_dev_completion, + 0, + ( ( ULONG )purb ) ); + purb1->pdev = pdev; + purb1->pendp = pendp; + + //change the dev state + pdev->flags &= ~USB_DEV_STATE_MASK; + pdev->flags |= USB_DEV_STATE_FIRST_CONFIG; + + unlock_dev( pdev, FALSE ); + + status = hcd->hcd_submit_urb( hcd, pdev, pendp, purb1 ); + if( status != STATUS_PENDING ) + { + usb_free_mem( purb1 ); + purb1 = NULL; + } + return status; + + LBL_OUT: + unlock_dev( pdev, FALSE ); + return status; +} + + +NTSTATUS +usb_submit_urb( +PUSB_DEV_MANAGER dev_mgr, +PURB purb +) +{ + NTSTATUS status; + PUSB_DEV pdev, parent_dev; + LONG if_idx, endp_idx; + DEV_HANDLE endp_handle, parent_dev_handle; + PUSB_CTRL_SETUP_PACKET psetup; + PUSB_ENDPOINT pendp; + + PHCD hcd; + USE_IRQL; + + if( purb == NULL || dev_mgr == NULL) + return STATUS_INVALID_PARAMETER; + + endp_handle = purb->endp_handle; + + if( endp_handle == 0 ) + return STATUS_INVALID_PARAMETER; + + status = usb_query_and_lock_dev( dev_mgr, endp_handle, &pdev ); + if( status != STATUS_SUCCESS ) + { + return status; + } + + if_idx = if_idx_from_handle( endp_handle ); + endp_idx = endp_idx_from_handle( endp_handle ); + + lock_dev( pdev, FALSE ); + if( dev_state( pdev ) == USB_DEV_STATE_ZOMB ) + { + unlock_dev( pdev, FALSE ); + status = STATUS_DEVICE_DOES_NOT_EXIST; + goto LBL_OUT; + } + + if( dev_state( pdev ) < USB_DEV_STATE_ADDRESSED ) + { + unlock_dev( pdev, FALSE ); + status = STATUS_DEVICE_NOT_READY; + goto LBL_OUT; + } + + dev_mgr = dev_mgr_from_dev( pdev ); + hcd = pdev->hcd; + + if( default_endp_handle( endp_handle ) ) + { + //default endp + pendp = &pdev->default_endp; + } + else if( if_idx >= MAX_INTERFACES_PER_CONFIG || endp_idx >= MAX_ENDPS_PER_IF ) + { + status = STATUS_INVALID_PARAMETER; + unlock_dev( pdev, FALSE ); + goto LBL_OUT; + } + else + { + if( dev_state( pdev ) < USB_DEV_STATE_CONFIGURED ) + { + status = STATUS_DEVICE_NOT_READY; + unlock_dev( pdev, FALSE ); + goto LBL_OUT; + } + pendp = &pdev->usb_config->interf[ if_idx ].endp[ endp_idx ]; + + } + + purb->pdev = pdev; + purb->pendp = pendp; + + //for default endpoint we have some special process + if( default_endp_handle( endp_handle ) ) + { + psetup = ( PUSB_CTRL_SETUP_PACKET ) purb->setup_packet; + if( psetup->bmRequestType == 0 + && psetup->bRequest == USB_REQ_SET_CONFIGURATION ) + { + unlock_dev( pdev, FALSE ); + status = usb_submit_config_urb( purb ); + goto LBL_OUT; + } + else if( psetup->bmRequestType == 1 + && psetup->bRequest == USB_REQ_SET_INTERFACE ) + { + unlock_dev( pdev, FALSE ); + // status = STATUS_NOT_SUPPORTED; + status = usb_set_interface( purb ); + goto LBL_OUT; + } + else if( psetup->bmRequestType == 0x80 + && psetup->bRequest == USB_REQ_GET_DESCRIPTOR ) + { + if( ( psetup->wValue >> 8 ) == USB_DT_CONFIG + || ( psetup->wValue >> 8 ) == USB_DT_DEVICE ) + { + unlock_dev( pdev, FALSE ); + status = usb_get_descriptor( pdev, purb ); + goto LBL_OUT; + + //get the descriptor directly + //status = hcd->hcd_submit_urb( hcd, pdev, purb->pendp, purb ); + //goto LBL_OUT; + } + } + else if( psetup->bmRequestType == 0x02 + && psetup->bRequest == USB_REQ_CLEAR_FEATURE + && psetup->wValue == 0 ) //reset pipe + { + ULONG endp_addr, endp_handle_to_reset; + BOOL found; + endp_addr = psetup->wIndex; + if( ( endp_addr & 0xf ) == 0 ) + { + unlock_dev( pdev, FALSE ); + status = STATUS_INVALID_PARAMETER; + goto LBL_OUT; + } + + // search for the endp by the endp addr in the wIndex + found = FALSE; + for( if_idx = 0; if_idx < pdev->usb_config->if_count; if_idx++ ) + { + for( endp_idx =0; endp_idx < pdev->usb_config->interf[ if_idx ].endp_count; endp_idx++ ) + { + pendp = &pdev->usb_config->interf[ if_idx ].endp[ endp_idx ]; + if( pendp->pusb_endp_desc->bEndpointAddress == endp_addr ) + { + found = TRUE; + break; + } + } + if( found == TRUE ) + break; + } + if( found ) + endp_handle = usb_make_handle( pdev->dev_id, if_idx, endp_idx ); + else + { + unlock_dev( pdev, FALSE ); + status = STATUS_INVALID_PARAMETER; + goto LBL_OUT; + } + unlock_dev( pdev, FALSE ); + status = usb_reset_pipe_ex( + dev_mgr, + endp_handle, + usb_reset_pipe_from_dispatch_completion, + purb ); + + goto LBL_OUT; + } + } + + unlock_dev( pdev, FALSE ); + status = hcd->hcd_submit_urb( hcd, pdev, purb->pendp, purb ); + + LBL_OUT: + usb_unlock_dev( pdev ); + return status; +} + + +void +usb_config_dev_completion( +PURB purb, +PVOID context +) +{ + PURB puser_urb; + PUSB_DEV pdev; + PUSB_ENDPOINT pendp; + PUSB_CTRL_SETUP_PACKET psetup; + ULONG config_val; + NTSTATUS status; + + USE_IRQL; + + if( purb == NULL ) + { + return; + } + pdev = purb->pdev; + pendp = purb->pendp; + + if( pdev == NULL ) + return; + + if( purb->reference != 0 ) + puser_urb = ( PURB ) purb->reference; + else + puser_urb = NULL; + + lock_dev( pdev, TRUE ); + + if( puser_urb ) + puser_urb->status = purb->status; + + if( purb->status != STATUS_SUCCESS ) + { + if( dev_state( pdev ) == USB_DEV_STATE_ZOMB ) + { + goto LBL_OUT; + } + + if( dev_state( pdev ) == USB_DEV_STATE_FIRST_CONFIG ) + { + pdev->flags &= ~USB_DEV_STATE_MASK; + pdev->flags |= USB_DEV_STATE_ADDRESSED; + } + else if( dev_state( pdev ) == USB_DEV_STATE_RECONFIG ) + { + pdev->flags &= ~USB_DEV_STATE_MASK; + pdev->flags |= USB_DEV_STATE_CONFIGURED; + + } + goto LBL_OUT; + } + // now let's construct usb_config + if( !pdev->usb_config ) + { + psetup = ( PUSB_CTRL_SETUP_PACKET )purb->setup_packet; + config_val = psetup->wValue; + status = dev_mgr_build_usb_config( pdev, + &pdev->desc_buf[ sizeof( USB_DEVICE_DESC ) ], + config_val, + pdev->pusb_dev_desc->bNumConfigurations ); + if( status != STATUS_SUCCESS ) + { + pdev->flags &= ~USB_DEV_STATE_MASK; + pdev->flags |= USB_DEV_STATE_ADDRESSED; + goto LBL_OUT; + } + pdev->flags &= ~USB_DEV_STATE_MASK; + pdev->flags |= USB_DEV_STATE_CONFIGURED; + //this usb dev represents physical dev + if( pdev->pusb_dev_desc->bDeviceClass == USB_CLASS_HUB + && pdev->pusb_dev_desc->bDeviceSubClass == 0 ) + { + pdev->flags &= ~USB_DEV_CLASS_MASK; + pdev->flags |= USB_DEV_CLASS_HUB; + } + else if( pdev->pusb_dev_desc->bDeviceClass == USB_CLASS_MASS_STORAGE + && pdev->pusb_dev_desc->bDeviceSubClass == 0 ) + { + pdev->flags &= ~USB_DEV_CLASS_MASK; + pdev->flags |= USB_DEV_CLASS_MASSSTOR; + } + else + { + pdev->flags &= ~USB_DEV_CLASS_MASK; + pdev->flags |= USB_DEV_CLASS_SCANNER; + } + } + else + { + //not supported + puser_urb->status = STATUS_NOT_SUPPORTED; + pdev->flags &= ~USB_DEV_STATE_MASK; + pdev->flags |= USB_DEV_STATE_CONFIGURED; + } + LBL_OUT: + unlock_dev( pdev, TRUE ); + usb_free_mem( purb ); + if( puser_urb && puser_urb->completion ) + puser_urb->completion( puser_urb, puser_urb->context ); + + return; +} + +NTSTATUS +usb_get_descriptor( +PUSB_DEV pdev, +PURB purb +) +{ + PUSB_CTRL_SETUP_PACKET psetup; + LONG idx, size, count, i; + PBYTE buf; + PUSB_CONFIGURATION_DESC pcfg_desc1; + + USE_IRQL; + + if( pdev == NULL || purb == NULL ) + return STATUS_INVALID_PARAMETER; + + if( purb->data_buffer == NULL || purb->data_length == 0 ) + { + return purb->status = STATUS_INVALID_PARAMETER; + } + + lock_dev( pdev, FALSE ); + psetup = ( PUSB_CTRL_SETUP_PACKET )purb->setup_packet; + if( pdev->desc_buf == NULL ) + { + purb->status = STATUS_DEVICE_NOT_READY; + goto LBL_OUT; + } + + if( ( psetup->wValue >> 8 ) == USB_DT_CONFIG ) + { + idx = ( psetup->wValue & 0xff ); + + count = pdev->pusb_dev_desc->bNumConfigurations; + + if( idx >= count ) + { + purb->status = STATUS_INVALID_PARAMETER; + goto LBL_OUT; + } + buf = &pdev->desc_buf[ sizeof( USB_DEVICE_DESC ) ]; + pcfg_desc1 = usb_find_config_desc_by_idx( buf, idx, count ); + if( pcfg_desc1 == NULL ) + { + purb->status = STATUS_UNSUCCESSFUL; + goto LBL_OUT; + } + + size = pcfg_desc1->wTotalLength; + size = size > purb->data_length ? purb->data_length : size; + for( i = 0; i < size; i++ ) + { + purb->data_buffer[ i ] = ( ( PBYTE )pcfg_desc1 )[ i ]; + } + purb->status = STATUS_SUCCESS; + goto LBL_OUT; + + } + else if(( psetup->wValue >> 8 ) == USB_DT_DEVICE ) + { + size = purb->data_length > sizeof( USB_DEVICE_DESC ) + ? sizeof( USB_DEVICE_DESC ) + : purb->data_length; + + for( i = 0; i < size; i ++ ) + { + purb->data_buffer[ i ] = ( ( PBYTE )pdev->pusb_dev_desc )[ i ]; + } + purb->status = STATUS_SUCCESS; + } +LBL_OUT: + unlock_dev( pdev, FALSE ); + return purb->status; +} + +LONG usb_count_list( PLIST_HEAD list_head ) +{ + LONG count; + PLIST_ENTRY pthis, pnext; + + if( list_head == NULL ) + return 0; + + count = 0; + ListFirst( list_head, pthis ); + + while( pthis ) + { + ListNext( list_head, pthis, pnext ); + pthis = pnext; + count ++; + } + return count; +} + +__inline BOOL +usb_query_clicks( +PLARGE_INTEGER clicks +) +{ + BOOL ret_val; + //so we have to use intel's cpu??? + ret_val = FALSE; + __asm + { + push ebx; + push eax; + mov eax, 1; //read version + cpuid; + test edx, 0x10; //timer stamp + jz LBL_OUT; + // cpuid //serialization + rdtsc; + mov ebx, dword ptr [ clicks ]; + mov dword ptr [ ebx ], eax; + mov dword ptr [ ebx + 4 ], edx; + mov dword ptr [ ret_val ], TRUE; +LBL_OUT: + pop eax; + pop ebx; + } + return ret_val; +} + +VOID +usb_wait_ms_dpc( +ULONG ms +) +{ + LARGE_INTEGER start; + LARGE_INTEGER ticker; + LARGE_INTEGER freq; + ULONG interval; + ULONG expire_count; + + KeQueryPerformanceCounter( &freq ); + + expire_count = 2000000; + if( cpu_clock_freq ) + expire_count = ( cpu_clock_freq / 1000 ) * ms; + + if( usb_query_clicks( &start ) == FALSE ) + { + ticker.QuadPart = 0; + while( TRUE ) + { + KeQuerySystemTime( &ticker ); + interval = ticker.LowPart - start.LowPart; + if( interval >= ms * 10000 ) + break; + } + } + else + { + ticker.QuadPart = 0; + while( TRUE ) + { + usb_query_clicks( &ticker ); + interval = ticker.LowPart - start.LowPart; + if( interval >= expire_count ) + break; + } + } +} + + +VOID +usb_wait_us_dpc( +ULONG us +) +{ + LARGE_INTEGER start; + LARGE_INTEGER ticker; + LARGE_INTEGER freq; + ULONG interval; + ULONG expire_count; + + KeQueryPerformanceCounter( &freq ); + + expire_count = 2000000; + if( cpu_clock_freq ) + expire_count = ( cpu_clock_freq / 1000000 ) * us; + + if( usb_query_clicks( &start ) == FALSE ) + { + ticker.QuadPart = 0; + while( TRUE ) + { + KeQuerySystemTime( &ticker ); + interval = ticker.LowPart - start.LowPart; + if( interval >= us * 10 ) + break; + } + } + else + { + ticker.QuadPart = 0; + while( TRUE ) + { + usb_query_clicks( &ticker ); + interval = ticker.LowPart - start.LowPart; + if( interval >= expire_count ) + break; + } + } +} + +VOID +usb_cal_cpu_freq() +{ + LARGE_INTEGER tick1, tick2; + LARGE_INTEGER interval; + LONG i; + // interval.QuadPart = -40 * 1000 * 1000; + + if( cpu_clock_freq >= 100 * 1000 * 1000 ) // assume it is valid + return; + + if( usb_query_clicks( &tick1 ) ) + { + for( i = 0; i < 25; i++ ) + { + usb_query_clicks( &tick1 ); + KeStallExecutionProcessor( 40 * 1000 ); + usb_query_clicks( &tick2 ); + cpu_clock_freq += ( ULONG )( tick2.QuadPart - tick1.QuadPart ); + } + // cpu_clock_freq *= 1000; + usb_dbg_print( DBGLVL_MAXIMUM, ( "usb_cal_cpu_freq(): cpu frequency = %d Hz\n", cpu_clock_freq ) ); + } +} + +NTSTATUS +usb_set_interface( +PURB purb +) +{ + ULONG u; + PURB purb1; + PCTRL_REQ_STACK pstack; + PUSB_DEV pdev; + PUSB_CTRL_SETUP_PACKET psetup; + PUSB_ENDPOINT pendp; + NTSTATUS status; + + PHCD hcd; + USE_IRQL; + + purb1 = purb; + pdev = purb->pdev; + psetup = ( PUSB_CTRL_SETUP_PACKET )purb->setup_packet; + + lock_dev( pdev, FALSE ); + if( dev_state( pdev ) == USB_DEV_STATE_ZOMB ) + { + unlock_dev( pdev, FALSE ); + return STATUS_DEVICE_NOT_CONNECTED; + } + if( dev_state( pdev ) < USB_DEV_STATE_CONFIGURED ) + { + unlock_dev( pdev, FALSE ); + return STATUS_DEVICE_NOT_READY; + } + + hcd = pdev->hcd; + + if( psetup->wIndex >= pdev->usb_config->if_count ) + { + unlock_dev( pdev, FALSE ); + return STATUS_INVALID_PARAMETER; + } + if( psetup->wValue >= pdev->usb_config->interf[ psetup->wIndex ].altif_count + 1 ) + { + unlock_dev( pdev, FALSE ); + return STATUS_INVALID_PARAMETER; + } + if( pdev->usb_config->interf[ psetup->wIndex ].pusb_if_desc->bAlternateSetting == psetup->wValue ) + { + // already the current interface + unlock_dev( pdev, FALSE ); + return STATUS_SUCCESS; + } + // check to see if the endp is busy + for( u = 0; u < pdev->usb_config->interf[ psetup->wIndex ].endp_count; u++ ) + { + // This check is not adquate. Since we do not have mechanism to block the new coming + // request during this request. the caller must guarantee no active or pending + // usb request on these endpoint. + pendp = &pdev->usb_config->interf[ psetup->wIndex ].endp[ u ]; + if( usb_endp_busy_count( pendp ) ) + { + // active urb on that endp + unlock_dev( pdev, FALSE ); + return STATUS_DEVICE_NOT_READY; + } + if( IsListEmpty( &pendp->urb_list ) ) + { + // pending urb on that endp + unlock_dev( pdev, FALSE ); + return STATUS_DEVICE_NOT_READY; + } + } + unlock_dev( pdev, FALSE ); + + if( purb1->ctrl_req_context.ctrl_stack_count == 0 ) + { + // ok, we have one stack cell for our use + if( purb1->completion != NULL ) + { + purb1->ctrl_req_context.ctrl_stack_count = 1; + purb1->ctrl_req_context.ctrl_cur_stack = 0; + } + else + { + // use urb's completion and context + purb1->completion = usb_set_interface_completion; + purb1->context = pdev; + } + } + else + { + if( purb->ctrl_req_context.ctrl_cur_stack + 1 >= purb->ctrl_req_context.ctrl_stack_count ) + { + // stack full, let's allocate one new urb, we need stack size one + purb1 = usb_alloc_mem( NonPagedPool, sizeof( URB ) ); + if( purb1 == NULL ) + return STATUS_NO_MEMORY; + + RtlCopyMemory( purb1, purb, sizeof( URB ) ); + + // we do not use stack + RtlZeroMemory( purb1->ctrl_req_stack, sizeof( CTRL_REQ_STACK ) ); + purb1->context = pdev; + purb1->completion = usb_set_interface_completion; + purb1->ctrl_parent_urb = purb; + purb1->ctrl_req_context.ctrl_req_flags = CTRL_PARENT_URB_VALID; + + goto LBL_SEND_URB; + } + else + purb->ctrl_req_context.ctrl_cur_stack++; + } + + u = purb1->ctrl_req_context.ctrl_cur_stack; + RtlZeroMemory( &purb1->ctrl_req_stack[ u ], sizeof( CTRL_REQ_STACK ) ); + pstack = &purb1->ctrl_req_stack[ u ]; + pstack->context = pdev; + pstack->urb_completion = usb_set_interface_completion; + +LBL_SEND_URB: + if( hcd == NULL ) + return STATUS_INVALID_PARAMETER; + + status = hcd->hcd_submit_urb( hcd, purb->pdev, purb->pendp, purb ); + return status; +} + +#define usb_complete_and_free_ctrl_urb( pURB ) \ +{\ + UCHAR i, j;\ + i = pURB->ctrl_req_context.ctrl_cur_stack;\ + j = pURB->ctrl_req_context.ctrl_stack_count;\ + usb_call_ctrl_completion( pURB );\ + if( i == 0xff || j == 0 )\ + usb_free_mem( pURB );\ +} + +VOID +usb_set_interface_completion( +PURB purb, +PVOID context +) +{ + PUSB_CTRL_SETUP_PACKET psetup; + PUSB_INTERFACE pif, palt_if; + USB_INTERFACE temp_if; + UCHAR if_idx, if_alt_idx; + PUSB_DEV pdev; + PUSB_ENDPOINT pendp; + ULONG i; + PLIST_ENTRY pthis, pnext; + + USE_IRQL; + + if( purb == NULL ) + return; + + if( purb->status == STATUS_SUCCESS ) + { + psetup = ( PUSB_CTRL_SETUP_PACKET )purb->setup_packet; + if_idx = ( UCHAR )psetup->wIndex; + if_alt_idx = ( UCHAR )psetup->wValue; + pdev = purb->pdev; + RtlZeroMemory( &temp_if, sizeof( USB_INTERFACE ) ); + + lock_dev( pdev, TRUE ); + if( dev_state( pdev ) == USB_DEV_STATE_ZOMB ) + { + unlock_dev( pdev, TRUE ); + purb->status = STATUS_DEVICE_NOT_CONNECTED; + purb->data_length = 0; + } + else + { + // let's swap the interface + pif = &pdev->usb_config->interf[ if_idx ]; + ListFirst( &pif->altif_list, pthis ); + pnext = pthis; + do + { + palt_if = struct_ptr( pthis, USB_INTERFACE, altif_list ); + if( palt_if->pusb_if_desc->bAlternateSetting == if_alt_idx ) + { + break; + } + palt_if = NULL; + ListNext( &pif->altif_list, pthis, pnext ); + pthis = pnext; + + }while( pthis ); + + if( palt_if != NULL ) + { + RtlCopyMemory( &temp_if, palt_if, sizeof( USB_INTERFACE ) ); + + palt_if->endp_count = pif->endp_count; + RtlCopyMemory( palt_if->endp, pif->endp, sizeof( pif->endp ) ); + palt_if->pif_drv = pif->pif_drv; + palt_if->pusb_if_desc = pif->pusb_if_desc; + for( i = 0; i < palt_if->endp_count; i++ ) + { + pendp = &palt_if->endp[ i ]; + InitializeListHead( &pendp->urb_list ); + pendp->flags = 0; + } + + RtlCopyMemory( pif->endp, temp_if.endp, sizeof( temp_if.endp ) ); + pif->endp_count = temp_if.endp_count; + pif->pusb_if_desc = temp_if.pusb_if_desc; + for( i = 0; i < pif->endp_count; i++ ) + { + pendp = &pif->endp[ i ]; + InitializeListHead( &pendp->urb_list ); + pendp->flags = 0; + } + } + else + { + TRAP(); + purb->status = STATUS_UNSUCCESSFUL; + } + } + unlock_dev( pdev, TRUE ); + } + + // for recursive reason, we have to store the parameter ahead + usb_complete_and_free_ctrl_urb( purb ); +} + +VOID +usb_call_ctrl_completion( +PURB purb +) +// can only be called when current completion finished and called only in +// urb completion. And this func may be called recursively, if this routine +// is called, the urb must be treated as released. +{ + PURB parent_urb; + PCTRL_REQ_STACK pstack; + ULONG i; + + + if( purb == NULL ) + return; + + if( purb->ctrl_req_context.ctrl_stack_count != 0 ) + { + i = purb->ctrl_req_context.ctrl_cur_stack; + if( i > 0 && i < 0x10 ) + { + i --; + purb->ctrl_req_context.ctrl_cur_stack = ( UCHAR )i; + pstack = &purb->ctrl_req_stack[ i ]; + if( pstack->urb_completion ) + { + pstack->urb_completion( purb, pstack->context ); + } + else + TRAP(); + } + else if( i == 0 ) + { + i = purb->ctrl_req_context.ctrl_cur_stack = 0xff; + if( purb->completion ) + { + purb->completion( purb, purb->context ); + } + else + TRAP(); + } + else if( i == 0xff ) + { + // only parent urb's completion, if parent urb exists, can be called + if( purb->ctrl_req_context.ctrl_req_flags & CTRL_PARENT_URB_VALID ) + { + parent_urb = purb->ctrl_parent_urb; + if( parent_urb ) + { + pstack = &parent_urb->ctrl_req_stack[ parent_urb->ctrl_req_context.ctrl_cur_stack ]; + pstack->urb_completion( parent_urb, pstack->context ); + } + else + TRAP(); + } + } + else + TRAP(); + } + else if( purb->ctrl_req_context.ctrl_req_flags & CTRL_PARENT_URB_VALID ) + { + // this is the case when the child urb won't use the stack + parent_urb = purb->ctrl_parent_urb; + if( parent_urb ) + { + // pstack = &parent_urb->ctrl_req_stack[ parent_urb->ctrl_req_context.ctrl_cur_stack ]; + // pstack->urb_completion( parent_urb, pstack->context ); + usb_call_ctrl_completion( parent_urb ); + } + else + TRAP(); + } + else + return; +} + diff --git a/reactos/drivers/usb/nt4compat/usbdriver/usb.h b/reactos/drivers/usb/nt4compat/usbdriver/usb.h new file mode 100644 index 00000000000..5fd7c55d0ba --- /dev/null +++ b/reactos/drivers/usb/nt4compat/usbdriver/usb.h @@ -0,0 +1,1062 @@ +#ifndef __USBD_H__ +#define __USBD_H__ +#include "ntddk.h" +/* + * Some USB bandwidth allocation constants. + */ + +#ifndef BOOL +#define BOOL ULONG +#endif + +#define USB2_HOST_DELAY 5 /* nsec, guess */ +#define BW_HOST_DELAY 1000L /* nanoseconds */ +#define BW_HUB_LS_SETUP 333L /* nanoseconds */ + /* 4 full-speed bit times (est.) */ + +#define FRAME_TIME_BITS 12000L /* frame = 1 millisecond */ +#define FRAME_TIME_MAX_BITS_ALLOC (90L * FRAME_TIME_BITS / 100L) +#define FRAME_TIME_USECS 1000L +#define FRAME_TIME_MAX_USECS_ALLOC (90L * FRAME_TIME_USECS / 100L) + +#define bit_time(bytecount) (7 * 8 * bytecount / 6) /* with integer truncation */ + /* Trying not to use worst-case bit-stuffing + of (7/6 * 8 * bytecount) = 9.33 * bytecount */ + /* bytecount = data payload byte count */ + +#define ns_to_us(ns) ((ns + 500L) / 1000L) + /* convert & round nanoseconds to microseconds */ + +#define usb_make_handle( dev_Id, if_iDx, endp_iDx) \ +( ( DEV_HANDLE )( ( ( ( ( ULONG )dev_Id ) << 16 ) | ( ( ( ULONG )if_iDx ) << 8 ) ) | ( ( ULONG ) endp_iDx ) ) ) + +#define usb_make_ref( poinTER ) \ +( poinTER ^ 0xffffffff ) + +#define ptr_from_ref uhci_make_ref + +#define dev_id_from_handle( hanDLE ) ( ( ( ULONG ) ( hanDLE ) ) >> 16 ) +#define if_idx_from_handle( hanDLE ) ( ( ( ( ULONG ) ( hanDLE ) ) << 16 ) >> 24 ) +#define endp_idx_from_handle( hanDLE ) ( ( ( ULONG ) ( hanDLE ) ) & 0xff ) + +#define endp_from_handle( pDEV, hanDLE, peNDP ) \ +{\ + LONG if_idx, endp_idx;\ + BOOL def_endp; \ + endp_idx = endp_idx_from_handle( hanDLE );\ + if_idx = if_idx_from_handle( hanDLE );\ + def_endp = ( ( hanDLE & 0xffff ) == 0xffff ); \ + if( def_endp ) \ + peNDP = &pdev->default_endp; \ + else \ + { \ + if( if_idx >= pdev->usb_config->if_count ) \ + peNDP = NULL; \ + else if( endp_idx >= pdev->usb_config->interf[ if_idx ].endp_count ) \ + peNDP = NULL; \ + else \ + peNDP = &( pDEV )->usb_config->interf[ if_idx ].endp[ endp_idx ]; \ + } \ +} + +#define endp_type( enDP ) \ +( ( enDP->flags & USB_ENDP_FLAG_DEFAULT_ENDP ) \ + ? USB_ENDPOINT_XFER_CONTROL\ + : ( ( enDP )->pusb_endp_desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK ) ) + + +//init work data for urb +#define urb_init( uRb ) \ +{\ + RtlZeroMemory( ( uRb ), sizeof( URB ) ); \ + InitializeListHead( &( uRb )->trasac_list ); \ +} + +#define UsbBuildInterruptOrBulkTransferRequest(uRb, \ + endp_hanDle, \ + data_Buf, \ + data_sIze, \ + completIon, \ + contExt, \ + refereNce ) \ +{ \ + urb_init( ( uRb ) );\ + ( uRb )->endp_handle = endp_hanDle;\ + ( uRb )->data_buffer = data_Buf;\ + ( uRb )->data_length = data_sIze;\ + ( uRb )->completion = completIon;\ + ( uRb )->context = contExt; \ + ( uRb )->reference = refereNce; \ +} + + + +#define UsbBuildGetDescriptorRequest(uRb, \ + endp_hAndle, \ + descriPtorType, \ + descriPtorIndex, \ + languaGeId, \ + data_bUffer, \ + data_sIze, \ + compleTion, \ + contexT, \ + refereNce ) \ +{ \ + PUSB_CTRL_SETUP_PACKET pseTup;\ + pseTup = ( PUSB_CTRL_SETUP_PACKET )( uRb )->setup_packet;\ + urb_init( ( uRb ) );\ + ( uRb )->endp_handle = ( endp_hAndle );\ + ( uRb )->data_length = ( data_sIze ); \ + ( uRb )->data_buffer = ( ( PUCHAR )data_bUffer ); \ + ( uRb )->completion = ( compleTion );\ + ( uRb )->context = ( ( PVOID )contexT ); \ + ( uRb )->reference = ( ULONG )refereNce; \ + pseTup->wValue = ( ( descriPtorType ) << 8 )| ( descriPtorIndex ); \ + pseTup->wLength = ( data_sIze ); \ + pseTup->wIndex = ( languaGeId );\ + pseTup->bRequest = USB_REQ_GET_DESCRIPTOR;\ + pseTup->bmRequestType = 0x80;\ +} + + + +#define UsbBuildGetStatusRequest(uRb, \ + endp_hanDle, \ + recipiEnt, \ + inDex, \ + transferBufFer, \ + completIon, \ + contExt, \ + refereNce ) \ +{ \ + PUSB_CTRL_SETUP_PACKET pseTup = ( PUSB_CTRL_SETUP_PACKET )( uRb )->setup_packet;\ + urb_init( ( uRb ) );\ + ( uRb )->endp_handle = ( endp_hanDle ); \ + ( uRb )->data_buffer = ( transferBufFer ); \ + ( uRb )->data_length = sizeof(USHORT); \ + ( uRb )->compleiton = ( completIon );\ + ( uRb )->context = ( contExt );\ + ( uRb )->reference = ( refereNce );\ + pseTup->bmRequestType = ( 0x80 | recipiEnt );\ + pseTup->bRequest = USB_REQ_GET_STATUS;\ + pseTup->wIndex = ( index ); \ + pseTup->wValue = 0;\ + pseTup->wLength = sizeof( USHORT );\ +} + + +#define UsbBuildFeatureRequest(uRb, \ + endp_hanDle,\ + recipiEnt, \ + featureSelecTor, \ + inDex, \ + completIon, \ + contExt, \ + refereNce ) \ + { \ + PUSB_CTRL_SETUP_PACKET pseTup = ( PUSB_CTRL_SETUP_PACKET )( uRb )->setup_packet;\ + urb_init( ( uRb ) );\ + ( uRb )->endp_handle = ( endp_hanDle ); \ + ( uRb )->data_buffer = NULL;\ + ( uRb )->data_length = ( 0 );\ + ( uRb )->completion = ( completIon );\ + ( uRb )->context = ( contExt ); \ + ( uRb )->reference = ( refereNce ); \ + pseTup->bmRequestType = recipiEnt; \ + pseTup->bRequest = USB_REQ_SET_FEATURE;\ + pseTup->wValue = ( featureSelecTor );\ + pseTup->wIndex = ( inDex );\ + pseTup->wLength = 0;\ +} + +#define UsbBuildSelectConfigurationRequest(uRb, \ + endp_hanDle,\ + config_Val,\ + completIon, \ + contExt, \ + refereNce ) \ + { \ + PUSB_CTRL_SETUP_PACKET pseTup = ( PUSB_CTRL_SETUP_PACKET )( uRb )->setup_packet;\ + urb_init( ( uRb ) );\ + ( uRb )->endp_handle = ( endp_hanDle ); \ + ( uRb )->data_buffer = NULL;\ + ( uRb )->data_length = 0;\ + ( uRb )->completion = ( completIon );\ + ( uRb )->context = ( contExt ); \ + ( uRb )->reference = ( refereNce ); \ + pseTup->bmRequestType = 0;\ + pseTup->bRequest = USB_REQ_SET_CONFIGURATION;\ + pseTup->wValue = ( config_Val );\ + pseTup->wIndex = 0;\ + pseTup->wLength = 0;\ +} + +#define UsbBuildSelectInterfaceRequest(uRb, \ + endp_hanDle,\ + if_Num, \ + alt_Num,\ + completIon, \ + contExt, \ + refereNce ) \ + { \ + PUSB_CTRL_SETUP_PACKET pseTup = ( PUSB_CTRL_SETUP_PACKET )( uRb )->setup_packet;\ + urb_init( ( uRb ) );\ + ( uRb )->endp_handle = ( endp_hanDle ); \ + ( uRb )->data_buffer = NULL;\ + ( uRb )->data_length = 0;\ + ( uRb )->completion = ( completIon );\ + ( uRb )->context = ( contExt ); \ + ( uRb )->reference = ( refereNce ); \ + pseTup->bmRequestType = 1;\ + pseTup->bRequest = USB_REQ_SET_INERFACE;\ + pseTup->wValue = ( alt_Num );\ + pseTup->wIndex = ( if_Num );\ + pseTup->wLength = 0;\ +} + + +#define UsbBuildVendorRequest(uRb, \ + endp_hanDle,\ + data_bufFer, \ + data_sIze, \ + request_tYpe, \ + requEst, \ + vaLue, \ + inDex, \ + completIon, \ + contExt, \ + refereNce ) \ + { \ + PUSB_CTRL_SETUP_PACKET pseTup = ( PUSB_CTRL_SETUP_PACKET )( uRb )->setup_packet;\ + urb_init( ( uRb ) );\ + ( uRb )->endp_handle = ( endp_hanDle ); \ + ( uRb )->data_buffer = data_bufFer;\ + ( uRb )->data_length = data_sIze;\ + ( uRb )->completion = ( completIon );\ + ( uRb )->context = ( contExt ); \ + ( uRb )->reference = ( refereNce ); \ + pseTup->bmRequestType = request_tYpe;\ + pseTup->bRequest = requEst;\ + pseTup->wValue = vaLue;\ + pseTup->wIndex = inDex;\ + pseTup->wLength = ( USHORT )data_sIze;\ +} + +#define UsbBuildResetPipeRequest(uRb, \ + dev_hanDle, \ + endp_aDdr, \ + completIon, \ + contExt, \ + refereNce ) \ +{\ + PUSB_CTRL_SETUP_PACKET pseTup = ( PUSB_CTRL_SETUP_PACKET )( uRb )->setup_packet;\ + urb_init( ( uRb ) );\ + ( uRb )->endp_handle = ( dev_hanDle | 0xffff ); \ + ( uRb )->completion = ( completIon );\ + ( uRb )->context = ( contExt ); \ + ( uRb )->reference = ( refereNce ); \ + pseTup->bmRequestType = 0x02;\ + pseTup->bRequest = USB_REQ_CLEAR_FEATURE;\ + pseTup->wIndex = endp_aDdr;\ +} + + + +/* USB constants */ + +#define USB_SPEED_FULL 0x00 +#define USB_SPEED_LOW 0x01 +#define USB_SPEED_HIGH 0x02 + +/* + * Device and/or Interface Class codes + */ +#define USB_CLASS_PER_INTERFACE 0 /* for DeviceClass */ +#define USB_CLASS_AUDIO 1 +#define USB_CLASS_COMM 2 +#define USB_CLASS_HID 3 +#define USB_CLASS_PHYSICAL 5 +#define USB_CLASS_PRINTER 7 +#define USB_CLASS_MASS_STORAGE 8 +#define USB_CLASS_HUB 9 +#define USB_CLASS_DATA 10 +#define USB_CLASS_APP_SPEC 0xfe +#define USB_CLASS_VENDOR_SPEC 0xff + +/* + * USB types + */ +#define USB_TYPE_MASK (0x03 << 5) +#define USB_TYPE_STANDARD (0x00 << 5) +#define USB_TYPE_CLASS (0x01 << 5) +#define USB_TYPE_VENDOR (0x02 << 5) +#define USB_TYPE_RESERVED (0x03 << 5) + +/* + * USB recipients + */ +#define USB_RECIP_MASK 0x1f +#define USB_RECIP_DEVICE 0x00 +#define USB_RECIP_INTERFACE 0x01 +#define USB_RECIP_ENDPOINT 0x02 +#define USB_RECIP_OTHER 0x03 + +/* + * USB directions + */ +#define USB_DIR_OUT 0 +#define USB_DIR_IN 0x80 + +/* + * Descriptor types + */ +#define USB_DT_DEVICE 0x01 +#define USB_DT_CONFIG 0x02 +#define USB_DT_STRING 0x03 +#define USB_DT_INTERFACE 0x04 +#define USB_DT_ENDPOINT 0x05 + +#define USB_DT_HID (USB_TYPE_CLASS | 0x01) +#define USB_DT_REPORT (USB_TYPE_CLASS | 0x02) +#define USB_DT_PHYSICAL (USB_TYPE_CLASS | 0x03) +#define USB_DT_HUB (USB_TYPE_CLASS | 0x09) + +/* + * Descriptor sizes per descriptor type + */ +#define USB_DT_DEVICE_SIZE 18 +#define USB_DT_CONFIG_SIZE 9 +#define USB_DT_INTERFACE_SIZE 9 +#define USB_DT_ENDPOINT_SIZE 7 +#define USB_DT_ENDPOINT_AUDIO_SIZE 9 /* Audio extension */ +#define USB_DT_HUB_NONVAR_SIZE 7 +#define USB_DT_HID_SIZE 9 + +/* + * Endpoints + */ +#define USB_ENDPOINT_NUMBER_MASK 0x0f /* in bEndpointAddress */ +#define USB_ENDPOINT_DIR_MASK 0x80 + +#define USB_ENDPOINT_XFERTYPE_MASK 0x03 /* in bmAttributes */ +#define USB_ENDPOINT_XFER_CONTROL 0 +#define USB_ENDPOINT_XFER_ISOC 1 +#define USB_ENDPOINT_XFER_BULK 2 +#define USB_ENDPOINT_XFER_INT 3 + +/* + * USB Packet IDs (PIDs) + */ +#define USB_PID_UNDEF_0 0xf0 +#define USB_PID_OUT 0xe1 +#define USB_PID_ACK 0xd2 +#define USB_PID_DATA0 0xc3 +#define USB_PID_PING 0xb4 /* USB 2.0 */ +#define USB_PID_SOF 0xa5 +#define USB_PID_NYET 0x96 /* USB 2.0 */ +#define USB_PID_DATA2 0x87 /* USB 2.0 */ +#define USB_PID_SPLIT 0x78 /* USB 2.0 */ +#define USB_PID_IN 0x69 +#define USB_PID_NAK 0x5a +#define USB_PID_DATA1 0x4b +#define USB_PID_PREAMBLE 0x3c /* Token mode */ +#define USB_PID_ERR 0x3c /* USB 2.0: handshake mode */ +#define USB_PID_SETUP 0x2d +#define USB_PID_STALL 0x1e +#define USB_PID_MDATA 0x0f /* USB 2.0 */ + +/* + * Standard requests + */ +#define USB_REQ_GET_STATUS 0x00 +#define USB_REQ_CLEAR_FEATURE 0x01 +#define USB_REQ_SET_FEATURE 0x03 +#define USB_REQ_SET_ADDRESS 0x05 +#define USB_REQ_GET_DESCRIPTOR 0x06 +#define USB_REQ_SET_DESCRIPTOR 0x07 +#define USB_REQ_GET_CONFIGURATION 0x08 +#define USB_REQ_SET_CONFIGURATION 0x09 +#define USB_REQ_GET_INTERFACE 0x0A +#define USB_REQ_SET_INTERFACE 0x0B +#define USB_REQ_SYNCH_FRAME 0x0C + +/* + * HID requests + */ +#define USB_REQ_GET_REPORT 0x01 +#define USB_REQ_GET_IDLE 0x02 +#define USB_REQ_GET_PROTOCOL 0x03 +#define USB_REQ_SET_REPORT 0x09 +#define USB_REQ_SET_IDLE 0x0A +#define USB_REQ_SET_PROTOCOL 0x0B + +// HUB request +#define HUB_REQ_GET_STATE 0x02 + +// usb2.0 hub +#define HUB_REQ_CLEAR_TT_BUFFER 0x08 + + +typedef LONG USBD_STATUS; + +// +// USBD status codes +// +// Status values are 32 bit values layed out as follows: +// +// 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 +// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 +// +---+---------------------------+-------------------------------+ +// | S | Status Code | +// +---+---------------------------+-------------------------------+ +// +// where +// +// S - is the state code +// +// 00 - completed with success +// 01 - request is pending +// 10 - completed with error, endpoint not stalled +// 11 - completed with error, endpoint stalled +// +// +// Code - is the status code +// + +// +// Generic test for success on any status value (non-negative numbers +// indicate success). +// + +#define usb_success(Status) ((USBD_STATUS)(Status) >= 0) + +// +// Generic test for pending status value. +// + +#define usb_pending(Status) ((ULONG)(Status) >> 30 == 1) + +// +// Generic test for error on any status value. +// + +#define usb_error(Status) ((USBD_STATUS)(Status) < 0) + +// +// Generic test for stall on any status value. +// + +#define usb_halted(Status) ((ULONG)(Status) >> 30 == 3) + +// +// Macro to check the status code only +// + +#define usb_status(Status) ((ULONG)(Status) & 0x0FFFFFFFL) + + +#define USB_STATUS_SUCCESS ((USBD_STATUS)0x00000000L) +#define USB_STATUS_PENDING ((USBD_STATUS)0x40000000L) +#define USB_STATUS_HALTED ((USBD_STATUS)0xC0000000L) +#define USB_STATUS_ERROR ((USBD_STATUS)0x80000000L) + +// +// HC status codes +// Note: these status codes have both the error and the stall bit set. +// +#define USB_STATUS_CRC ((USBD_STATUS)0xC0000401L) +#define USB_STATUS_BTSTUFF ((USBD_STATUS)0xC0000402L) +#define USB_STATUS_DATA_TOGGLE_MISMATCH ((USBD_STATUS)0xC0000403L) +#define USB_STATUS_STALL_PID ((USBD_STATUS)0xC0000404L) +#define USB_STATUS_DEV_NOT_RESPONDING ((USBD_STATUS)0xC0000405L) +#define USB_STATUS_PID_CHECK_FAILURE ((USBD_STATUS)0xC0000406L) +#define USB_STATUS_UNEXPECTED_PID ((USBD_STATUS)0xC0000407L) +#define USB_STATUS_DATA_OVERRUN ((USBD_STATUS)0xC0000408L) +#define USB_STATUS_DATA_UNDERRUN ((USBD_STATUS)0xC0000409L) +#define USB_STATUS_RESERVED1 ((USBD_STATUS)0xC000040AL) +#define USB_STATUS_RESERVED2 ((USBD_STATUS)0xC000040BL) +#define USB_STATUS_BUFFER_OVERRUN ((USBD_STATUS)0xC000040CL) +#define USB_STATUS_BUFFER_UNDERRUN ((USBD_STATUS)0xC000040DL) +#define USB_STATUS_NOT_ACCESSED ((USBD_STATUS)0xC000040FL) +#define USB_STATUS_FIFO ((USBD_STATUS)0xC0000410L) +#define USB_STATUS_BABBLE_DETECTED ((USBD_STATUS)0xC0000408L) + +// +// returned by HCD if a transfer is submitted to an endpoint that is +// stalled +// +#define USB_STATUS_ENDPOINT_HALTED ((USBD_STATUS)0xC0000430L) + +// +// Software status codes +// Note: the following status codes have only the error bit set +// +#define USB_STATUS_NO_MEMORY ((USBD_STATUS)0x80000100L) +#define USB_STATUS_INVALID_URB_FUNCTION ((USBD_STATUS)0x80000200L) +#define USB_STATUS_INVALID_PARAMETER ((USBD_STATUS)0x80000300L) + +// +// returned if client driver attempts to close an endpoint/interface +// or configuration with outstanding transfers. +// +#define USB_STATUS_ERROR_BUSY ((USBD_STATUS)0x80000400L) +// +// returned by USBD if it cannot complete a URB request, typically this +// will be returned in the URB status field when the Irp is completed +// with a more specific NT error code in the irp.status field. +// +#define USB_STATUS_REQUEST_FAILED ((USBD_STATUS)0x80000500L) + +#define USB_STATUS_INVALID_PIPE_HANDLE ((USBD_STATUS)0x80000600L) + +// returned when there is not enough bandwidth avialable +// to open a requested endpoint +#define USB_STATUS_NO_BANDWIDTH ((USBD_STATUS)0x80000700L) +// +// generic HC error +// +#define USB_STATUS_INTERNAL_HC_ERROR ((USBD_STATUS)0x80000800L) +// +// returned when a short packet terminates the transfer +// ie USBD_SHORT_TRANSFER_OK bit not set +// +#define USB_STATUS_ERROR_SHORT_TRANSFER ((USBD_STATUS)0x80000900L) +// +// returned if the requested start frame is not within +// USBD_ISO_START_FRAME_RANGE of the current USB frame, +// note that the stall bit is set +// +#define USB_STATUS_BAD_START_FRAME ((USBD_STATUS)0xC0000A00L) +// +// returned by HCD if all packets in an iso transfer complete with an error +// +#define USB_STATUS_ISOCH_REQUEST_FAILED ((USBD_STATUS)0xC0000B00L) +// +// returned by USBD if the frame length control for a given +// HC is already taken by anothe driver +// +#define USB_STATUS_FRAME_CONTROL_OWNED ((USBD_STATUS)0xC0000C00L) +// +// returned by USBD if the caller does not own frame length control and +// attempts to release or modify the HC frame length +// +#define USB_STATUS_FRAME_CONTROL_NOT_OWNED ((USBD_STATUS)0xC0000D00L) + +// +// set when a transfers is completed due to an AbortPipe request from +// the client driver +// +// Note: no error or stall bit is set for these status codes +// +#define USB_STATUS_CANCELED ((USBD_STATUS)0x00010000L) + +#define USB_STATUS_CANCELING ((USBD_STATUS)0x00020000L) + +// Device type -- in the "User Defined" range." +#define FILE_HCD_DEV_TYPE 45000 +#define FILE_UHCI_DEV_TYPE ( FILE_HCD_DEV_TYPE + 1 ) +#define FILE_OHCI_DEV_TYPE ( FILE_HCD_DEV_TYPE + 2 ) +#define FILE_EHCI_DEV_TYPE ( FILE_HCD_DEV_TYPE + 3 ) +#define FILE_USB_DEV_TYPE ( FILE_HCD_DEV_TYPE + 8 ) + +#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( unsigned 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( unsigned 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 ) +// if the major_function is IRP_MJ_DEVICE_CONTROL +// 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 +// if the major_function is IRP_MJ_INTERNAL_DEVICE_CONTROL +// input_buffer is a URB, and input_buffer_length is equal to or greater than +// sizeof( URB ); +// only the following urb fields can be accessed, others must be zeroed. +// DEV_HANDLE endp_handle; +// UCHAR setup_packet[8]; // for control pipe, or zeroed +// PUCHAR data_buffer; // buffer for READ/WRITE +// ULONG data_length; // buffer size in bytes + +#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 +// there is no difference between IRP_MJ_DEVICE_CONTROL and IRP_MJ_INTERNAL_DEVICE_CONTROL +#define IOCTL_GET_DEV_HANDLE CTL_CODE( FILE_HCD_DEV_TYPE, 4099, METHOD_BUFFERED, FILE_ANY_ACCESS ) +// input_buffer is null ,and input_buffer_length is zero. +// output_buffer will hold the handle to this dev, output_buffer_length is 4 +// or bigger + +typedef ULONG DEV_HANDLE, ENDP_HANDLE, IF_HANDLE; + +struct URB; +#pragma pack( push, usb_align, 1 ) + +//structures for DeviceIoControl +typedef struct _ENUM_DEV_ELEMENT +{ + DEV_HANDLE dev_handle; + USHORT product_id; + USHORT vendor_id; + UCHAR dev_addr; + +} ENUM_DEV_ELEMENT, *PENUM_DEV_ELEMENT; + +typedef struct _ENUM_DEV_ARRAY +{ + UCHAR dev_count; + ENUM_DEV_ELEMENT dev_arr[ 1 ]; + +} ENUM_DEV_ARRAY, *PENUM_DEV_ARRAY; + +typedef struct _GET_DEV_DESC_REQ +{ + DEV_HANDLE dev_handle; + UCHAR desc_type; + UCHAR desc_idx; + +} GET_DEV_DESC_REQ, *PGET_DEV_DESC_REQ; + +//usb definitions +typedef struct _USB_CTRL_SETUP_PACKET +{ + UCHAR bmRequestType; + UCHAR bRequest; + USHORT wValue; + USHORT wIndex; + USHORT wLength; + +}USB_CTRL_SETUP_PACKET, *PUSB_CTRL_SETUP_PACKET; + +typedef struct _USB_STRING_DESCRIPTOR +{ + UCHAR bLength; + UCHAR bDescriptorType; + USHORT wData[1]; + +} USB_STRING_DESCRIPTOR, *PUSB_STRING_DESCRIPTOR; + +typedef struct _USB_DESC_HEADER +{ + UCHAR bLength; + UCHAR bDescriptorType; + +} USB_DESC_HEADER, *PUSB_DESC_HEADER; + +typedef struct _USB_ENDPOINT_DESC +{ + UCHAR bLength; + UCHAR bDescriptorType; + UCHAR bEndpointAddress; + UCHAR bmAttributes; + USHORT wMaxPacketSize; + UCHAR bInterval; + +} USB_ENDPOINT_DESC, *PUSB_ENDPOINT_DESC; + +typedef struct _USB_INTERFACE_DESC +{ + UCHAR bLength; + UCHAR bDescriptorType; + UCHAR bInterfaceNumber; + UCHAR bAlternateSetting; + UCHAR bNumEndpoints; + UCHAR bInterfaceClass; + UCHAR bInterfaceSubClass; + UCHAR bInterfaceProtocol; + UCHAR iInterface; + +} USB_INTERFACE_DESC, *PUSB_INTERFACE_DESC; + +typedef struct _USB_CONFIGURATION_DESC +{ + UCHAR bLength; + UCHAR bDescriptorType; + USHORT wTotalLength; + UCHAR bNumInterfaces; + UCHAR bConfigurationValue; + UCHAR iConfiguration; + UCHAR bmAttributes; + UCHAR MaxPower; + +} USB_CONFIGURATION_DESC, *PUSB_CONFIGURATION_DESC; + +typedef struct _USB_DEVICE_DESC +{ + UCHAR bLength; + UCHAR bDescriptorType; + USHORT bcdUSB; + UCHAR bDeviceClass; + UCHAR bDeviceSubClass; + UCHAR bDeviceProtocol; + UCHAR bMaxPacketSize0; + USHORT idVendor; + USHORT idProduct; + USHORT bcdDevice; + UCHAR iManufacturer; + UCHAR iProduct; + UCHAR iSerialNumber; + UCHAR bNumConfigurations; + +} USB_DEVICE_DESC, *PUSB_DEVICE_DESC; + + +#define URB_FLAG_STATE_MASK 0x0f +#define URB_FLAG_STATE_PENDING 0x00 +#define URB_FLAG_STATE_IN_PROCESS 0x01 +#define URB_FLAG_STATE_FINISHED 0x02 + +// USB2.0 state +#define URB_FLAG_STATE_DOORBELL 0x03 // for async request removal +#define URB_FLAG_STATE_WAIT_FRAME 0x04 // for sync request removal( for cancel only ) +#define URB_FLAG_STATE_ERROR 0x05 + +#define URB_FLAG_IN_SCHEDULE 0x10 +#define URB_FLAG_FORCE_CANCEL 0x20 +#define URB_FLAG_SHORT_PACKET 0x80000000 + +typedef struct _SPLIT_ISO_BUS_TIME +{ + USHORT bus_time; + USHORT start_uframe; + +} SPLIT_ISO_BUS_TIME, *PSPLIT_ISO_BUS_TIME; + +typedef struct _ISO_PACKET_DESC +{ + LONG offset; + LONG length; // expected length + LONG actual_length; + LONG status; + union + { + LONG bus_time; //opaque for client request of split iso, the bus_time is the start_uframe for each transaction + SPLIT_ISO_BUS_TIME params; + }; + +} ISO_PACKET_DESC, *PISO_PACKET_DESC; + +#define CTRL_PARENT_URB_VALID 1 + +typedef void ( *PURBCOMPLETION )( struct _URB *purb, PVOID pcontext); + +typedef struct _CTRL_REQ_STACK +{ + PURBCOMPLETION urb_completion; // the last step of the urb_completion is to call the + // the prevoius stack's callback if has, and the last + // one is purb->completion + PVOID context; + ULONG params[ 3 ]; + +} CTRL_REQ_STACK, *PCTRL_REQ_STACK; + +#pragma pack( pop, usb_align ) + +#include "td.h" + +typedef struct _URB_HS_PIPE_CONTENT +{ + ULONG trans_type : 2; // bit 0-1 + ULONG mult_count : 2; // bit 2-3, used in high speed int and iso requests + ULONG reserved : 1; // bit 1 + ULONG speed_high : 1; // bit 5 + ULONG speed_low : 1; // bit 6 + ULONG trans_dir : 1; // bit 7 + ULONG dev_addr : 7; // bit 8-14 + ULONG endp_addr : 4; // bit 15-18 + ULONG data_toggle : 1; // bit 19 + ULONG max_packet_size : 4; // bit 20-23 log( max_packet_size ) + ULONG interval : 4; // bit 24-27 the same definition in USB2.0, for high or full/low speed + ULONG start_uframe : 3; // bit 28-30 + ULONG reserved1 : 1; // + +} URB_HS_PIPE_CONTENT, *PURB_HS_PIPE_CONTENT; + +typedef struct _URB_HS_CONTEXT_CONTENT +{ + ULONG hub_addr : 7; // high speed hub addr for split transfer + ULONG port_idx : 7; + ULONG reserved : 18; + +} URB_HS_CONTEXT_CONTENT, *PURB_HS_CONTEXT_CONTENT; + +typedef struct _URB +{ + LIST_ENTRY urb_link; + ULONG flags; + DEV_HANDLE endp_handle; + LONG status; + //record info for isr use, similar to td.status + //int pipe has different content in the 8 msb + //the eight bits contain interrupt interval. + //and max packet length is encoded in 3 bits from 23-21 + //that means 2^(x) bytes in the packet. + ULONG pipe; // bit0-1: endp type, bit 6: ls or fs. bit 7: dir + + union + { + UCHAR setup_packet[8]; // for control + LONG params[ 2 ]; // params[ 0 ] is used in iso transfer as max_packet_size + }; + + PUCHAR data_buffer; //user data + LONG data_length; //user data length + + struct _USB_DEV *pdev; + struct _USB_ENDPOINT *pendp; //pipe for current transfer + + PURBCOMPLETION completion; + PVOID context; //parameter of completion + + PVOID urb_ext; //for high speed hcd use + ULONG hs_context; //for high speed hcd use + + PIRP pirp; //irp from client driver + LONG reference; //for caller private use + + LONG td_count; //for any kinds of transfer + LONG rest_bytes; //for split bulk transfer involving more than 1024 tds + LONG bytes_to_transfer; + LONG bytes_transfered; //( bulk transfer )accumulate one split-transfer by xfered bytes of the executed transactions + PLIST_ENTRY last_finished_td; //last inactive td useful for large amount transfer + LIST_ENTRY trasac_list; //list of tds or qhs + + union + { + LONG iso_start_frame; // for high speed endp, this is uframe index, and not used for full/low speed endp, instead, + // iso_packet_desc.param.start_uframe is used. + LONG int_start_frame; // frame( ms ) index for high/full/low speed endp + struct + { + UCHAR ctrl_req_flags; + UCHAR ctrl_stack_count; + UCHAR ctrl_cur_stack; // the receiver uses this by increment the stack pointer first. if the requester + UCHAR ctrl_reserved; // send it down with ctrl_stack_count zero, that means the stack is not initialized, + // and can be initialized by receiver to 1 and only 1. + // If the initializer found the stack size won't meet the number down the drivers, it must + // reallocate one urb with the required stack size. and store the previous urb in + // ctrl_parent_urb + } ctrl_req_context; + }; + + union + { + LONG iso_frame_count; // uframe for high speed and frame for full/low speed + struct _URB* ctrl_parent_urb; + }; + + union + { + ISO_PACKET_DESC iso_packet_desc[ 1 ]; //used to build up trasac_list for iso transfer and claim bandwidth + CTRL_REQ_STACK ctrl_req_stack[ 1 ]; + }; + +} URB, *PURB; + + +NTSTATUS +usb_set_dev_ext( +ULONG dev_ref, +PVOID dev_ext, +LONG size +); + +NTSTATUS +usb_set_if_ext( +ULONG dev_ref, +ULONG if_ref, +PVOID if_ext, +LONG size +); + +PVOID +usb_get_dev_ext( +ULONG dev_ref +); + +PVOID +usb_get_if_ext( +ULONG dev_ref, +ULONG if_ref +); + +NTSTATUS +usb_claim_interface( +ULONG dev_ref, +ULONG if_ref, +struct _USB_DRIVER *usb_drvr +); + +//return reference to the endp +ULONG +usb_get_endp( +ULONG dev_ref, +LONG endp_addr +); + +//return reference to the interface +ULONG +usb_get_interface( +ULONG dev_ref, +LONG if_idx +); + +NTSTATUS +usb_set_configuration( +ULONG dev_ref, +LONG config_value +); + +// each call will return full size of the config desc and +// its if, endp descs. +// return value is the bytes actually returned. +// if the return value is equal to wTotalLength, all the descs of the +// configuration returned. +NTSTATUS +usb_get_config_desc( +ULONG dev_ref, +LONG config_idx, +PCHAR buffer, +PLONG psize +); + +NTSTATUS +usb_submit_urb( +struct _USB_DEV_MANAGER* dev_mgr, +PURB purb +); + +NTSTATUS +usb_cancel_urb( +ULONG dev_ref, +ULONG endp_ref, +PURB purb +); + +LONG +usb_calc_bus_time( +LONG low_speed, +LONG input_dir, +LONG isoc, +LONG bytecount +); + +//increment the dev->ref_count to lock the dev +NTSTATUS +usb_query_and_lock_dev( +struct _USB_DEV_MANAGER* dev_mgr, +DEV_HANDLE dev_handle, +struct _USB_DEV** ppdev +); + +//decrement the dev->ref_count +NTSTATUS +usb_unlock_dev( +struct _USB_DEV *dev +); + +NTSTATUS +usb_reset_pipe( +struct _USB_DEV *pdev, +struct _USB_ENDPOINT *pendp, +PURBCOMPLETION client_completion, +PVOID param //parameter for client_completion +); + +VOID +usb_reset_pipe_completion( +PURB purb, +PVOID pcontext +); + +PVOID +usb_alloc_mem( +POOL_TYPE pool_type, +LONG size +); + +VOID +usb_free_mem( +PVOID pbuf +); + +PUSB_CONFIGURATION_DESC +usb_find_config_desc_by_val( +PUCHAR pbuf, +LONG val, +LONG cfg_count +); + +PUSB_CONFIGURATION_DESC +usb_find_config_desc_by_idx( +PUCHAR pbuf, +LONG idx, +LONG cfg_count +); + +ULONG +usb_skip_if_and_altif( +PUCHAR* pdesc_BUF +); + +ULONG +usb_skip_one_config( +PUCHAR* pconfig_desc_BUF +); + +VOID +usb_wait_ms_dpc( +ULONG ms +); + +VOID +usb_wait_us_dpc( +ULONG us +); + +ULONG +usb_query_clicks( +PLARGE_INTEGER clicks +); + +VOID +usb_cal_cpu_freq(); + +NTSTATUS +usb_reset_pipe_ex( +struct _USB_DEV_MANAGER *dev_mgr, +DEV_HANDLE endp_handle, +PURBCOMPLETION reset_completion, +PVOID param +); + +VOID +usb_call_ctrl_completion( +PURB purb +); + +BOOL +is_header_match( +PUCHAR pbuf, +ULONG type +); //used to check descriptor validity +#endif diff --git a/reactos/drivers/usb/nt4compat/usbdriver/usbdriver.rbuild b/reactos/drivers/usb/nt4compat/usbdriver/usbdriver.rbuild new file mode 100644 index 00000000000..6cd0cee3f8b --- /dev/null +++ b/reactos/drivers/usb/nt4compat/usbdriver/usbdriver.rbuild @@ -0,0 +1,19 @@ + + + . + ntoskrnl + hal + ehci.c + uhci.c + hub.c + td.c + usb.c + umss.c + bulkonly.c + cbi.c + dmgrdisp.c + compdrv.c + etd.c + gendrv.c + ehci.rc + diff --git a/reactos/drivers/usb/nt4compat/usbdriver/usbdriver.rc b/reactos/drivers/usb/nt4compat/usbdriver/usbdriver.rc new file mode 100644 index 00000000000..d5153dcf41d --- /dev/null +++ b/reactos/drivers/usb/nt4compat/usbdriver/usbdriver.rc @@ -0,0 +1,45 @@ +// Resource script for USBISO driver +// Generated by Walt Oney's driver wizard + +#include + +LANGUAGE LANG_ENGLISH, SUBLANG_NEUTRAL + + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 0,0,1,0 + PRODUCTVERSION 0,0,1,0 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "Comments", "This is a beta version of usb driver stack( ehci ), contact me at mypublic99@yahoo.com\0" + VALUE "CompanyName", "Woodhead Software\0" + VALUE "FileDescription", "ehci.sys\0" + VALUE "FileVersion", "0, 0, 1, 0\0" + VALUE "InternalName", "ehci.sys\0" + VALUE "LegalCopyright", "Copyright © 2002-2004 Woodhead Software\0" + VALUE "LegalTrademarks", "\0" + VALUE "OriginalFilename", "ehci.sys\0" + VALUE "PrivateBuild", "0.01\0" + VALUE "ProductName", "usb driver stack for windows NT\0" + VALUE "ProductVersion", "0, 0, 1, 0\0" + VALUE "SpecialBuild", "0131.d\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END +