/*****************************************************************************
 * Copyright (C) Cinterion Wireless Modules GmbH
 *****************************************************************************
 * Project: Quinn-CE-USB-Driver
 *****************************************************************************
 * Author : Siemens AG Berlin COM WM RD SW
 *****************************************************************************
 * $Workfile: O:/ctna0450_RIL/z_ms-d/src/working/Quinn/HcUsbDrv/DrvEntryUsb.cpp $
 * $Revision: /main/ms-d/28 $
 * $Date: 2011-02-02 10:27:38 $
 * $Author: ctna0450 $
 *****************************************************************************
 * Target:
 *  Microsoft Windows CE 5.0
 *****************************************************************************
 * Description:
 *  Quinn-USB-Driver: USB entry points
 *****************************************************************************
 *
 * $Log: O:/ctna0450_RIL/z_ms-d/src/working/Quinn/HcUsbDrv/DrvEntryUsb.cpp$ 
 * 
 * /main/ms-d/28   2011-02-02 10:27:38   ctna0450 
 * Removed unused USB-Reset workaround for Uranus USB problems
 *
 * /main/ms-d/27   2011-01-13 15:24:27   ctna0152
 * added version debug messages on DLL start
 *
 * /main/ms-d/26   2011-01-11 11:38:45   ctna0152
 * added debugzones for DEBUG build
 *
 * /main/ms-d/25   2010-12-13 14:20:45   ctna0152
 * added USB reset handling
 *
 * /main/ms-d/24   2010-08-17 14:28:34   ctna0450
 * Added trace info
 *
 * /main/ms-d/23   2009-02-23 11:40:24   ctna0450
 * Leave CS properly in case of error in USBDeviceAttach()
 *
 * /main/ms-d/22   2009-02-18 11:49:02   ctna0256
 * Set copyright text
 *
 * /main/ms-d/21   2008-08-20 11:39:27   ctna0450
 * Prepared for new disclaimer handling
 *
 * /main/ms-d/20   2008-07-31 15:53:52   ht37010
 * replaced RIL traces with USB traces
 *
 * /main/ms-d/19   2008-06-19 14:24:22   ht37010
 * added Cinterion vendor ID handling
 *
 * /main/ms-d/18   2007-12-18 14:07:51   ht37010
 * changed debug messages
 *
 * /main/ms-d/17   2007-12-17 16:39:36   ht37010
 * changed debug messages
 *
 * /main/ms-d/16   2007-08-02 15:44:29   ht37010
 * changed debug messages
 *
 * /main/ms-d/15   2007-06-01 20:39:44   ht37010
 * synchronized system function calls for USB
 *
 * /main/ms-d/14   2007-05-30 16:34:55   ht37010
 * init/deinit system locks to synchronize system functions
 *
 * /main/ms-d/13   2007-04-12 13:37:58   ht37010
 * changed debug messages
 *
 * /main/ms-d/12   2007-03-08 17:44:23   ht37010
 * added tick counting in USBDeviceAttach() & USBDeviceNotify()
 *
 * /main/ms-d/11   2007-02-16 16:37:52   wm36264
 * Adapted selective suspend/resume notification to MS confirmend interface.
 * Untested and still deactivated by default!
 *
 * /main/ms-d/10   2006-12-14 12:15:54   wm36264
 * Prepared resume notification
 *
 * /main/ms-d/9   2006-11-09 09:46:07   wm36264
 * Added useful debug zones
 *
 * /main/ms-d/8   2006-11-03 17:07:07   wm36264
 * Continued to prepare selective suspend handling
 *
 * /main/ms-d/7   2006-10-09 15:24:12   wm36264
 * Fixed USB detach problems
 *
 * /main/ms-d/6   2006-10-06 18:00:30   wm36264
 * Make virtual ports fully instancable and use different device names for serial, ndis and qmi devices
 *
 * /main/ms-d/5   2006-09-29 15:33:07   wm36264
 * Consider latest findings about QMI and NDIS handling
 *
 * /main/ms-d/4   2006-09-27 17:07:08   wm36264
 * Switched to central version and trace handling
 *
 * /main/ms-d/3   2006-09-22 14:33:22   wm36264
 * Cosmetics mostly
 *
 * /main/ms-d/2   2006-09-21 17:05:38   wm36264
 * Prepared access to QMI interface
 *
 * /main/ms-d/1   2006-09-18 15:31:43   wm36264
 * Initial Quinn Windows CE USB Driver based on UsbSer
 *
*/

