dousb.c 11 KB
#include "../bbclocal.h"
#include <windows.h>
#include <conio.h>
#include <assert.h>
#include <time.h>
#include <setupapi.h>
#include <basetyps.h>
#include "usbdi.h"
#include <io.h>
#include <fcntl.h>
//#include "rdb_guid.h"
#include "dousb.h"
#include "bulkusr.h"

HANDLE OpenOneDevice (HDEVINFO HardwareDeviceInfo, 
                      PSP_INTERFACE_DEVICE_DATA DeviceInfoData, 
                      char *devName)
{
    PSP_INTERFACE_DEVICE_DETAIL_DATA     functionClassDeviceData = NULL;
    ULONG                                predictedLength = 0;
    ULONG                                requiredLength = 0;
    HANDLE                               hOut = INVALID_HANDLE_VALUE;

    SetupDiGetInterfaceDeviceDetail (
       HardwareDeviceInfo,
       DeviceInfoData,
       NULL, // probing so no output buffer yet
       0, // probing so output buffer length of zero
       &requiredLength,
       NULL); // not interested in the specific dev-node

    predictedLength = requiredLength;

    functionClassDeviceData = (PSP_INTERFACE_DEVICE_DETAIL_DATA) malloc (predictedLength);
    functionClassDeviceData->cbSize = sizeof (SP_INTERFACE_DEVICE_DETAIL_DATA);

    if (! SetupDiGetInterfaceDeviceDetail (
        HardwareDeviceInfo,
        DeviceInfoData,
        functionClassDeviceData,
        predictedLength,
        &requiredLength,
        NULL)) {
        free( functionClassDeviceData );
        return INVALID_HANDLE_VALUE;
    }

    strcpy( devName,functionClassDeviceData->DevicePath) ;
    BBC_LOG(MSG_DIAG, "Attempting to open %s\n", devName );

    // NOTE: Okay to open USB device as shared since read/write pipes
    //  are separately controlled.   Regardless, the share access parameters
    //  ignored by thebbrdb driver
    hOut = CreateFile ( functionClassDeviceData->DevicePath,
                        GENERIC_READ | GENERIC_WRITE,
                        FILE_SHARE_READ | FILE_SHARE_WRITE, 
                        NULL, // no SECURITY_ATTRIBUTES structure
                        OPEN_EXISTING, // No special create flags
                        0, // No special attributes
                        NULL); // No template file

    if (INVALID_HANDLE_VALUE == hOut) {
        BBC_LOG(MSG_ERR, "FAILED to open %s\n", devName );
        BBC_LOG_SYSERROR("Error opening USB");
    }
    free( functionClassDeviceData );

    return hOut;
}

BOOL OpenUsbDevice( LPGUID  pGuid, char *outNameBuf, int* bbDevices)
{
    ULONG NumberDevices;
    HANDLE hOut = INVALID_HANDLE_VALUE;
    HDEVINFO                 hardwareDeviceInfo;
    SP_INTERFACE_DEVICE_DATA deviceInfoData;
    ULONG                    i;
    BOOLEAN                  done;
    PUSB_DEVICE_DESCRIPTOR   usbDeviceInst;
    PUSB_DEVICE_DESCRIPTOR    *UsbDevices = &usbDeviceInst;

    *UsbDevices = NULL;

    hardwareDeviceInfo = SetupDiGetClassDevs (
                           pGuid,
                           NULL, // Define no enumerator (global)
                           NULL, // Define no
                           (DIGCF_PRESENT | // Only Devices present
                            DIGCF_INTERFACEDEVICE));

    NumberDevices = 4;
    done = FALSE;
    deviceInfoData.cbSize = sizeof (SP_INTERFACE_DEVICE_DATA);

    i=0;
    while (!done) {
        NumberDevices *= 2;

        if (*UsbDevices) {
            *UsbDevices = (PUSB_DEVICE_DESCRIPTOR)
            realloc(*UsbDevices, (NumberDevices * sizeof (USB_DEVICE_DESCRIPTOR)));
        } else {
            *UsbDevices = (PUSB_DEVICE_DESCRIPTOR) calloc (NumberDevices, sizeof (USB_DEVICE_DESCRIPTOR));
        }

        if (NULL == *UsbDevices) {  
            SetupDiDestroyDeviceInfoList (hardwareDeviceInfo);
            return FALSE;
        }

        for (; i < NumberDevices; i++) {
            if (SetupDiEnumDeviceInterfaces (hardwareDeviceInfo,
                                             0, // We don't care about specific PDOs
                                             pGuid,
                                             i,
                                             &deviceInfoData)) {                
                hOut = OpenOneDevice (hardwareDeviceInfo, &deviceInfoData, outNameBuf);
                if ( hOut != INVALID_HANDLE_VALUE ) {
                   CloseHandle( hOut );
                   (*bbDevices) ++;
                   if (*bbDevices>1) 
                       done = TRUE;
                   i++;
                   break;
                }
             } else {
                if (ERROR_NO_MORE_ITEMS == GetLastError()) {
                   done = TRUE;
                   break;
                }
             }    
        }
    }

    NumberDevices = i;
    SetupDiDestroyDeviceInfoList (hardwareDeviceInfo);
    free ( *UsbDevices );

    if (*bbDevices==0 || *bbDevices>1) 
        return FALSE;
    else 
        return TRUE;
}

