/*
 * Copyright (C) 2006 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/** \file
  This file consists of implementation of rotines that are exported
  from this DLL.
*/

#include "stdafx.h"
#include "adb_api.h"
#include "adb_object_handle.h"
#include "adb_interface_enum.h"
#include "adb_interface.h"
#include "adb_legacy_interface.h"
#include "adb_endpoint_object.h"
#include "adb_io_completion.h"
#include "adb_helper_routines.h"
#include "adb_winusb_api.h"

/** \brief Points to InstantiateWinUsbInterface exported from AdbWinUsbApi.dll.

  This variable is initialized with the actual address in DllMain routine for
  this DLL on DLL_PROCESS_ATTACH event.
  @see PFN_INSTWINUSBINTERFACE for more information.
*/
PFN_INSTWINUSBINTERFACE InstantiateWinUsbInterface = NULL;

ADBAPIHANDLE __cdecl AdbEnumInterfaces(GUID class_id,
                               bool exclude_not_present,
                               bool exclude_removed,
                               bool active_only) {
  AdbInterfaceEnumObject* enum_obj = NULL;
  ADBAPIHANDLE ret = NULL;

  try {
    // Instantiate and initialize enum object
    enum_obj = new AdbInterfaceEnumObject();

    if (enum_obj->InitializeEnum(class_id,
                                 exclude_not_present,
                                 exclude_removed,
                                 active_only)) {
      // After successful initialization we can create handle.
      ret = enum_obj->CreateHandle();
    }
  } catch (...) {
    SetLastError(ERROR_OUTOFMEMORY);
  }

  if (NULL != enum_obj)
    enum_obj->Release();

  return ret;
}

bool __cdecl AdbNextInterface(ADBAPIHANDLE adb_handle,
                      AdbInterfaceInfo* info,
                      unsigned long* size) {
  if (NULL == size) {
    SetLastError(ERROR_INVALID_PARAMETER);
    return false;
  }

  // Lookup AdbInterfaceEnumObject object for the handle
  AdbInterfaceEnumObject* adb_ienum_object =
    LookupObject<AdbInterfaceEnumObject>(adb_handle);
  if (NULL == adb_ienum_object)
    return false;

  // Everything is verified. Pass it down to the object
  bool ret = adb_ienum_object->Next(info, size);

  adb_ienum_object->Release();

  return ret;
}

bool __cdecl AdbResetInterfaceEnum(ADBAPIHANDLE adb_handle) {
  // Lookup AdbInterfaceEnumObject object for the handle
  AdbInterfaceEnumObject* adb_ienum_object =
    LookupObject<AdbInterfaceEnumObject>(adb_handle);
  if (NULL == adb_ienum_object)
    return false;

  // Everything is verified. Pass it down to the object
  bool ret = adb_ienum_object->Reset();

  adb_ienum_object->Release();

  return ret;
}

ADBAPIHANDLE __cdecl AdbCreateInterfaceByName(
    const wchar_t* interface_name) {
  AdbInterfaceObject* obj = NULL;
  ADBAPIHANDLE ret = NULL;

  try {
    // Instantiate interface object, depending on the USB driver type.
    if (IsLegacyInterface(interface_name)) {
      // We have legacy USB driver underneath us.
      obj = new AdbLegacyInterfaceObject(interface_name);
    } else {
      // We have WinUsb driver underneath us. Make sure that AdbWinUsbApi.dll
      // is loaded and its InstantiateWinUsbInterface routine address has
      // been cached.
      if (NULL != InstantiateWinUsbInterface) {
        obj = InstantiateWinUsbInterface(interface_name);
        if (NULL == obj) {
          return NULL;
        }
      } else {
        return NULL;
      }
    }

    // Create handle for it
    ret = obj->CreateHandle();
  } catch (...) {
    SetLastError(ERROR_OUTOFMEMORY);
  }

  if (NULL != obj)
    obj->Release();

  return ret;
}