//////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2008 Cinterion Wireless Modules GmbH
// Copyright (C) 2006-2008 Siemens AG
//
// Version: 1.0
// Date:    dummy
//
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
//
// DrvEntryUsb.cpp
//
// This file contains the Windows CE USB host client interface as well as the
// driver dll's main entry function.
//
//////////////////////////////////////////////////////////////////////////////

#include "precomp.h"

//////////////////////////////////////////////////////////////////////////////

//#include "UsbTrace.cpp"

//////////////////////////////////////////////////////////////////////////////

extern "C" void __cdecl _cinit(void);
extern "C" void __cdecl _cexit(void);

//////////////////////////////////////////////////////////////////////////////

#ifdef DEBUG
DBGPARAM dpCurSettings = {
  TEXT("HcUsbDrv"), {
    TEXT("Error"),       TEXT("Init"),               TEXT("USB"),          TEXT("USB Parse"),
    TEXT("USB Pipe"),    TEXT("USB Transfer"),       TEXT("USB Suspend")   TEXT("Port Read"),
    TEXT("Port Write"),  TEXT("Port Data"),          TEXT("Port Signals"), TEXT("Port Events"),
    TEXT("Lock"),        TEXT("USB Host Function"),  TEXT("Port"),         TEXT("Info")
  },
  0xFFFF
};
#endif

//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
//
// This function is an optional method of entry into a dynamic-link library
// (DLL). If the function is used, it is called by the system when processes
// and threads are initialized and terminated, or upon calls to the
// LoadLibrary and FreeLibrary functions.
//
// Parameters:
// DllInstance: Handle to the DLL. The value is the base address of the DLL.
//              The HINSTANCE of a DLL is the same as the HMODULE of the
//              DLL, so hinstDLL can be used in subsequent calls to the
//              GetModuleFileName function and other functions that require
//              a module handle.
// Reason     : Specifies a flag indicating why the DLL entry-point function
//              is being called (DLL_PROCESS_ATTACH, DLL_THREAD_ATTACH,
//              DLL_THREAD_DETACH, DLL_PROCESS_DETACH).
// Reserved   : Specifies further aspects of DLL initialization and cleanup.
//              If dwReason is DLL_PROCESS_ATTACH, lpvReserved is NULL for
//              dynamic loads and non-NULL for static loads.
//              If dwReason is DLL_PROCESS_DETACH, lpvReserved is NULL if
//              DllMain has been called by using FreeLibrary and non-NULL
//              if DllMain has been called during process termination.
//
// Return:
// When the system calls the DllMain function with the DLL_PROCESS_ATTACH
// value, the function returns TRUE if it succeeds or FALSE if initialization
// fails.
//
//////////////////////////////////////////////////////////////////////////////
BOOL HcUsbDrvDllMain(HINSTANCE DllInstance, DWORD Reason, LPVOID Reserved) {
  switch(Reason) {
    case DLL_PROCESS_ATTACH:
      _cinit();
      // Init our possibly used serial port trace
      UsbTrace(NULL);
      DEBUGMSG(ZONE_INIT, (TEXT("HcUsb: HcUsbDrvDllMain(%p, %d, %d) - DLL_PROCESS_ATTACH\r\n"), DllInstance, Reason, Reserved));
      DEBUGMSG(TRUE, (TEXT("HcUsb: version %d.%d.%d.%d (WCE %d.%d Build %d System Build %d)\r\n"),
                      HCUSB_VERSION_1, HCUSB_VERSION_2, HCUSB_VERSION_3, HCUSB_VERSION_4,
                      CE_MAJOR_VER, CE_MINOR_VER, CE_BUILD_VER, CE_BUILD_SYSVER));
      CVirtualPort::InitSystemLock();
      CUsbHandling::InitSystemLock();
      break;

    case DLL_PROCESS_DETACH:
      _cexit();
      CUsbHandling::DeinitSystemLock();
      CVirtualPort::DeinitSystemLock();
      // Deinit our possibly used serial port trace
      UsbTrace(NULL);
      DEBUGMSG(ZONE_INIT, (TEXT("HcUsb: HcUsbDrvDllMain(%p, %d, %d) - DLL_PROCESS_DETACH\r\n"), DllInstance, Reason, Reserved));
      break;
  }
  return TRUE;
}

