Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1542 lines
45 KiB

/*++
Copyright (C) 1999- Microsoft Corporation
Module Name:
camusb.cpp
Abstract:
This module implements CUsbCamera object
Author:
William Hsieh (williamh) created
Revision History:
--*/
#include "ptppch.h"
#include <atlbase.h>
#include <atlconv.h>
#include <devioctl.h>
//
// Private IOCTL to workaround #446466 (Whistler)
//
#define IOCTL_SEND_USB_REQUEST_PTP CTL_CODE(FILE_DEVICE_USB_SCAN,IOCTL_INDEX+20,METHOD_BUFFERED,FILE_ANY_ACCESS)
//
// Constructor for CUsbCamera
//
CUsbCamera::CUsbCamera() :
m_hUSB(NULL),
m_hEventUSB(NULL),
m_hEventCancel(NULL),
m_hEventRead(NULL),
m_pUsbData(NULL),
m_UsbDataSize(0),
m_prevOpCode(0),
m_prevTranId(0)
{
DBG_FN("CUsbCamera::CUsbCamera");
memset(&m_EndpointInfo, NULL, sizeof(m_EndpointInfo));
}
CUsbCamera::~CUsbCamera()
{
}
//
// This function takes care of USB-specific processing for opening
// a connection with a device.
//
// Input:
// DevicePortName -- name used to access device via CreateFile
// pIPTPEventCB -- IWiaPTPEventCallback interface pointer
//
HRESULT
CUsbCamera::Open(
LPWSTR DevicePortName,
PTPEventCallback pPTPEventCB,
PTPDataCallback pPTPDataCB,
LPVOID pEventParam,
BOOL bEnableEvents
)
{
USES_CONVERSION;
DBG_FN("CUsbCamera::Open");
HRESULT hr = S_OK;
//
// Call the base class Open function first
//
hr = CPTPCamera::Open(DevicePortName, pPTPEventCB, pPTPDataCB, pEventParam, bEnableEvents);
if (FAILED(hr))
{
wiauDbgError("Open", "base class Open failed");
return hr;
}
//
// Open another handle to talk with the device, to work around possible
// bug in Usbscan.sys
//
m_hEventUSB = ::CreateFile(W2T(DevicePortName), // file name
GENERIC_READ | GENERIC_WRITE, // desired access
0, // sharing mode
NULL, // security descriptor
OPEN_EXISTING, // creation disposition
FILE_FLAG_OVERLAPPED, // file attributes
NULL // template file
);
if (m_hEventUSB == INVALID_HANDLE_VALUE)
{
hr = HRESULT_FROM_WIN32(::GetLastError());
wiauDbgErrorHr(hr, "Open", "CreateFile failed");
m_hUSB = NULL;
return hr;
}
//
// Open a handle to talk with the device
//
m_hUSB = ::CreateFile(W2T(DevicePortName), // file name
GENERIC_READ | GENERIC_WRITE, // desired access
0, // sharing mode
NULL, // security descriptor
OPEN_EXISTING, // creation disposition
0, // file attributes
NULL // template file
);
if (m_hUSB == INVALID_HANDLE_VALUE)
{
hr = HRESULT_FROM_WIN32(::GetLastError());
wiauDbgErrorHr(hr, "Open", "Second CreateFile failed");
m_hUSB = NULL;
return hr;
}
//
// Create event handle that will cancel interrupt pipe read
//
m_hEventCancel = CreateEvent(NULL, TRUE, FALSE, NULL);
if (!m_hEventCancel)
{
hr = HRESULT_FROM_WIN32(::GetLastError());
wiauDbgErrorHr(hr, "Open", "CreateEvent failed");
return hr;
}
//
// Create event handle for reading interrupt pipe
//
m_hEventRead = CreateEvent(NULL, TRUE, FALSE, NULL);
if (!m_hEventRead)
{
hr = HRESULT_FROM_WIN32(::GetLastError());
wiauDbgErrorHr(hr, "Open", "CreateEvent failed");
return hr;
}
//
// Set up array used by WaitForMultipleObjects
//
m_EventHandles[0] = m_hEventCancel;
m_EventHandles[1] = m_hEventRead;
//
// Get the pipe configuration information of each pipe
//
USBSCAN_PIPE_CONFIGURATION PipeCfg;
DWORD BytesReturned;
if (!DeviceIoControl(m_hUSB,
IOCTL_GET_PIPE_CONFIGURATION,
NULL,
0,
&PipeCfg,
sizeof(PipeCfg),
&BytesReturned,
NULL))
{
hr = HRESULT_FROM_WIN32(::GetLastError());
wiauDbgErrorHr(hr, "Open", "get pipe config DeviceIoControl failed");
return hr;
}
//
// Loop through the pipe configurations and store the information we'll need
// later (maximum packet size and address). Also make sure there is at least
// one endpoint of each: bulk-in, bulk-out, and interrupt.
//
USBSCAN_PIPE_INFORMATION *pPipeInfo; // Temporary pointer
for (ULONG count = 0; count < PipeCfg.NumberOfPipes; count++)
{
pPipeInfo = &PipeCfg.PipeInfo[count];
switch (pPipeInfo->PipeType)
{
case USBSCAN_PIPE_BULK:
if (pPipeInfo->EndpointAddress & BULKIN_FLAG)
{
m_EndpointInfo.BulkInMaxSize = pPipeInfo->MaximumPacketSize;
m_EndpointInfo.BulkInAddress = pPipeInfo->EndpointAddress;
wiauDbgTrace("Open", "found a bulk-in endpoint, address = 0x%04x, packet size = %d, index = %d",
pPipeInfo->EndpointAddress, pPipeInfo->MaximumPacketSize, count);
}
else
{
m_EndpointInfo.BulkOutMaxSize = pPipeInfo->MaximumPacketSize;
m_EndpointInfo.BulkOutAddress = pPipeInfo->EndpointAddress;
wiauDbgTrace("Open", "found a bulk-out endpoint, address = 0x%04x, packet size = %d, index = %d",
pPipeInfo->EndpointAddress, pPipeInfo->MaximumPacketSize, count);
}
break;
case USBSCAN_PIPE_INTERRUPT:
m_EndpointInfo.InterruptMaxSize = pPipeInfo->MaximumPacketSize;
m_EndpointInfo.InterruptAddress = pPipeInfo->EndpointAddress;
wiauDbgTrace("Open", "found an interrupt endpoint, address = 0x%02x, packet size = %d, index = %d",
pPipeInfo->EndpointAddress, pPipeInfo->MaximumPacketSize, count);
break;
default:
wiauDbgTrace("Open", "found an endpoint of unknown type, type = 0x%04x, address = 0x%02x, packet size = %d, index = %d",
pPipeInfo->PipeType, pPipeInfo->EndpointAddress, pPipeInfo->MaximumPacketSize, count);
}
}
//
// Each of these endpoints must be present and have non-zero packet size
//
if (!m_EndpointInfo.BulkInMaxSize ||
!m_EndpointInfo.BulkOutMaxSize ||
!m_EndpointInfo.InterruptMaxSize)
{
wiauDbgError("Open", "At least one endpoint is invalid");
return E_FAIL;
}
//
// Allocate a re-usable buffer for handling the USB header during reads
// and writes. It needs to be large enough to hold one packet and large
// enough to hold a USB header.
//
m_UsbDataSize = max(m_EndpointInfo.BulkInMaxSize, m_EndpointInfo.BulkOutMaxSize);
while (m_UsbDataSize < sizeof(m_pUsbData->Header))
{
m_UsbDataSize += m_UsbDataSize;
}
m_pUsbData = (PUSB_PTP_DATA) new BYTE[m_UsbDataSize];
if (!m_pUsbData)
{
wiauDbgError("Open", "memory allocation failed");
return E_OUTOFMEMORY;
}
return hr;
}
//
// This function closes the connection to the camera
//
HRESULT
CUsbCamera::Close()
{
DBG_FN("CUsbCamera::Close");
HRESULT hr = S_OK;
//
// Call the base class Close function first
//
hr = CPTPCamera::Close();
if (FAILED(hr))
{
wiauDbgError("Close", "base class Close failed");
}
//
// Signal event to cancel interrupt pipe I/O
//
if (!SetEvent(m_hEventCancel))
{
hr = HRESULT_FROM_WIN32(::GetLastError());
wiauDbgErrorHr(hr, "Close", "SetEvent failed");
} else {
if (m_bEventsEnabled)
{
//
// We need to wait until event thread finishes, otherwise driver DLL may get unloaded
// with a running thread in it
//
DWORD ret = WaitForSingleObject(m_hEventThread, INFINITE);
if (ret != WAIT_OBJECT_0) {
hr = HRESULT_FROM_WIN32(::GetLastError());
wiauDbgErrorHr(hr, "Close", "WaitForSingleObject failed");
}
}
}
//
// Close handle to the event thread
//
if (m_hEventThread)
{
CloseHandle(m_hEventThread);
m_hEventThread = NULL;
}
//
// Close the file handles and event handles
//
if (m_hUSB)
{
CloseHandle(m_hUSB);
m_hUSB = NULL;
}
if (m_hEventUSB)
{
CloseHandle(m_hEventUSB);
m_hEventUSB = NULL;
}
if (m_hEventCancel)
{
CloseHandle(m_hEventCancel);
m_hEventCancel = NULL;
}
if (m_hEventRead)
{
CloseHandle(m_hEventRead);
m_hEventRead = NULL;
}
//
// Free memory used for reading/writing data
//
if (m_pUsbData)
{
delete[] (BYTE*)m_pUsbData;
m_pUsbData = NULL;
}
return hr;
}
//
// This function writes a command buffer to the device
//
// Input:
// pCommand -- pointer to the command to send
// NumParams -- number of parameters in the command
//
HRESULT
CUsbCamera::SendCommand(
PTP_COMMAND *pCommand,
UINT NumParams
)
{
DBG_FN("CUsbCamera::SendCommand");
HRESULT hr = S_OK;
if (!pCommand || NumParams > COMMAND_NUMPARAMS_MAX)
{
wiauDbgError("SendCommand", "invalid arg");
return E_INVALIDARG;
}
//
// Check for the reset command, and send it via the control pipe instead
//
if (pCommand->OpCode == PTP_OPCODE_RESETDEVICE)
{
wiauDbgTrace("SendCommand", "sending reset request");
hr = ResetDevice();
if (FAILED(hr))
{
wiauDbgError("SendCommand", "ResetDevice failed");
return hr;
}
}
else
{
//
// Put the PTP command into a USB container
//
m_UsbCommand.Header.Len = sizeof(m_UsbCommand.Header) + sizeof(DWORD) * NumParams;
m_UsbCommand.Header.Type = PTPCONTAINER_TYPE_COMMAND;
m_UsbCommand.Header.Code = pCommand->OpCode;
m_UsbCommand.Header.TransactionId = pCommand->TransactionId;
if (NumParams > 0)
{
memcpy(m_UsbCommand.Params, pCommand->Params, sizeof(DWORD) * NumParams);
}
//
// Send the command to the device
//
DWORD BytesWritten = 0;
wiauDbgTrace("SendCommand", "writing command");
if (!WriteFile(m_hUSB, &m_UsbCommand, m_UsbCommand.Header.Len, &BytesWritten, NULL))
{
hr = HRESULT_FROM_WIN32(::GetLastError());
wiauDbgErrorHr(hr, "SendCommand", "WriteFile failed");
return hr;
}
if (BytesWritten != m_UsbCommand.Header.Len)
{
wiauDbgError("SendCommand", "wrong amount of data written = %d", BytesWritten);
return E_FAIL;
}
//
// If the amount written is a multiple of the packet size, send a null packet
//
if (m_UsbCommand.Header.Len % m_EndpointInfo.BulkOutMaxSize == 0)
{
wiauDbgTrace("SendCommand", "sending null packet");
if (!WriteFile(m_hUSB, NULL, 0, &BytesWritten, NULL))
{
hr = HRESULT_FROM_WIN32(::GetLastError());
wiauDbgErrorHr(hr, "SendCommand", "second WriteFile failed");
return hr;
}
if (BytesWritten != 0)
{
wiauDbgError("SendCommand", "wrong amount of data written = %d -", BytesWritten);
return E_FAIL;
}
}
}
//
// Save the opcode, because we need it for the data container header
//
m_prevOpCode = pCommand->OpCode;
m_prevTranId = pCommand->TransactionId;
wiauDbgTrace("SendCommand", "command successfully sent");
return hr;
}
//
// This function reads bulk data from the device
//
// Input:
// pData -- pointer to a buffer to receive read data
// BufferSize -- size of buffer
//
HRESULT
CUsbCamera::ReadData(
BYTE *pData,
UINT *pBufferSize
)
{
DBG_FN("CUsbCamera::ReadData");
HRESULT hr = S_OK;
BOOL bAbortTransfer = FALSE;
if (!pData ||
!pBufferSize ||
*pBufferSize == 0)
{
wiauDbgError("ReadData", "invalid arg");
return E_INVALIDARG;
}
//
// First read the header from the device
//
memset(m_pUsbData, NULL, m_UsbDataSize);
DWORD BytesRead = 0;
wiauDbgTrace("ReadData", "reading data header");
if (!ReadFile(m_hUSB, m_pUsbData, sizeof(m_pUsbData->Header), &BytesRead, NULL))
{
hr = HRESULT_FROM_WIN32(::GetLastError());
wiauDbgErrorHr(hr, "ReadData", "ReadFile failed");
return hr;
}
if (BytesRead != sizeof(m_pUsbData->Header))
{
wiauDbgError("ReadData", "wrong amount of data read = %d", BytesRead);
return E_FAIL;
}
//
// Check the type code in the header to make sure it's correct
//
if (m_pUsbData->Header.Type != PTPCONTAINER_TYPE_DATA)
{
wiauDbgError("ReadData", "expected a data header but received type = %d", m_pUsbData->Header.Type);
return E_FAIL;
}
//
// Check the opcode and transaction id in the header just to make sure they are correct
//
if ((m_pUsbData->Header.Code != m_prevOpCode) ||
(m_pUsbData->Header.TransactionId != m_prevTranId))
{
wiauDbgError("ReadData", "fields in the data header were incorrect, opcode=0x%04x tranid=0x%08x",
m_pUsbData->Header.Code, m_pUsbData->Header.TransactionId);
return E_FAIL;
}
//
// Loop, reading the data. The callback function will be called at least 10 times during
// the transfer. More if the buffer size is small.
//
LONG lOffset = 0;
UINT BytesToRead = 0;
UINT TotalRead = 0;
UINT TotalToRead = m_pUsbData->Header.Len - sizeof(m_pUsbData->Header);
UINT TotalRemaining = TotalToRead;
//
// Make sure the buffer is large enough, unless a callback function is being used
//
if (m_pDataCallbackParam == NULL &&
*pBufferSize < TotalToRead)
{
wiauDbgError("ReadData", "buffer is too small");
return E_FAIL;
}
//
// When doing callbacks, read the data in chunk sizes slightly
// larger the 1/10 the total and divisible by 4.
//
if (m_pDataCallbackParam)
BytesToRead = (TotalToRead / 40 + 1) * 4;
else
BytesToRead = *pBufferSize;
//
// Set time out values for Usbscan
//
USBSCAN_TIMEOUT TimeOut;
DWORD BytesReturned = 0;
TimeOut.TimeoutRead = PTP_READ_TIMEOUT + max(BytesToRead / 100000, 114);
TimeOut.TimeoutWrite = PTP_WRITE_TIMEOUT;
TimeOut.TimeoutEvent = PTP_EVENT_TIMEOUT;
if (!DeviceIoControl(m_hUSB,
IOCTL_SET_TIMEOUT,
&TimeOut,
sizeof(TimeOut),
NULL,
0,
&BytesReturned,
NULL))
{
hr = HRESULT_FROM_WIN32(::GetLastError());
wiauDbgErrorHr(hr, "Open", "set timeout DeviceIoControl failed");
return hr;
}
while (TotalRemaining > 0)
{
//
// Make sure the amount to read is never larger than the buffer size. The buffer size may
// be updated by the callback function.
//
if (BytesToRead > *pBufferSize)
BytesToRead = *pBufferSize;
//
// On the last read, the bytes to read may need to be reduced
//
if (BytesToRead > TotalRemaining)
BytesToRead = TotalRemaining;
wiauDbgTrace("ReadData", "reading a chunk of data = %d", BytesToRead);
BytesRead = 0;
if (!ReadFile(m_hUSB, pData, BytesToRead, &BytesRead, NULL))
{
hr = HRESULT_FROM_WIN32(::GetLastError());
wiauDbgErrorHr(hr, "ReadData", "ReadFile failed");
return hr;
}
if ((BytesRead > *pBufferSize) ||
(BytesRead != BytesToRead))
{
wiauDbgError("ReadData", "wrong amount of data read = %d -", BytesRead);
return E_FAIL;
}
TotalRemaining -= BytesRead;
TotalRead += BytesRead;
if (m_pDataCallbackParam &&
!bAbortTransfer)
{
//
// Call the callback function reporting percent complete, offset, and amount read.
// The callback may update pData and BufferSize.
//
hr = m_pPTPDataCB(m_pDataCallbackParam, (TotalRead * 100 / TotalToRead),
lOffset, BytesRead, &pData, (LONG *) pBufferSize);
if (FAILED(hr))
{
//
// Report the error
//
wiauDbgErrorHr(hr, "ReadData", "data callback failed");
}
//
// Check if caller wants to cancel the transfer or returns error
//
if (hr == S_FALSE || FAILED(hr))
{
//
// Do not send CancelRequest to cameras that do not support it, just read the
// remainder of the object without reporting progress and return S_FALSE.
//
// Cameras not supporting CancelRequest are:
// all Sony cameras with DeviceVersion < 1.0004
// Nikon E2500 with DeviceVersion = 1.0
//
const double NIKON_E2500_VERSION_NOT_SUPPORTING_CANCEL = 1.0;
const double MIN_SONY_VERSION_SUPPORTING_CANCEL = 1.0004;
if ((GetHackModel() == HACK_MODEL_NIKON_E2500 &&
GetHackVersion() == NIKON_E2500_VERSION_NOT_SUPPORTING_CANCEL) ||
(GetHackModel() == HACK_MODEL_SONY &&
GetHackVersion() < MIN_SONY_VERSION_SUPPORTING_CANCEL))
{
wiauDbgWarning("ReadData",
"Transfer cancelled, reading but ignoring remainder of the object (%d bytes)", TotalRemaining);
bAbortTransfer = TRUE;
m_Phase = CAMERA_PHASE_RESPONSE; // camera will send response
hr = S_OK;
}
else
{
wiauDbgWarning("ReadData", "Transfer cancelled, aborting current transfer");
hr = SendCancelRequest(m_prevTranId);
if (FAILED(hr))
{
wiauDbgErrorHr(hr, "ReadData", "SendCancelRequest failed");
return hr;
}
m_Phase = CAMERA_PHASE_IDLE; // camera will not send response
return S_FALSE;
}
}
}
//
// Increment the offset
//
lOffset += BytesRead;
}
if ((TotalRead + sizeof(m_pUsbData->Header)) % m_EndpointInfo.BulkInMaxSize == 0)
{
//
// Read the extra null packet
//
wiauDbgTrace("ReadData", "reading a null packet");
BytesRead = 0;
if (!ReadFile(m_hUSB, m_pUsbData, m_UsbDataSize, &BytesRead, NULL))
{
hr = HRESULT_FROM_WIN32(::GetLastError());
wiauDbgErrorHr(hr, "ReadData", "ReadFile failed");
return hr;
}
if (BytesRead != 0)
{
wiauDbgError("ReadData", "tried to read null packet but read %d bytes instead", BytesRead);
return E_FAIL;
}
}
*pBufferSize = TotalRead;
wiauDbgTrace("ReadData", "%d bytes of data successfully read", TotalRead);
if (bAbortTransfer)
hr = S_FALSE;
return hr;
}
//
// This function writes bulk data to the device
//
// Input:
// pData -- pointer to a buffer of data to write
// BufferSize -- amount of data to write
//
HRESULT
CUsbCamera::SendData(
BYTE *pData,
UINT BufferSize
)
{
DBG_FN("CUsbCamera::SendData");
HRESULT hr = S_OK;
if (!pData ||
BufferSize == 0)
{
wiauDbgError("SendData", "invalid arg");
return E_INVALIDARG;
}
//
// Figure out how many packets it will take to contain the header
//
UINT BytesToWrite = m_EndpointInfo.BulkOutMaxSize;
while (BytesToWrite < sizeof(m_pUsbData->Header))
{
BytesToWrite += m_EndpointInfo.BulkOutMaxSize;
}
//
// The first write will contain the USB header plus as much of the data as it
// takes to fill out the packet. We need to write full packets, otherwise the device
// will think the transfer is done.
//
UINT FirstWriteDataAmount = min(BufferSize, BytesToWrite - sizeof(m_pUsbData->Header));
BytesToWrite = sizeof(m_pUsbData->Header) + FirstWriteDataAmount;
//
// Fill out header fields
//
m_pUsbData->Header.Len = BufferSize + sizeof(m_pUsbData->Header);
m_pUsbData->Header.Type = PTPCONTAINER_TYPE_DATA;
m_pUsbData->Header.Code = m_prevOpCode;
m_pUsbData->Header.TransactionId = m_prevTranId;
//
// Copy the part of the data needed to fill out the packets
//
memcpy(m_pUsbData->Data, pData, FirstWriteDataAmount);
//
// Write the header plus partial data
//
wiauDbgTrace("SendData", "Writing first packet, length = %d", BytesToWrite);
DWORD BytesWritten = 0;
if (!WriteFile(m_hUSB, m_pUsbData, BytesToWrite, &BytesWritten, NULL))
{
hr = HRESULT_FROM_WIN32(::GetLastError());
wiauDbgErrorHr(hr, "SendData", "WriteFile failed");
return hr;
}
if (BytesWritten != BytesToWrite)
{
wiauDbgError("SendData", "wrong amount of data written = %d", BytesWritten);
return E_FAIL;
}
UINT TotalBytesWritten = BytesWritten;
//
// The next write (if necessary) will include the remainder of the data
//
if (BufferSize > FirstWriteDataAmount)
{
BytesToWrite = BufferSize - FirstWriteDataAmount;
BytesWritten = 0;
wiauDbgTrace("SendData", "writing remainder of data, length = %d", BytesToWrite);
if (!WriteFile(m_hUSB, &pData[FirstWriteDataAmount], BytesToWrite, &BytesWritten, NULL))
{
hr = HRESULT_FROM_WIN32(::GetLastError());
wiauDbgErrorHr(hr, "SendData", "second WriteFile failed");
return hr;
}
if (BytesWritten != BytesToWrite)
{
wiauDbgError("SendData", "wrong amount of data written = %d -", BytesWritten);
return E_FAIL;
}
TotalBytesWritten += BytesWritten;
}
//
// If the amount written is exactly a multiple of the packet size, send an empty packet
// so the device knows we are done sending data
//
if (TotalBytesWritten % m_EndpointInfo.BulkOutMaxSize == 0)
{
BytesWritten = 0;
wiauDbgTrace("SendData", "writing null packet");
if (!WriteFile(m_hUSB, NULL, 0, &BytesWritten, NULL))
{
hr = HRESULT_FROM_WIN32(::GetLastError());
wiauDbgErrorHr(hr, "SendData", "third WriteFile failed");
return hr;
}
if (BytesWritten != 0)
{
wiauDbgError("SendData", "wrong amount of data written = %d --", BytesWritten);
return E_FAIL;
}
}
wiauDbgTrace("SendData", "%d bytes of data successfully written", TotalBytesWritten);
return hr;
}
//
// This function reads the response data from the device
//
// Input:
// pResponse -- pointer to a response structure to receive the response data
//
HRESULT
CUsbCamera::ReadResponse(
PTP_RESPONSE *pResponse
)
{
DBG_FN("CUsbCamera::ReadResponse");
HRESULT hr = S_OK;
if (!pResponse)
{
wiauDbgError("ReadResponse", "invalid arg");
return E_INVALIDARG;
}
//
// Handle response from reset command
//
if (m_prevOpCode == PTP_OPCODE_RESETDEVICE)
{
wiauDbgTrace("ReadResponse", "creating reset response");
pResponse->ResponseCode = PTP_RESPONSECODE_OK;
pResponse->SessionId = m_SessionId;
pResponse->TransactionId = m_prevTranId;
}
else
{
//
// Clear the USB response buffer
//
memset(&m_UsbResponse, NULL, sizeof(m_UsbResponse));
//
// Read the response from the device
//
DWORD BytesRead = 0;
wiauDbgTrace("ReadResponse", "reading response");
if (!ReadFile(m_hUSB, &m_UsbResponse, sizeof(m_UsbResponse), &BytesRead, NULL))
{
hr = HRESULT_FROM_WIN32(::GetLastError());
wiauDbgErrorHr(hr, "ReadResponse", "ReadFile failed");
return hr;
}
if ((BytesRead < sizeof(m_UsbResponse.Header)) ||
(BytesRead > sizeof(m_UsbResponse)))
{
wiauDbgError("ReadResponse", "wrong amount of data read = %d", BytesRead);
return E_FAIL;
}
//
// Check the type code in the response to make sure it's correct
//
if (m_UsbResponse.Header.Type != PTPCONTAINER_TYPE_RESPONSE)
{
wiauDbgError("ReadResponse", "expected a response but received type = %d", m_UsbResponse.Header.Type);
return E_FAIL;
}
//
// Unwrap the PTP response from the USB container
//
pResponse->ResponseCode = m_UsbResponse.Header.Code;
pResponse->SessionId = m_SessionId; // USB doesn't care about session id, so just use the one we have stored
pResponse->TransactionId = m_UsbResponse.Header.TransactionId;
DWORD ParamLen = BytesRead - sizeof(m_UsbResponse.Header);
if (ParamLen > 0)
{
memcpy(pResponse->Params, m_UsbResponse.Params, ParamLen);
}
}
wiauDbgTrace("ReadResponse", "response successfully read");
return hr;
}
//
// This function reads event data from the device
//
// Input:
// pEvent -- pointer to a PTP event structure to receive the event data
//
HRESULT
CUsbCamera::ReadEvent(
PTP_EVENT *pEvent
)
{
DBG_FN("CUsbCamera::ReadEvent");
HRESULT hr = S_OK;
if (!pEvent)
{
wiauDbgError("ReadEvent", "invalid arg");
return E_INVALIDARG;
}
//
// Allocate buffer for reading event from camera. It should be big enough to accomodate
// packet of maximum allowed size, otherwise we'll get INVALID_ARG
//
DWORD cbEventBufSize = max(sizeof(USB_PTP_EVENT), m_EndpointInfo.InterruptMaxSize);
USB_PTP_EVENT *pEventBuf = (USB_PTP_EVENT*) new BYTE[cbEventBufSize];
if (pEventBuf == NULL)
{
wiauDbgError("ReadEvent", "Memory allocation failed");
hr = E_OUTOFMEMORY;
goto Cleanup;
}
memset(pEventBuf, 0, cbEventBufSize);
//
// Read the event from the device. DeviceIoControl is called in overlapped mode. If
// no information is ready on the interrupt pipe, GetOverlappedResult will wait for
// data to arrive. Unfortunately, DeviceIoControl returns after each packet, so keep
// reading until a short packet is received.
//
DWORD BytesRead = 0;
DWORD TotalBytesRead = 0;
BOOL bReceivedShortPacket = FALSE;
BYTE *pData = (BYTE*) pEventBuf;
wiauDbgTrace("ReadEvent", "reading event");
while (!bReceivedShortPacket)
{
if (!ResetEvent(m_hEventRead))
{
hr = HRESULT_FROM_WIN32(::GetLastError());
wiauDbgErrorHr(hr, "ReadEvent", "ResetEvent failed");
return hr;
}
memset(&m_Overlapped, 0, sizeof(OVERLAPPED));
m_Overlapped.hEvent = m_hEventRead;
if (!DeviceIoControl(m_hEventUSB,
IOCTL_WAIT_ON_DEVICE_EVENT,
NULL,
0,
pData,
cbEventBufSize - TotalBytesRead,
&BytesRead,
&m_Overlapped))
{
hr = HRESULT_FROM_WIN32(::GetLastError());
if (hr == HRESULT_FROM_WIN32(ERROR_IO_PENDING))
{
hr = S_OK;
DWORD ret;
wiauDbgTrace("ReadEvent", "waiting for interrupt pipe data");
ret = WaitForMultipleObjects(2, m_EventHandles, FALSE, INFINITE);
if (ret == WAIT_FAILED)
{
hr = HRESULT_FROM_WIN32(::GetLastError());
wiauDbgErrorHr(hr, "ReadEvent", "WaitForMultipleObjects failed");
goto Cleanup;
}
else if (ret == WAIT_OBJECT_0)
{
//
// Indicate to caller that I/O was cancelled
//
wiauDbgTrace("ReadEvent", "Cancelling I/O on the interrupt pipe");
hr = S_FALSE;
HRESULT temphr = S_OK;
//
// Cancel the pending I/O on the interrupt pipe
//
if (!CancelIo(m_hEventUSB))
{
temphr = HRESULT_FROM_WIN32(::GetLastError());
wiauDbgErrorHr(hr, "ReadEvent", "CancelIo failed");
}
//
// Exit point when I/O is cancelled!!!
//
goto Cleanup;
}
else
{
//
// Get result of read
//
if (!GetOverlappedResult(m_hEventUSB, &m_Overlapped, &BytesRead, TRUE))
{
hr = HRESULT_FROM_WIN32(::GetLastError());
wiauDbgErrorHr(hr, "ReadEvent", "GetOverlappedResult failed");
goto Cleanup;
}
}
}
else
{
wiauDbgErrorHr(hr, "ReadEvent", "DeviceIoControl failed");
goto Cleanup;
}
}
if (BytesRead == 0) {
bReceivedShortPacket = TRUE;
}
else {
TotalBytesRead += BytesRead;
pData += BytesRead;
bReceivedShortPacket = (BytesRead % m_EndpointInfo.InterruptMaxSize != 0);
}
}
//
// Verify that camera sent correct amount of data
//
if ((TotalBytesRead < sizeof(USB_PTP_HEADER)) ||
(TotalBytesRead > sizeof(USB_PTP_EVENT)))
{
wiauDbgError("ReadEvent", "wrong amount of data read by DeviceIoControl = %d", TotalBytesRead);
hr = E_FAIL;
goto Cleanup;
}
//
// Check the type code in the response to make sure it's correct
//
if (pEventBuf->Header.Type != PTPCONTAINER_TYPE_EVENT)
{
wiauDbgError("ReadEvent", "expected an event but received type = %d", pEventBuf->Header.Type);
hr = E_FAIL;
goto Cleanup;
}
//
// Unwrap the PTP event from the USB container
//
pEvent->EventCode = pEventBuf->Header.Code;
pEvent->SessionId = m_SessionId; // USB doesn't care about session id, so just use the one we have stored
pEvent->TransactionId = pEventBuf->Header.TransactionId;
DWORD ParamLen = TotalBytesRead - sizeof(pEventBuf->Header);
if (ParamLen > 0)
{
memcpy(pEvent->Params, pEventBuf->Params, ParamLen);
}
wiauDbgTrace("ReadEvent", "event successfully read, byte count = %d", TotalBytesRead);
Cleanup:
if (pEventBuf)
{
delete[] (BYTE*)pEventBuf;
pEventBuf = NULL;
}
return hr;
}
//
// This function cancels the remainder of a data transfer.
//
HRESULT
CUsbCamera::AbortTransfer()
{
DBG_FN("CUsbCamera::AbortTransfer");
HRESULT hr = S_OK;
//
// WIAFIX-8/28/2000-davepar Fill in the details:
// 1. If usbscan.sys already transferred the data, clear it's buffer
// 2. If not, send cancel control code to camera
//
return hr;
}
//
// This function attempts to recover from an error. When this function returns, the
// device will be in one of three states:
// 1. Ready for more commands, indicated by S_OK
// 2. Reset, indicated by S_FALSE
// 3. Unreachable, indicated by FAILED(hr)
//
HRESULT
CUsbCamera::RecoverFromError()
{
DBG_FN("CUsbCamera::RecoverFromError");
HRESULT hr = S_OK;
//
// WIAFIX-7/29/2000-davepar Maybe first should cancel all pending I/O with IOCTL_CANCEL_IO??
//
//
// Attempt to get status on the device
//
USB_PTPDEVICESTATUS DeviceStatus;
hr = GetDeviceStatus(&DeviceStatus);
//
// If that worked, clear any stalls returned
//
if (SUCCEEDED(hr))
{
hr = ClearStalls(&DeviceStatus);
//
// If clearing all the stalls worked, exit
//
if (SUCCEEDED(hr))
{
wiauDbgTrace("RecoverFromError", "device is ready for more commands");
return S_OK;
}
}
//
// Either the GetDeviceStatus or ClearStall failed, reset the device
//
hr = ResetDevice();
//
// If that worked, return S_FALSE
//
if (SUCCEEDED(hr))
{
wiauDbgWarning("RecoverFromError", "the device was reset");
return S_FALSE;
}
//
// If that fails, the device is unreachable
//
wiauDbgError("RecoverFromError", "ResetDevice failed");
return hr;
}
//
// This function gets the device status, used mainly after an error occurs. It
// may return an endpoint number that the device has intentionally stalled to
// cancel a transaction. The caller should be prepared to clear the stall.
//
// Input:
// pDeviceStatus -- the receive the status.
//
HRESULT
CUsbCamera::GetDeviceStatus(
USB_PTPDEVICESTATUS *pDeviceStatus
)
{
DBG_FN("CUsbCamera::GetDeviceStatus");
HRESULT hr = S_OK;
//
// Set up the request
//
IO_BLOCK_EX IoBlock;
IoBlock.bRequest = USB_PTPREQUEST_GETSTATUS;
IoBlock.bmRequestType = USB_PTPREQUEST_TYPE_IN;
IoBlock.fTransferDirectionIn = TRUE;
IoBlock.uOffset = 0;
IoBlock.uLength = sizeof(*pDeviceStatus);
IoBlock.pbyData = (UCHAR *) pDeviceStatus;
IoBlock.uIndex = 0;
pDeviceStatus->Header.Code = 0;
//
// Send the request
//
wiauDbgTrace("GetDeviceStatus", "sending GetDeviceStatus request");
DWORD BytesRead = 0;
if (!DeviceIoControl(m_hUSB,
IOCTL_SEND_USB_REQUEST_PTP,
&IoBlock,
sizeof(IoBlock),
pDeviceStatus,
sizeof(*pDeviceStatus),
&BytesRead,
NULL
))
{
hr = HRESULT_FROM_WIN32(::GetLastError());
wiauDbgErrorHr(hr, "GetDeviceStatus", "DeviceIoControl failed");
return hr;
}
if (BytesRead < sizeof(USB_PTPDEVICESTATUS_HEADER) ||
BytesRead > sizeof(*pDeviceStatus))
{
wiauDbgError("GetDeviceStatus", "wrong amount of data returned = %d", BytesRead);
return E_FAIL;
}
if((HIBYTE(pDeviceStatus->Header.Code) & 0xF0) != 0x20 &&
(HIBYTE(pDeviceStatus->Header.Code) & 0xF0) != 0xA0)
{
wiauDbgError("GetDeviceStatus", "PTP status code (0x%x)is invalid ", pDeviceStatus->Header.Code);
return E_FAIL;
}
wiauDbgTrace("GetDeviceStatus", "read %d bytes", BytesRead);
if (g_dwDebugFlags & WIAUDBG_DUMP)
{
wiauDbgTrace("GetDeviceStatus", "Dumping device status:");
wiauDbgTrace("GetDeviceStatus", " Length = 0x%04x", pDeviceStatus->Header.Len);
wiauDbgTrace("GetDeviceStatus", " Response code = 0x%04x", pDeviceStatus->Header.Code);
ULONG NumParams = (ULONG)min(MAX_NUM_PIPES, (BytesRead - sizeof(pDeviceStatus->Header) / sizeof(pDeviceStatus->Params[0])));
for (ULONG count = 0; count < NumParams; count++)
{
wiauDbgTrace("GetDeviceStatus", " Param %d = 0x%08x", count, pDeviceStatus->Params[count]);
}
}
return hr;
}
//
// This function clears all the stalls listed in the given device status
//
// Input:
// pDeviceStatus -- lists zero or more stalled endpoints
//
HRESULT
CUsbCamera::ClearStalls(
USB_PTPDEVICESTATUS *pDeviceStatus
)
{
DBG_FN("CUsbCamera::ClearStalls");
HRESULT hr = S_OK;
if (!pDeviceStatus)
{
wiauDbgError("ClearStalls", "invalid arg");
return E_INVALIDARG;
}
PIPE_TYPE PipeType;
ULONG NumStalls = (pDeviceStatus->Header.Len - sizeof(pDeviceStatus->Header)) / sizeof(pDeviceStatus->Params[0]);
for (ULONG count = 0; count < NumStalls; count++)
{
//
// Translate the endpoint address to the pipe type
//
if ((UCHAR)pDeviceStatus->Params[count] == m_EndpointInfo.BulkInAddress)
{
PipeType = READ_DATA_PIPE;
}
else if ((UCHAR)pDeviceStatus->Params[count] == m_EndpointInfo.BulkOutAddress)
{
PipeType = WRITE_DATA_PIPE;
}
else if ((BYTE)pDeviceStatus->Params[count] == m_EndpointInfo.InterruptAddress)
{
PipeType = EVENT_PIPE;
}
else
{
//
// Unrecognized, ignore it
//
wiauDbgError("ClearStalls", "unrecognized pipe address 0x%08x", pDeviceStatus->Params[count]);
continue;
}
//
// Reset the endpoint
//
DWORD BytesRead;
if (!DeviceIoControl(m_hUSB,
IOCTL_RESET_PIPE,
&PipeType,
sizeof(PipeType),
NULL,
0,
&BytesRead,
NULL
))
{
hr = HRESULT_FROM_WIN32(::GetLastError());
wiauDbgErrorHr(hr, "ClearStalls", "DeviceIoControl failed");
return hr;
}
}
if(NumStalls) {
for(count = 0; count < 3; count++) {
if(FAILED(GetDeviceStatus(pDeviceStatus))) {
wiauDbgErrorHr(hr, "ClearStalls", "GetDeviceStatus failed");
return hr;
}
if(pDeviceStatus->Header.Code == PTP_RESPONSECODE_OK) {
break;
}
}
//
// check if still there are stalled endpoints
//
if(pDeviceStatus->Header.Code != PTP_RESPONSECODE_OK)
{
hr = E_FAIL;
}
}
//
// Device should be ready to receive commands again
//
m_Phase = CAMERA_PHASE_IDLE;
return hr;
}
//
// Reset the device
//
HRESULT
CUsbCamera::SendResetDevice()
{
DBG_FN("CUsbCamera::SendResetDevice");
HRESULT hr = S_OK;
//
// Set up the request
//
IO_BLOCK_EX IoBlock;
IoBlock.bRequest = USB_PTPREQUEST_RESET;
IoBlock.bmRequestType = USB_PTPREQUEST_TYPE_OUT;
IoBlock.fTransferDirectionIn = FALSE;
IoBlock.uOffset = 0;
IoBlock.uLength = 0;
IoBlock.pbyData = NULL;
IoBlock.uIndex = 0;
//
// Send the request
//
DWORD BytesRead;
if (!DeviceIoControl(m_hUSB,
IOCTL_SEND_USB_REQUEST_PTP,
&IoBlock,
sizeof(IoBlock),
NULL,
0,
&BytesRead,
NULL
))
{
hr = HRESULT_FROM_WIN32(::GetLastError());
wiauDbgErrorHr(hr, "ResetDevice", "DeviceIoControl failed");
goto Cleanup;
}
//
// Let the device settle
//
Sleep(1000);
//
// See if reset helped
//
USB_PTPDEVICESTATUS DeviceStatus;
hr = GetDeviceStatus(&DeviceStatus);
if (FAILED(hr) || DeviceStatus.Header.Code != PTP_RESPONSECODE_OK)
{
hr = E_FAIL; // device is still unconscious
goto Cleanup;
}
//
// Side effect of reseting the device is that the phase, session id, and transaction id get reset
//
m_Phase = CAMERA_PHASE_IDLE;
m_SessionId = 0;
m_NextTransactionId = PTP_TRANSACTIONID_MIN;
//
// Indicate that camera was reset, so that CWiaMiniDriver can notify caller
//
m_bCameraWasReset = TRUE;
Cleanup:
return hr;
}
//
// This function sends the class CancelRequest command to the device
// and wait for the device to complete the request.
// Input:
// dwTransactionId -- transaction being canceled
//
HRESULT
CUsbCamera::SendCancelRequest(DWORD dwTransactionId)
{
DBG_FN("CUsbCamera::CancelRequest");
HRESULT hr = S_OK;
IO_BLOCK_EX IoBlock;
USB_PTPCANCELIOREQUEST CancelRequest;
DWORD BytesReturned;
IoBlock.bRequest = USB_PTPREQUEST_CANCELIO;
IoBlock.bmRequestType = USB_PTPREQUEST_TYPE_OUT;
IoBlock.fTransferDirectionIn = FALSE; // Host to device
IoBlock.uOffset = 0; // 0 for this request
IoBlock.uLength = sizeof(USB_PTPCANCELIOREQUEST); // Data output length
IoBlock.pbyData = (BYTE *)&CancelRequest; // output data
IoBlock.uIndex = 0; // 0 for this request
CancelRequest.Id = USB_PTPCANCELIO_ID;
CancelRequest.TransactionId = dwTransactionId;
if (DeviceIoControl(m_hUSB,
IOCTL_SEND_USB_REQUEST_PTP,
&IoBlock,
sizeof(IoBlock),
NULL,
0,
&BytesReturned,
NULL
))
{
//
// Poll device until it returns to idle state
//
USB_PTPDEVICESTATUS DeviceStatus;
const UINT MAX_CANCEL_RECOVERY_MILLISECONDS = 3000;
const UINT SLEEP_BETWEEN_RETRIES = 100;
DWORD RetryCounts = MAX_CANCEL_RECOVERY_MILLISECONDS / SLEEP_BETWEEN_RETRIES;
while (RetryCounts--)
{
hr = GetDeviceStatus(&DeviceStatus);
if (SUCCEEDED(hr))
{
if (PTP_RESPONSECODE_OK == DeviceStatus.Header.Code)
{
//
// CancelRequest completed and device is back idle
//
hr = S_OK;
break;
}
else if (PTP_RESPONSECODE_DEVICEBUSY != DeviceStatus.Header.Code)
{
//
// This is wrong. Device must be either busy or idle
//
wiauDbgError("SendCancelRequest",
"Device is in invalid state, DeviceStatus=0x%X", DeviceStatus.Header.Code);
hr = E_FAIL;
break;
}
}
else
{
if (RetryCounts)
{
hr = S_OK;
wiauDbgWarning("CancelRequest", "GetDeviceStatus failed, retrying...");
}
else
{
wiauDbgError("CancelRequest", "GetDeviceStatus failed");
}
}
Sleep(SLEEP_BETWEEN_RETRIES);
}
//
// Flush system buffers - otherwise we'll get old data on next read
//
FlushFileBuffers(m_hUSB);
}
else
{
hr = HRESULT_FROM_WIN32(::GetLastError());
wiauDbgErrorHr(hr, "CancelRequest", "send USB request failed");
}
return hr;
}