ADBAPIHANDLE __cdecl AdbCreateInterface(GUID class_id,
                                unsigned short vendor_id,
                                unsigned short product_id,
                                unsigned char interface_id) {
  // Enumerate all active interfaces for the given class
  AdbEnumInterfaceArray interfaces;

  if (!EnumerateDeviceInterfaces(class_id,
                                 DIGCF_DEVICEINTERFACE | DIGCF_PRESENT,
                                 true,
                                 true,
                                 &interfaces)) {
    return NULL;
  }

  if (interfaces.empty()) {
    SetLastError(ERROR_DEVICE_NOT_AVAILABLE);
    return NULL;
  }

  // Now iterate over active interfaces looking for the name match.
  // The name is formatted as such:
  // "\\\\?\\usb#vid_xxxx&pid_xxxx&mi_xx#123456789abcdef#{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}"
  // where
  //    vid_xxxx is for the vendor id (xxxx are hex for the given vendor id),
  //    pid_xxxx is for the product id (xxxx are hex for the given product id)
  //    mi_xx is for the interface id  (xx are hex for the given interface id)
  // EnumerateDeviceInterfaces will guarantee that returned interface names
  // will have our class id at the end of the name (those last XXXes in the
  // format). So, we only need to match the beginning of the name
  wchar_t match_name[64];
  if (0xFF == interface_id) {
    // No interface id for the name.
    swprintf(match_name, L"\\\\?\\usb#vid_%04x&pid_%04x#",
             vendor_id, product_id);
  } else {
    // With interface id for the name.
    swprintf(match_name, L"\\\\?\\usb#vid_%04x&pid_%04x&mi_%02x#",
             vendor_id, product_id, interface_id);
  }
  size_t match_len = wcslen(match_name);

  for (AdbEnumInterfaceArray::iterator it = interfaces.begin();
       it != interfaces.end(); it++) {
    const AdbInstanceEnumEntry& next_interface = *it;
    if (0 == _wcsnicmp(match_name,
                      next_interface.device_name().c_str(),
                      match_len)) {
      // Found requested interface among active interfaces.
      return AdbCreateInterfaceByName(next_interface.device_name().c_str());
    }
  }

  SetLastError(ERROR_DEVICE_NOT_AVAILABLE);
  return NULL;
}

bool __cdecl AdbGetInterfaceName(ADBAPIHANDLE adb_interface,
                         void* buffer,
                         unsigned long* buffer_char_size,
                         bool ansi) {
  // Lookup interface object for the handle
  AdbInterfaceObject* adb_object =
    LookupObject<AdbInterfaceObject>(adb_interface);

  if (NULL != adb_object) {
    // Dispatch call to the found object
    bool ret = adb_object->GetInterfaceName(buffer, buffer_char_size, ansi);
    adb_object->Release();
    return ret;
  } else {
    SetLastError(ERROR_INVALID_HANDLE);
    return false;
  }
}

bool __cdecl AdbGetSerialNumber(ADBAPIHANDLE adb_interface,
                        void* buffer,
                        unsigned long* buffer_char_size,
                        bool ansi) {
  // Lookup interface object for the handle
  AdbInterfaceObject* adb_object =
    LookupObject<AdbInterfaceObject>(adb_interface);

  if (NULL != adb_object) {
    // Dispatch call to the found object
    bool ret = adb_object->GetSerialNumber(buffer, buffer_char_size, ansi);
    adb_object->Release();
    return ret;
  } else {
    SetLastError(ERROR_INVALID_HANDLE);
    return false;
  }
}

bool __cdecl AdbGetUsbDeviceDescriptor(ADBAPIHANDLE adb_interface,
                               USB_DEVICE_DESCRIPTOR* desc) {
  // Lookup interface object for the handle
  AdbInterfaceObject* adb_object =
    LookupObject<AdbInterfaceObject>(adb_interface);

  if (NULL != adb_object) {
    // Dispatch close to the found object
    bool ret = adb_object->GetUsbDeviceDescriptor(desc);
    adb_object->Release();
    return ret;
  } else {
    SetLastError(ERROR_INVALID_HANDLE);
    return false;
  }
}

bool __cdecl AdbGetUsbConfigurationDescriptor(ADBAPIHANDLE adb_interface,
                                      USB_CONFIGURATION_DESCRIPTOR* desc) {
  // Lookup interface object for the handle
  AdbInterfaceObject* adb_object =
    LookupObject<AdbInterfaceObject>(adb_interface);

  if (NULL != adb_object) {
    // Dispatch close to the found object
    bool ret = adb_object->GetUsbConfigurationDescriptor(desc);
    adb_object->Release();
    return ret;
  } else {
    SetLastError(ERROR_INVALID_HANDLE);
    return false;
  }
}

bool __cdecl AdbGetUsbInterfaceDescriptor(ADBAPIHANDLE adb_interface,
                                  USB_INTERFACE_DESCRIPTOR* desc) {
  // Lookup interface object for the handle
  AdbInterfaceObject* adb_object =
    LookupObject<AdbInterfaceObject>(adb_interface);

  if (NULL != adb_object) {
    // Dispatch close to the found object
    bool ret = adb_object->GetUsbInterfaceDescriptor(desc);
    adb_object->Release();
    return ret;
  } else {
    SetLastError(ERROR_INVALID_HANDLE);
    return false;
  }
}