//////////////////////////////////////////////////////////////////////////////
//
// This function installs a universal serial bus (USB) client driver.
// The USB driver module calls this function when an unrecognized device is
// attached to the USB port and the user enters the client driver's DLL name.
// It registers a unique client identifier string with the USB driver module
// and sets up client driver settings.
//
// Parameters:
// DriverLibFile: Driver DLL name.
//
// Return:
// TRUE indicates success. FALSE indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL USBInstallDriver(LPCWSTR DriverLibFile) {
USB_DRIVER_SETTINGS  usbDriverSettings = { HCUSBDRV_DRIVER_SETTINGS };
HKEY                 hKey;
DWORD                dwDisposition;

  DEBUGMSG(ZONE_INIT | ZONE_USB, (TEXT("HcUsb: +USBInstallDriver(%s)\r\n"), DriverLibFile));

  if (!RegisterClientDriverID(TEXT("Cinterion\\")HCUSBDRV_CLASS_NAME)) {
    DEBUGMSG(ZONE_USB | ZONE_ERROR, (TEXT("HcUsb: -USBInstallDriver() - Error: RegisterClientDriverID() failed %d\r\n"), GetLastError()));
    return FALSE;
  }

  /*
  1st register "Cinterion Wireless Modules" vendor ID
  2nd register "Siemens AG" vendor ID
  */
  const DWORD dwVendorIDs[MAX_SUPPORTED_VENDOR_IDS] = {CINTERION_VENDOR_ID, SIEMENS_VENDOR_ID};

  for ( int i = 0; i < MAX_SUPPORTED_VENDOR_IDS; i++ ) {
      usbDriverSettings.dwVendorId = dwVendorIDs[i];
      if (!RegisterClientSettings(DriverLibFile, HCUSBDRV_CLASS_NAME, NULL, &usbDriverSettings)) {
        DEBUGMSG(ZONE_USB | ZONE_ERROR, (TEXT("HcUsb: -USBInstallDriver() - Error: RegisterClientSettings() failed %d\r\n"), GetLastError()));
        return FALSE;
      }
  }

  // Save the required registry values
  if (FAILED(RegCreateKeyEx(HKEY_LOCAL_MACHINE, HCUSBDRV_CLIENT_REGKEY, 0, NULL, 0, 0, NULL, &hKey, &dwDisposition))) {
    DEBUGMSG(ZONE_USB | ZONE_ERROR, (TEXT("HcUsb: -USBInstallDriver() - Error: Failed to open or create driver registry key %s: %d\r\n"), HCUSBDRV_CLIENT_REGKEY, GetLastError()));
    return FALSE;
  }
  if (FAILED(RegSetValueEx(hKey, TEXT("Dll"), 0, REG_SZ, (BYTE*)DriverLibFile, (wcslen(DriverLibFile) + 1) * sizeof(WCHAR)))) {
    DEBUGMSG(ZONE_USB | ZONE_ERROR, (TEXT("HcUsb: -USBInstallDriver() - Error: Failed to write registry value \"Dll\" =  %s: %d\r\n"), DriverLibFile, GetLastError()));
    RegCloseKey( hKey);
    return FALSE;
  }
  RegCloseKey( hKey);

  DEBUGMSG(ZONE_INIT | ZONE_USB, (TEXT("HcUsb: -USBInstallDriver()\r\n")));

  return TRUE;
}