/* 
 * Reset USB Pipe
 *
 * This sends a CLEAR ENDPOINT_HALT to the BB Player
 *   on the host read (BB transmit) endpoint, and will
 *   cause the BB Player to reset it's USB and RDB states,
 *   and to transmit a RDB_GtoH_READY_FOR_DATA signal.
 *
 *   When this signal is received by the USB read,
 *   it will allow __bbc_send_win to send an RDB packet
 *   to the BB Player through the USB.
 */
BOOL do_usb_reset(HANDLE usbHandle)
{
    BOOL bResult;                 // results flag
    DWORD nBytes;                 // Bytes returned

    /* Try to use DeviceIoControl to send a reset to the BB Player */
    if (usbHandle == INVALID_HANDLE_VALUE)
    {
        BBC_LOG(MSG_ERR, "do_usb_reset: Invalid USB Handle %d", usbHandle);
        return (FALSE);
    }

    nBytes = 0;
    bResult = DeviceIoControl(usbHandle,  // device to be queried
                              IOCTL_BULKUSB_RESET_PIPE,  // operation to perform
                              NULL, 0, // no input buffer
                              NULL, 0, // no output buffer
                              &nBytes,               // # bytes returned
                              (LPOVERLAPPED) NULL);  // synchronous I/O

    if (bResult == FALSE) {
        BBC_LOG_SYSERROR("Error resetting USB pipe");
    }

    return bResult;
}

HANDLE  open_usb_pipe(int which, char* usbDevName, int* bbDevices)
{
    HANDLE h;
    char pipename[8];

    *bbDevices = 0;
    if( !OpenUsbDevice((LPGUID) &GUID_CLASS_BBRDB, usbDevName, bbDevices) 
        || *bbDevices>1)
        return INVALID_HANDLE_VALUE;

    strcat(usbDevName, "\\");
    sprintf(pipename, "PIPE%02d", which);
    strcat (usbDevName, pipename);

    BBC_LOG(MSG_DIAG, "open_usb_pipe: completeDeviceName = (%s)\n", usbDevName);

    h = CreateFile(usbDevName,
        GENERIC_WRITE | GENERIC_READ,
        0,  // Do not share - this parameter is currently ignored by the bbrdb driver
        NULL,
        OPEN_EXISTING,
        0,
        NULL);

    if (h == INVALID_HANDLE_VALUE) {
        BBC_LOG(MSG_ERR, "Failed to open (%s) = %d\n", usbDevName, GetLastError());
        BBC_LOG_SYSERROR("Error opening USB");
    } else {
        BBC_LOG(MSG_INFO, "Opened successfully %d.\n", h);
    }        

    return h;
}

HANDLE hUsbThread;
int recv_next(const char* buf, unsigned int buflen);