bool __cdecl AdbGetEndpointInformation(ADBAPIHANDLE adb_interface,
                               UCHAR endpoint_index,
                               AdbEndpointInformation* info) {
  // Lookup interface object for the handle
  AdbInterfaceObject* adb_object =
    LookupObject<AdbInterfaceObject>(adb_interface);

  if (NULL != adb_object) {
    // Dispatch close to the found object
    bool ret = adb_object->GetEndpointInformation(endpoint_index, info);
    adb_object->Release();
    return ret;
  } else {
    SetLastError(ERROR_INVALID_HANDLE);
    return false;
  }
}

bool __cdecl AdbGetDefaultBulkReadEndpointInformation(ADBAPIHANDLE adb_interface,
                                              AdbEndpointInformation* info) {
  return AdbGetEndpointInformation(adb_interface,
                                   ADB_QUERY_BULK_READ_ENDPOINT_INDEX,
                                   info);
}

bool __cdecl AdbGetDefaultBulkWriteEndpointInformation(ADBAPIHANDLE adb_interface,
                                               AdbEndpointInformation* info) {
  return AdbGetEndpointInformation(adb_interface,
                                   ADB_QUERY_BULK_WRITE_ENDPOINT_INDEX,
                                   info);
}

ADBAPIHANDLE __cdecl AdbOpenEndpoint(ADBAPIHANDLE adb_interface,
                             unsigned char endpoint_index,
                             AdbOpenAccessType access_type,
                             AdbOpenSharingMode sharing_mode) {
  // Lookup interface object for the handle
  AdbInterfaceObject* adb_object =
    LookupObject<AdbInterfaceObject>(adb_interface);

  if (NULL != adb_object) {
    // Dispatch close to the found object
    ADBAPIHANDLE ret =
      adb_object->OpenEndpoint(endpoint_index, access_type, sharing_mode);
    adb_object->Release();
    return ret;
  } else {
    SetLastError(ERROR_INVALID_HANDLE);
    return NULL;
  }
}

ADBAPIHANDLE __cdecl AdbOpenDefaultBulkReadEndpoint(ADBAPIHANDLE adb_interface,
                                            AdbOpenAccessType access_type,
                                            AdbOpenSharingMode sharing_mode) {
  return AdbOpenEndpoint(adb_interface,
                         ADB_QUERY_BULK_READ_ENDPOINT_INDEX,
                         access_type,
                         sharing_mode);
}

ADBAPIHANDLE __cdecl AdbOpenDefaultBulkWriteEndpoint(ADBAPIHANDLE adb_interface,
                                             AdbOpenAccessType access_type,
                                             AdbOpenSharingMode sharing_mode) {
  return AdbOpenEndpoint(adb_interface,
                         ADB_QUERY_BULK_WRITE_ENDPOINT_INDEX,
                         access_type,
                         sharing_mode);
}

ADBAPIHANDLE __cdecl AdbGetEndpointInterface(ADBAPIHANDLE adb_endpoint) {
  // Lookup endpoint object for the handle
  AdbEndpointObject* adb_object =
    LookupObject<AdbEndpointObject>(adb_endpoint);

  if (NULL != adb_object) {
    // Dispatch the call to the found object
    ADBAPIHANDLE ret = adb_object->GetParentInterfaceHandle();
    adb_object->Release();
    return ret;
  } else {
    SetLastError(ERROR_INVALID_HANDLE);
    return NULL;
  }
}

bool __cdecl AdbQueryInformationEndpoint(ADBAPIHANDLE adb_endpoint,
                                 AdbEndpointInformation* info) {
  // Lookup endpoint object for the handle
  AdbEndpointObject* adb_object =
    LookupObject<AdbEndpointObject>(adb_endpoint);

  if (NULL != adb_object) {
    // Dispatch the call to the found object
    bool ret = adb_object->GetEndpointInformation(info);
    adb_object->Release();
    return ret;
  } else {
    SetLastError(ERROR_INVALID_HANDLE);
    return false;
  }
}