//////////////////////////////////////////////////////////////////////////////
//
// This function uninstalls a universal serial bus (USB) client driver
// A client driver can call this function to deregister from a USB driver.
//
// Parameters:
// None.
//
// Return:
// TRUE indicates success. FALSE indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL USBUnInstallDriver(VOID) {
USB_DRIVER_SETTINGS  usbDriverSettings = { HCUSBDRV_DRIVER_SETTINGS };
BOOL                 fRet = TRUE;

  DEBUGMSG(ZONE_INIT | ZONE_USB, (TEXT("HcUsb: +USBUnInstallDriver()\r\n")));

  if (!UnRegisterClientSettings(HCUSBDRV_CLASS_NAME, NULL, &usbDriverSettings)) {
    DEBUGMSG(ZONE_USB | ZONE_ERROR, (TEXT("HcUsb: Error: UnRegisterClientSettings() failed %d\r\n"), GetLastError()));
    fRet = FALSE;
  }

  if (!UnRegisterClientDriverID(HCUSBDRV_CLASS_NAME)) {
    DEBUGMSG(ZONE_USB | ZONE_ERROR, (TEXT("HcUsb: Error: UnRegisterClientDriverID() failed %d\r\n"), GetLastError()));
    fRet = FALSE;
  }

  DEBUGMSG(ZONE_INIT | ZONE_USB, (TEXT("HcUsb: -USBUnInstallDriver()\r\n")));

  return fRet;
}

//////////////////////////////////////////////////////////////////////////////
//
// This function is a universal serial bus (USB) device attach routine. It
// passes a table of functions to USB client drivers.
// The USB driver module calls this function when a device is attached and a
// matching registry key is found in the LoadClients registry key. The driver
// should determine whether it can control the device. It and must load
// drivers for any uncontrolled interfaces on the device.
//
// Parameters:
// Device        : Handle to a USB device.
// UsbFuncs      : Pointer to a USB device interface function table.
// Interface     : Pointer to the USB_INTERFACE structure that contains
//                 interface information if a client is loaded as an interface
//                 driver. If a client is not loaded for a specific interface,
//                 this parameter is NULL, and the client can get interface
//                 information through the LPFIND_INTERFACE function.
// UniqueDriverId: Client driver identifier string.
// AcceptControl : Indicator if the driver accepts control of the device or if
//                 the driver should continue to try to load client drivers.
///                Set to TRUE if the driver accepts control of the device or
//                 FALSE if the USB driver should continue to try to load
//                 client drivers.
// DriverSettings: Pointer to the USB_DRIVER_SETTINGS structure that indicates
//                 how the driver is loaded.
// Unused        : Reserved for use with future versions of USB driver.
//
// Return:
// TRUE indicates success. FALSE indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL USBDeviceAttach(USB_HANDLE              Device,
                     LPCUSB_FUNCS            Funcs,
                     LPCUSB_INTERFACE        Interface,
                     LPCWSTR                 UniqueDriverId,
                     LPBOOL                  AcceptControl,
                     LPCUSB_DRIVER_SETTINGS  DriverSettings,
                     DWORD                   Unused) {
  PHCUSBDRV_CONTEXT pHcUsbDrv;

  DEBUGMSG(ZONE_INIT | ZONE_USB, (TEXT("HcUsb: +USBDeviceAttach(0x%p, \"%s\")\r\n"), Device, UniqueDriverId));

  CUsbHandling::LockSystem();

  *AcceptControl = FALSE;

  pHcUsbDrv = new HCUSBDRV_CONTEXT;
  if (!pHcUsbDrv) {
    DEBUGMSG(ZONE_INIT | ZONE_USB | ZONE_ERROR, (TEXT("HcUsb: -USBDeviceAttach() - Error: Failed to create HCUSBDRV_CONTEXT\r\n")));
    CUsbHandling::UnlockSystem();
    return FALSE;
  }

  pHcUsbDrv->Sig = HCUSBDRV_SIG;

  DEBUGMSG(ZONE_USB, (TEXT("HcUsb: Created USB device context %p\r\n"), pHcUsbDrv));

  if (!pHcUsbDrv->UsbHandling.USBDeviceAttach(Device, Funcs, (DWORD)pHcUsbDrv)) {
    DEBUGMSG(ZONE_INIT | ZONE_USB | ZONE_ERROR, (TEXT("HcUsb: -USBDeviceAttach() - Error: Failed to init USB handling\r\n")));
    delete pHcUsbDrv;
    CUsbHandling::UnlockSystem();
    return FALSE;
  }

  *AcceptControl = TRUE;

  CUsbHandling::UnlockSystem();

  DEBUGMSG(ZONE_INIT | ZONE_USB, (TEXT("HcUsb: -USBDeviceAttach()\r\n")));

  return TRUE;
}