unsigned __stdcall do_usb_read(LPVOID lpParameter)
{
    unsigned long  readLen;
    unsigned char  recv_data[MAX_PIPE_DATA];
    unsigned int rv = 0;

    DWORD dwWaitResult;
    HANDLE hDeviceEvents[3];
    hDeviceEvents[0] = hDeviceErrorEvent;
    hDeviceEvents[1] = hDeviceWriteDoneEvent;
    hDeviceEvents[2] = hBBCCloseEvent;

    memset(recv_data, 0, sizeof(recv_data));    

    /*  Because RDB protocol, 
        we can only read small block */
    while (1) { 
        if ((HANDLE)lpParameter == NULL)   /* Usb pipe cannot be opened */
        {
            BBC_LOG(MSG_ERR, "Cannot open USB pipe.\n");
            rv = -1;
            break;
        }

        BBC_LOG(MSG_ALL, "do_usb_read: Waiting for device write done or error event\n");
        dwWaitResult = myWaitForMultipleObjects( 
            3,              /* number of handles in array      */
            hDeviceEvents,  /* array of device-event handles   */
            FALSE,          /* wait until only one is signaled */
            WAIT_DEVICE_READY_TIMEOUT);  /* 2 second wait      */

        if (dwWaitResult == WAIT_TIMEOUT) {
			BBC_LOG(MSG_ALL, "do_usb_read: Device write done wait timeout\n");
            continue;
		}
        else if (dwWaitResult != WAIT_OBJECT_0 + 1) {
            /* Hmm, write not done */
            if (dwWaitResult == WAIT_OBJECT_0 + 2) {
                // Check to see if we are closing
                BBC_LOG(MSG_INFO, "do_usb_read: Stopping USB thread\n");
                rv = 0;
            }
            else if (dwWaitResult == WAIT_OBJECT_0) {
                // Check to see if device error
                BBC_LOG(MSG_ERR, "do_usb_read: Device error\n");
                rv = -1;
            }
            else {
                // Other error
                BBC_LOG(MSG_ERR, "do_usb_read: Error waiting for device write done, got %d\n", dwWaitResult);
                rv = -1;
            }
            break;
        }

        BBC_LOG(MSG_DEBUG, "do_usb_read: Entering USB read...");
        if (ReadFile((HANDLE)lpParameter, recv_data, MAX_PIPE_DATA, &readLen, NULL)) { 

#if 0
            BBC_LOG(MSG_ALL, "Got %d bytes from USB: %02x %02x %02x %02x\n", 
                    readLen, recv_data[i], recv_data[i+1],
                    recv_data[i+2], recv_data[i+3]);
#endif

            /* Deal with Recv data here */
            BBC_LOG(MSG_DEBUG, "dousb: recv_next %d\n", readLen);
            recv_next(recv_data, readLen);
            BBC_LOG(MSG_DEBUG, "dousb: recv_next %d done\n", readLen);
        } else { 
            // We've decided to close, don't care about USB error anymore
            if (myWaitForSingleObject(hBBCCloseEvent, 0) == WAIT_OBJECT_0) {
                BBC_LOG(MSG_INFO, "do_usb_read: Terminating USB thread\n");
                rv = 0;    
                break;
            }

            /* XXX whs should handle Read failure case */
            BBC_LOG_SYSERROR("Error reading from USB");
            SetEvent(hDeviceErrorEvent);
            (HANDLE)lpParameter = NULL;
            rv = -1;
            break;
        }
    }

    _endthreadex(rv);
    return rv;
}

int bb_usb_read_end()
{
    if (hUsbThread) {
        BBC_LOG(MSG_INFO, "Wait for USB thread to terminate nicely\n");
        if (myWaitForSingleObject( hUsbThread, WAIT_THREAD_TERM_TIMEOUT ) != WAIT_OBJECT_0 ) {
            BBC_LOG(MSG_INFO, "Terminating USB thread\n");
            if (TerminateThread(hUsbThread, -1) == FALSE) {
                BBC_LOG_SYSERROR("Error terminating USB thread");
            }
        }
        else {
            BBC_LOG(MSG_INFO, "USB thread terminated\n");
        }
        if (CloseHandle(hUsbThread) == FALSE) {
            BBC_LOG_SYSERROR("Error closing handle to USB thread");
        }
        hUsbThread = NULL;
    }

    return 0;
}

int bb_usb_read_start(HANDLE* hread)
{
    unsigned int dwUsbId;

    hUsbThread = (HANDLE) _beginthreadex(NULL, 0, &do_usb_read, 
                                *hread, 0, &dwUsbId);

    if (hUsbThread==NULL) 
        return -1;

    return 0;
}