ADBAPIHANDLE __cdecl AdbReadEndpointAsync(ADBAPIHANDLE adb_endpoint,
                                  void* buffer,
                                  unsigned long bytes_to_read,
                                  unsigned long* bytes_read,
                                  unsigned long time_out,
                                  HANDLE event_handle) {
  // Lookup endpoint object for the handle
  AdbEndpointObject* adb_object =
    LookupObject<AdbEndpointObject>(adb_endpoint);

  if (NULL != adb_object) {
    // Dispatch the call to the found object
    ADBAPIHANDLE ret = adb_object->AsyncRead(buffer,
                                             bytes_to_read,
                                             bytes_read,
                                             event_handle,
                                             time_out);
    adb_object->Release();
    return ret;
  } else {
    SetLastError(ERROR_INVALID_HANDLE);
    return NULL;
  }
}

ADBAPIHANDLE __cdecl AdbWriteEndpointAsync(ADBAPIHANDLE adb_endpoint,
                                   void* buffer,
                                   unsigned long bytes_to_write,
                                   unsigned long* bytes_written,
                                   unsigned long time_out,
                                   HANDLE event_handle) {
  // Lookup endpoint object for the handle
  AdbEndpointObject* adb_object =
    LookupObject<AdbEndpointObject>(adb_endpoint);

  if (NULL != adb_object) {
    // Dispatch the call to the found object
    ADBAPIHANDLE ret = adb_object->AsyncWrite(buffer,
                                              bytes_to_write,
                                              bytes_written,
                                              event_handle,
                                              time_out);
    adb_object->Release();
    return ret;
  } else {
    SetLastError(ERROR_INVALID_HANDLE);
    return false;
  }
}

bool __cdecl AdbReadEndpointSync(ADBAPIHANDLE adb_endpoint,
                         void* buffer,
                         unsigned long bytes_to_read,
                         unsigned long* bytes_read,
                         unsigned long time_out) {
  // Lookup endpoint object for the handle
  AdbEndpointObject* adb_object =
    LookupObject<AdbEndpointObject>(adb_endpoint);

  if (NULL != adb_object) {
    // Dispatch the call to the found object
    bool ret =
      adb_object->SyncRead(buffer, bytes_to_read, bytes_read, time_out);
    adb_object->Release();
    return ret;
  } else {
    SetLastError(ERROR_INVALID_HANDLE);
    return NULL;
  }
}

bool __cdecl AdbWriteEndpointSync(ADBAPIHANDLE adb_endpoint,
                          void* buffer,
                          unsigned long bytes_to_write,
                          unsigned long* bytes_written,
                          unsigned long time_out) {
  // Lookup endpoint object for the handle
  AdbEndpointObject* adb_object =
    LookupObject<AdbEndpointObject>(adb_endpoint);

  if (NULL != adb_object) {
    // Dispatch the call to the found object
    bool ret =
      adb_object->SyncWrite(buffer, bytes_to_write, bytes_written, time_out);
    adb_object->Release();
    return ret;
  } else {
    SetLastError(ERROR_INVALID_HANDLE);
    return false;
  }
}

bool __cdecl AdbGetOvelappedIoResult(ADBAPIHANDLE adb_io_completion,
                             LPOVERLAPPED overlapped,
                             unsigned long* bytes_transferred,
                             bool wait) {
  // Lookup endpoint object for the handle
  AdbIOCompletion* adb_object =
    LookupObject<AdbIOCompletion>(adb_io_completion);

  if (NULL != adb_object) {
    // Dispatch the call to the found object
    bool ret =
      adb_object->GetOvelappedIoResult(overlapped, bytes_transferred, wait);
    adb_object->Release();
    return ret;
  } else {
    SetLastError(ERROR_INVALID_HANDLE);
    return false;
  }
}

bool __cdecl AdbHasOvelappedIoComplated(ADBAPIHANDLE adb_io_completion) {
  // Lookup endpoint object for the handle
  AdbIOCompletion* adb_object =
    LookupObject<AdbIOCompletion>(adb_io_completion);

  if (NULL != adb_object) {
    // Dispatch the call to the found object
    bool ret =
      adb_object->IsCompleted();
    adb_object->Release();
    return ret;
  } else {
    SetLastError(ERROR_INVALID_HANDLE);
    return true;
  }
}

bool __cdecl AdbCloseHandle(ADBAPIHANDLE adb_handle) {
  // Lookup object for the handle
  AdbObjectHandle* adb_object = AdbObjectHandle::Lookup(adb_handle);

  if (NULL != adb_object) {
    // Dispatch close to the found object
    bool ret = adb_object->CloseHandle();
    adb_object->Release();
    return ret;
  } else {
    SetLastError(ERROR_INVALID_HANDLE);
    return false;
  }
}