//////////////////////////////////////////////////////////////////////////////
//
// This function receives device notifications. Client drivers register this
// function with the USB driver interface to receive device notifications.
// All client drivers are required to process the USB_CLOSE_DEVICE message.
// USB notification messages are passed to the client driver's
// LPDEVICE_NOTIFY_ROUTINE function. The client driver registers its
// notification routine with the LPREGISTER_NOTIFICATION_ROUTINE function.
//
// Parameters:
// lpvNotifyParameter: Device context.
// dwCode            : Set to USB_CLOSE_DEVICE.
// dwInfo1           : Not used.
// dwInfo2           : Not used.
// dwInfo3           : Not used.
// dwInfo4           : Not used.
//
// Return:
// If a client driver processes a message it should return TRUE. If a client
// driver does not process a message it should return FALSE.
//
//////////////////////////////////////////////////////////////////////////////
BOOL WINAPI USBDeviceNotify(LPVOID    lpvNotifyParameter,
                            DWORD     dwCode,
                            LPDWORD*  dwInfo1,
                            LPDWORD*  dwInfo2,
                            LPDWORD*  dwInfo3,
                            LPDWORD*  dwInfo4) {
PHCUSBDRV_CONTEXT pHcUsbDrv;

  DEBUGMSG(ZONE_USB_NOTIFY, (TEXT("HcUsb: +DeviceNotify()\r\n")));

  pHcUsbDrv = (PHCUSBDRV_CONTEXT)lpvNotifyParameter;
  if (!IS_VALID_CONTEXT(pHcUsbDrv)) {
    DEBUGMSG(ZONE_USB | ZONE_ERROR, (TEXT("HcUsb: -DeviceNotify() - Invalid device context\r\n")));
    return FALSE;
  }

  CUsbHandling::LockSystem();

  switch (dwCode) {
    case USB_CLOSE_DEVICE:
      // Delete our USB handling instance to execute the detach process
      delete pHcUsbDrv;
      CUsbHandling::UnlockSystem();
      DEBUGMSG(ZONE_USB_NOTIFY, (TEXT("HcUsb: -DeviceNotify() - Properly closed USB device\r\n")));
      return TRUE;

#if defined(USB_DEVICE_SUSPENDED) && defined(USB_DEVICE_RESUMED)
    case USB_DEVICE_SUSPENDED:
      CUsbHandling::UnlockSystem();
      DEBUGMSG(ZONE_USB_NOTIFY, (TEXT("HcUsb: -DeviceNotify() - Device-Suspended-Notification\r\n")));
      return pHcUsbDrv->UsbHandling.DeviceSuspended();

    case USB_DEVICE_RESUMED:
      CUsbHandling::UnlockSystem();
      DEBUGMSG(ZONE_USB_NOTIFY, (TEXT("HcUsb: -DeviceNotify() - Device-Resumed-Notification\r\n")));
      return pHcUsbDrv->UsbHandling.DeviceResumed();
#endif // USB_DEVICE_SUSPENDED && USB_DEVICE_RESUMED
  }

  CUsbHandling::UnlockSystem();

  DEBUGMSG(ZONE_USB_NOTIFY, (TEXT("HcUsb: -DeviceNotify() - Not our call; dwCode=%d\r\n"),dwCode));

  return FALSE;
}
