/*
 * USB routines of Cypress USB Serial
 * Copyright (C) 2013 Cypress Semiconductor
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include "CyUSBCommon.h"

static bool glDriverInit = false;
static libusb_device **glDeviceList;
static UINT32 glNumDevices;
/*The API initializes the Libusb library
*/
pthread_mutex_t criticalSection;
CY_RETURN_STATUS CyLibraryInit ()
{
    UINT32 rStatus;
    rStatus = libusb_init (NULL);
    
    if (glDriverInit != true){
        if (rStatus != LIBUSB_SUCCESS){
            CY_DEBUG_PRINT_ERROR ("CY:Driver Init Failed ...\n");
            return CY_ERROR_DRIVER_INIT_FAILED;
        }
        glNumDevices = libusb_get_device_list (NULL, &glDeviceList);
        if (glNumDevices < 0){
            CY_DEBUG_PRINT_ERROR ("CY:Building device list Failed ...\n");
            glNumDevices = -1;
            return CY_ERROR_DRIVER_INIT_FAILED;
        }
        pthread_mutex_init (&criticalSection, NULL);
        glDriverInit = true;
        return CY_SUCCESS;
    }
    else{
        CY_DEBUG_PRINT_ERROR ("CY:Error ... library already initialized \n");
        return CY_ERROR_DRIVER_INIT_FAILED;
    }
}
/*
   This API needs to be called after Calling CyGetListofDevices.
 */
CY_RETURN_STATUS CyLibraryExit ()
{
    if (glDriverInit == true){
        if (glNumDevices >= 0)
            libusb_free_device_list (glDeviceList, 1);
        libusb_exit (NULL);
        glDriverInit = false;
        pthread_mutex_destroy (&criticalSection);
        return CY_SUCCESS;
    }
    CY_DEBUG_PRINT_ERROR ("CY:Error ... Library not initialized \n");
    return CY_ERROR_REQUEST_FAILED;
}
/*
 * This function Gets the number of all the devices currently 
 * Connected to the host (It includes Cypress Device as well as 
 * no Cypress Devices connected)
 */
CY_RETURN_STATUS CyGetListofDevices (
        UINT8 *numDevices
        )
{
    // Use this variable to call libusb_close and exit of the application
    if (numDevices == NULL)
        return CY_ERROR_INVALID_PARAMETER;
    if (!glDriverInit){
        CY_DEBUG_PRINT_ERROR ("CY:Error Library not initialised ...function is %s\n", __func__);
        return CY_ERROR_REQUEST_FAILED;
    }
    pthread_mutex_lock (&criticalSection);
    libusb_free_device_list (glDeviceList, 1);
    glNumDevices = (*numDevices) = libusb_get_device_list (NULL, &glDeviceList);
    pthread_mutex_unlock (&criticalSection);
    if (glNumDevices < 0){
        CY_DEBUG_PRINT_ERROR ("CY:Building device list Failed ...function is %s\n", __func__);
        glNumDevices = -1;
        (*numDevices) = -1;
        return CY_ERROR_REQUEST_FAILED;
    }
    return CY_SUCCESS;     
}
/* This function gets all the neccessary info such as VID,PID,
   String Descriptors and if is a cypress serial device you will 
   get the info on class and device type
 */
CY_RETURN_STATUS CyGetDeviceInfo (
        UINT8 deviceNumber,
        CY_DEVICE_INFO *deviceInfo
        )
{
    struct libusb_device_descriptor deviceDesc;
    UINT32 rStatus, numInterfaces;
    UINT8 iManufacturer, iProduct, iSerial;
    libusb_device *usbDevice;;
    struct libusb_config_descriptor *configDesc;
    libusb_device_handle *devHandle;

    // Get the list of descriptor info for the device
    if (glDriverInit == false){
        CY_DEBUG_PRINT_ERROR ("CY:Error Library not initialised ...function is %s\n", __func__);
        return CY_ERROR_REQUEST_FAILED;
    }
    if (deviceInfo == NULL)
        return CY_ERROR_INVALID_PARAMETER;
    pthread_mutex_lock (&criticalSection);
    if (deviceNumber >= glNumDevices){
        CY_DEBUG_PRINT_ERROR ("CY:Error invalid device number... \n");
        pthread_mutex_unlock (&criticalSection);
        return CY_ERROR_INVALID_PARAMETER;    
    }
    usbDevice = glDeviceList[deviceNumber]; 
    rStatus = libusb_get_device_descriptor (usbDevice, &deviceDesc);
    if (rStatus != LIBUSB_SUCCESS){
        CY_DEBUG_PRINT_ERROR ("CY:Error ... unable to retrieve device descriptor \n");
        pthread_mutex_unlock (&criticalSection);
        return CY_ERROR_DEVICE_INFO_FETCH_FAILED;    
    }
    
    deviceInfo->vidPid.vid = deviceDesc.idVendor;
    deviceInfo->vidPid.pid = deviceDesc.idProduct;
    // Get the all the index of the String descriptors so that it can be used 
    // to retrieve the string descriptor info.
    iManufacturer = deviceDesc.iManufacturer;
    iProduct = deviceDesc.iProduct;
    iSerial = deviceDesc.iSerialNumber;
    //Get the Device handle so that we can communicate with the device retreiving 
    // descriptor info
    deviceInfo->manufacturerName[0] = '\0';
    deviceInfo->productName[0] = '\0';
    deviceInfo->serialNum[0] = '\0';
    rStatus = libusb_open (usbDevice, &devHandle);
    if (rStatus == LIBUSB_ERROR_ACCESS){
        CY_DEBUG_PRINT_ERROR ("CY:Error ...Insufficient permission... Libusb error is %d \n", rStatus);
        pthread_mutex_unlock (&criticalSection);
        return CY_ERROR_ACCESS_DENIED;
    }
    else if (rStatus != CY_SUCCESS){
        CY_DEBUG_PRINT_ERROR ("CY:Error in opening the device... Libusb error is %d \n", rStatus);
        pthread_mutex_unlock (&criticalSection);
        return CY_ERROR_DEVICE_INFO_FETCH_FAILED;
    }
    if (iManufacturer > 0){
        rStatus = libusb_get_string_descriptor_ascii (devHandle, iManufacturer, deviceInfo->manufacturerName, 256);
        if (rStatus <= LIBUSB_SUCCESS){
            CY_DEBUG_PRINT_ERROR ("CY:Error in Getting Manufacturer name Error is <%x> \n", rStatus);
        }
    }
    if (iProduct > 0){
        rStatus = libusb_get_string_descriptor_ascii (devHandle, iProduct, deviceInfo->productName, 256);
        if (rStatus <= LIBUSB_SUCCESS){
            CY_DEBUG_PRINT_ERROR ("CY:Error in Getting product name Error is <%d> \n", rStatus);
        }
    }
    if (iSerial > 0){
        rStatus = libusb_get_string_descriptor_ascii (devHandle, iSerial, deviceInfo->serialNum, 256);
        if (rStatus <= LIBUSB_SUCCESS){
            CY_DEBUG_PRINT_ERROR ("CY:Error in Getting Serial name <%d>\n", rStatus);
        }
    }
    rStatus = libusb_get_config_descriptor (usbDevice, 0, &configDesc);
    if (rStatus == LIBUSB_SUCCESS){
        UINT32 index_i = 0;
        const struct libusb_interface *interface;
        numInterfaces = configDesc->bNumInterfaces;
        deviceInfo->numInterfaces = numInterfaces;
        interface = configDesc->interface;
        while ((numInterfaces) && (index_i < CY_MAX_DEVICE_INTERFACE)){
            deviceInfo->deviceClass[index_i] = (CY_DEVICE_CLASS)interface->altsetting->bInterfaceClass;
            if (deviceInfo->deviceClass[index_i] == CY_CLASS_VENDOR){
                deviceInfo->deviceType[index_i] = (CY_DEVICE_CLASS)interface->altsetting->bInterfaceSubClass;
            }
            else
                deviceInfo->deviceType[index_i] = CY_TYPE_DISABLED;
            index_i++;
            numInterfaces--;
            interface++;
        }
        libusb_free_config_descriptor(configDesc);
    }
    else {
        CY_DEBUG_PRINT_ERROR ("CY: Error in Getting config descriptor ...Libusb error is %d \n", rStatus);
        if (devHandle)
            libusb_close (devHandle);
        pthread_mutex_unlock (&criticalSection);    
        return CY_ERROR_DEVICE_INFO_FETCH_FAILED;
    }
    if (devHandle)
        libusb_close (devHandle);
    pthread_mutex_unlock (&criticalSection);
    return CY_SUCCESS;
}
/* This function gets all the neccessary info such as VID,PID,
   String Descriptors and if is a cypress serial device you will 
   get the info on class and device type
 */
CY_RETURN_STATUS CyGetDeviceInfoVidPid (
        CY_VID_PID vidPid,
        UINT8 *deviceNumber,
        PCY_DEVICE_INFO deviceInfoList,
        UINT8 *deviceCount,
        UINT8 infoListLength
        )
{
    struct libusb_device_descriptor deviceDesc;
    UINT32 rStatus = CY_ERROR_DRIVER_INIT_FAILED, numInterfaces, index = 0, devNum;
    uint8_t iManufacturer, iProduct, iSerial;
    libusb_device *usbDevice;
    struct libusb_config_descriptor *configDesc;
    libusb_device_handle *devHandle = NULL;
    PCY_DEVICE_INFO deviceInfo;
    
    if (glDriverInit == false){
        CY_DEBUG_PRINT_ERROR ("CY:Error Library not initialised ...function is %s\n", __func__);
        return CY_ERROR_REQUEST_FAILED;
    }
    if ((infoListLength) < 1){
        CY_DEBUG_PRINT_ERROR ("CY:Error invalid device info list length specified should be > 0 .. function is %s\n", __func__);
        return CY_ERROR_INVALID_PARAMETER;
    }
    if (deviceNumber == NULL || deviceInfoList == NULL || deviceCount == NULL)
        return CY_ERROR_INVALID_PARAMETER;
    // Get the list of descriptor info for the device
    (*deviceCount) = 0;
    pthread_mutex_lock (&criticalSection);
    for (devNum = 0; devNum < glNumDevices; devNum++) {
        //We are making sure that we do not overrun 
        //the list.
        deviceInfo = &(deviceInfoList [index]);
        usbDevice = glDeviceList[devNum];
        rStatus = libusb_get_device_descriptor (usbDevice, &deviceDesc);
        if (rStatus != LIBUSB_SUCCESS){
            CY_DEBUG_PRINT_ERROR ("CY:Error in getting device descriptor for device-%d... Libusb Error is %d \n", devNum, rStatus);
            pthread_mutex_unlock (&criticalSection);
            return CY_ERROR_DEVICE_INFO_FETCH_FAILED; 
        }
        if ((deviceDesc.idVendor != vidPid.vid) || (deviceDesc.idProduct != vidPid.pid)){
            continue;
        }
        (*deviceCount)++;
        if (index > infoListLength){
        	continue;
        }
        rStatus = libusb_open (usbDevice, &devHandle);
        if (rStatus == LIBUSB_ERROR_ACCESS){
            CY_DEBUG_PRINT_ERROR ("CY:Insufficient permission ... Libusb error is %d \n", rStatus);
            pthread_mutex_unlock (&criticalSection);
            return CY_ERROR_ACCESS_DENIED;
        } 
        else if (rStatus != LIBUSB_SUCCESS){
            CY_DEBUG_PRINT_ERROR ("CY:Error in Opening the Device ...Error is %d \n", rStatus);
            pthread_mutex_unlock (&criticalSection);
            return CY_ERROR_DEVICE_INFO_FETCH_FAILED;     
        }
        deviceNumber[index] = devNum;
        index++;
        deviceInfo->vidPid.vid = deviceDesc.idVendor;
        deviceInfo->vidPid.pid = deviceDesc.idProduct;
        // Get all the index of the String descriptors so that it can be used 
        // to retrieve the string descriptor info.
        iManufacturer = deviceDesc.iManufacturer;
        iProduct = deviceDesc.iProduct;
        iSerial = deviceDesc.iSerialNumber;
        //Get the Device handle so that we can communicate with the device retreiving 
        // descriptor info
        //Initialise manufacturer, product and serial names
        deviceInfo->manufacturerName[0] = '\0';
        deviceInfo->productName[0] = '\0';
        deviceInfo->serialNum[0] = '\0';
        if (iManufacturer > 0) {
            rStatus = libusb_get_string_descriptor_ascii (devHandle, iManufacturer, deviceInfo->manufacturerName, 256);
            if (rStatus <= LIBUSB_SUCCESS){
                CY_DEBUG_PRINT_INFO ("CY:Error in Getting Manufacturer name Error is <%d> \n",rStatus);
            }
        }
        if (iProduct > 0){
            rStatus = libusb_get_string_descriptor_ascii (devHandle, iProduct, deviceInfo->productName, 256);
            if (rStatus <= LIBUSB_SUCCESS){
                CY_DEBUG_PRINT_INFO ("CY:Error in Getting product name Error is <%d> \n", rStatus);
            }
        }
        if (iSerial > 0){
            rStatus = libusb_get_string_descriptor_ascii (devHandle, iSerial, deviceInfo->serialNum, 256);
            if (rStatus <= LIBUSB_SUCCESS){
                CY_DEBUG_PRINT_INFO ("CY:Error in Getting Serial name <%d>\n", rStatus);
            }
        }
        CY_DEBUG_PRINT_INFO ("Manufacturer name <%s> \nProduct Name <%s> \nserial number <%s> \n",
                deviceInfo->manufacturerName,deviceInfo->productName,deviceInfo->serialNum);
        rStatus = libusb_get_config_descriptor (usbDevice, 0, &configDesc);
        if (rStatus == LIBUSB_SUCCESS){
            int index_i = 0;
            const struct libusb_interface *interfaceDesc;
            numInterfaces = configDesc->bNumInterfaces;
            deviceInfo->numInterfaces = numInterfaces;
            interfaceDesc = configDesc->interface;
            while ((numInterfaces) && (index_i < CY_MAX_DEVICE_INTERFACE)){
                deviceInfo->deviceClass[index_i] = (CY_DEVICE_CLASS)interfaceDesc->altsetting->bInterfaceClass;
                if (deviceInfo->deviceClass[index_i] == CY_CLASS_VENDOR)
                    deviceInfo->deviceType[index_i] = (CY_DEVICE_CLASS)interfaceDesc->altsetting->bInterfaceSubClass;
                else
                    deviceInfo->deviceType[index_i] = CY_TYPE_DISABLED;

                index_i++;
                numInterfaces--;
                interfaceDesc++;
            }
        }
        else {
            CY_DEBUG_PRINT_ERROR ("CY: Error in Getting config descriptor ... Libusb Error is %d\n", rStatus);
            pthread_mutex_unlock (&criticalSection);
            return CY_ERROR_DEVICE_INFO_FETCH_FAILED; 
        }
        libusb_free_config_descriptor (configDesc);
        libusb_close (devHandle);
    }
    if ((*deviceCount) == 0)
        rStatus = CY_ERROR_DEVICE_NOT_FOUND;
    pthread_mutex_unlock (&criticalSection);
    return rStatus;
}
/*
   This API will claim the interface in the device
   To make sure only claimed application speaks to device.
 */
CY_RETURN_STATUS CySelectInterface (
        CY_HANDLE handle,
        UINT8 interfaceNum
        )
{
    UINT32 rStatus, numEP;
    CY_DEVICE *device; 
    libusb_device_handle *devHandle;
    libusb_device *usbDev;
    struct libusb_config_descriptor *configDesc;
    const struct libusb_interface *interfaceDesc;
    const struct libusb_endpoint_descriptor *epDesc;

    if (handle == NULL)
        return CY_ERROR_INVALID_HANDLE;
    device = (CY_DEVICE *)handle;
    devHandle = device->devHandle;

    usbDev = libusb_get_device (devHandle);
    if (usbDev == NULL){
        CY_DEBUG_PRINT_ERROR ("CY:Error Invalide handle ..function is %s\n", __func__);
        return CY_ERROR_REQUEST_FAILED;
    }
    //Get the config descriptor and parse it to get the 
    //interface and endpoint descriptor 
    rStatus = libusb_get_config_descriptor (usbDev, 0, &configDesc);
    if (rStatus != LIBUSB_SUCCESS){
        CY_DEBUG_PRINT_ERROR ("CY:Error in Getting Config Desc ...function is %s\n", __func__);
        printf("\n rstatus10= %d",rStatus); 
        return CY_ERROR_REQUEST_FAILED;
    }
    interfaceDesc = configDesc->interface;
    //Interface Number should be a valid one and should not exceed
    // total number of interfaces
    if (interfaceNum >= configDesc->bNumInterfaces){
        CY_DEBUG_PRINT_ERROR ("CY:Interface Number not valid... \n");
        libusb_free_config_descriptor (configDesc);
       printf("\n rstatus11= %d",rStatus);
        return CY_ERROR_REQUEST_FAILED;
    }
    if (libusb_kernel_driver_active (devHandle, interfaceNum)){
        CY_DEBUG_PRINT_ERROR ("CY:Kernel driver active on the interface number %d \n", interfaceNum);;
        /*User can uncomment this section if needed.
#ifdef CY_DETACH_KERNEL_DRIVER
        if (!libusb_detach_kernel_driver (devHandle, interfaceNum)){
            CY_DEBUG_PRINT_ERROR ("CY:Kernel driver detach failed %d\n", interfaceNum);
printf("\n rstatus12= %d",rStatus);
            return CY_ERROR_REQUEST_FAILED;
        }
#else
       printf("\n rstatus13= %d",rStatus);
        return CY_ERROR_REQUEST_FAILED;
#endif
    }
    rStatus = libusb_claim_interface (devHandle, interfaceNum);
    if (rStatus != LIBUSB_SUCCESS){
        CY_DEBUG_PRINT_ERROR ("CY:Error in claiming interface -interface num %d... Libusb error is %d \n", interfaceNum, rStatus);
       printf("\n rstatus14= %d",rStatus);
        return CY_ERROR_REQUEST_FAILED;
    }*/
}
    device->interfaceNum = interfaceNum;
    while (interfaceNum--)
        interfaceDesc++;
    
    epDesc = interfaceDesc->altsetting->endpoint;
    numEP = interfaceDesc->altsetting->bNumEndpoints;
    device->numEndpoints = numEP;
    // Check the total number of endpoints interface has
    // and get all the endpoint add
    CY_DEBUG_PRINT_INFO ("CY:Info The total number of endpoints are %d \n", numEP);
    while (numEP){
        if (epDesc->bmAttributes == 0x2){ //Bulk EP checking
            if (epDesc->bEndpointAddress & 0x80)
                device->inEndpoint = epDesc->bEndpointAddress;
            else 
                device->outEndpoint = epDesc->bEndpointAddress;
        }
        else if (epDesc->bmAttributes == 0x3) //Interrupt EP checking (We have only one interrupt EP)
            device->interruptEndpoint = epDesc->bEndpointAddress;
        epDesc++;
        numEP--;     
    }
    CY_DEBUG_PRINT_INFO ("CY:Info The Endpoints are in %d and out %d and interrup %d\n", 
            device->inEndpoint, device->outEndpoint, device->interruptEndpoint);
    libusb_free_config_descriptor (configDesc);
    return CY_SUCCESS;
}
/*
 * This API selects the type of the device
 */
void CySelectDeviceType (CY_DEVICE *device, libusb_device *libUsbdev, unsigned char interfaceNum)
{
    int rStatus, numInterfaces;
    struct libusb_config_descriptor *configDesc;
    int index = 0;
    const struct libusb_interface *interfaceDesc;
    device->deviceType = CY_TYPE_DISABLED;

    rStatus = libusb_get_config_descriptor (libUsbdev, 0, &configDesc);
    if (0 == rStatus){
        interfaceDesc = configDesc->interface;
        numInterfaces = configDesc->bNumInterfaces;
        if (interfaceNum >= numInterfaces)
            return;
        while (index != interfaceNum) {
            index++;
            interfaceDesc++;
        }
        if (interfaceDesc->altsetting->bInterfaceClass == CY_CLASS_VENDOR)
            device->deviceType = (CY_DEVICE_CLASS)interfaceDesc->altsetting->bInterfaceSubClass;
        libusb_free_config_descriptor (configDesc);
    }
    CY_DEBUG_PRINT_INFO ("CY:Info The device type is %d \n", device->deviceType);
}
/*
   The Api Gets the handle for the specified device number
   (refer to usage guide and example for usage)
   and this handle should be called for further communication
   with the device
 */
CY_RETURN_STATUS CyOpen (
        unsigned char deviceNumber,
        unsigned char interfaceNum,
        CY_HANDLE *handle
        )
{
    libusb_device_handle *devHandle;
    libusb_device *dev;
    CY_DEVICE *device;
    UINT32 rStatus;

    if (glDriverInit == false){
        CY_DEBUG_PRINT_ERROR ("CY:Error Library not initialised ...function is %s\n", __func__);
        return CY_ERROR_REQUEST_FAILED;
    }
    pthread_mutex_lock (&criticalSection);
    if (glDriverInit == true){
        if (deviceNumber >= glNumDevices){
            CY_DEBUG_PRINT_ERROR ("CY:Error ... Invalid device number ... \n");
	    pthread_mutex_unlock (&criticalSection);
            return CY_ERROR_INVALID_PARAMETER;
        }
        dev = glDeviceList [deviceNumber];
        rStatus = libusb_open (dev, &devHandle);
        if (rStatus == LIBUSB_ERROR_ACCESS){
            CY_DEBUG_PRINT_ERROR ("CY:Error in opening the device ..Access denied \n");
            handle = NULL; 
            pthread_mutex_unlock (&criticalSection);
            return CY_ERROR_ACCESS_DENIED;     
        }
        if (rStatus != LIBUSB_SUCCESS){
            CY_DEBUG_PRINT_ERROR ("CY:Error in Opening the Device ...Error is %d \n", rStatus);
            printf("rstatus1 %d", rStatus);
            handle = NULL; 
            pthread_mutex_unlock (&criticalSection);
            return CY_ERROR_DRIVER_OPEN_FAILED;     
        }
        device = (CY_DEVICE *)malloc(sizeof (CY_DEVICE));
        if (device == NULL){
            pthread_mutex_unlock (&criticalSection);
            return CY_ERROR_ALLOCATION_FAILED;
        }
        device->devHandle = devHandle;
        (*handle) = device;
        rStatus = CySelectInterface (device, interfaceNum);
        if (rStatus != CY_SUCCESS){
            libusb_close (devHandle);
            free (device);
            pthread_mutex_unlock (&criticalSection);
printf("rstatus2 %d", rStatus);
            return CY_ERROR_DRIVER_OPEN_FAILED;
        }
        CySelectDeviceType (device, dev, interfaceNum);
        if (device->deviceType == CY_TYPE_UART) {
            CyUartSetRts (*handle);
            CyUartSetDtr (*handle);
            if (!CyUartSetHwFlowControl (*handle, CY_UART_FLOW_CONTROL_DISABLE))
                device->uartFlowControlMode = CY_UART_FLOW_CONTROL_DISABLE;
        }
        //initialising structure elements
        device->spiThreadId = (pthread_t)0;
        device->uartThreadId = (pthread_t)0;
        device->spiCancelEvent = false;
        device->uartCancelEvent = false;
        device->spiTransfer = NULL;
        device->uartTransfer = NULL;
        if (pthread_mutex_init (&device->readLock, NULL)){
            CY_DEBUG_PRINT_ERROR ("CY:Error initializing the read mutex .. Function is %s \n", __func__);
            libusb_close (devHandle);
            free (device);
            pthread_mutex_unlock (&criticalSection);
printf("rstatus3 %d", rStatus);
            return CY_ERROR_DRIVER_OPEN_FAILED; 
        }
        if (pthread_mutex_init (&device->writeLock, NULL)){
            CY_DEBUG_PRINT_ERROR ("CY:Error initializing the write mutex .. Function is %s \n", __func__);
            libusb_close (devHandle);
            free (device);
            pthread_mutex_unlock (&criticalSection);
printf("rstatus4 %d", rStatus);
            return CY_ERROR_DRIVER_OPEN_FAILED; 
        }
        if (pthread_mutex_init (&device->notificationLock, NULL)){
            CY_DEBUG_PRINT_ERROR ("CY:Error initializing the write mutex .. Function is %s \n", __func__);
            libusb_close (devHandle);
            free (device);
            pthread_mutex_unlock (&criticalSection);
printf("rstatus5 %d", rStatus);
            return CY_ERROR_DRIVER_OPEN_FAILED; 
        }
        pthread_mutex_unlock (&criticalSection);
        return CY_SUCCESS;
    }
    else{
        CY_DEBUG_PRINT_ERROR ("CY:Error iniitalise library by calling CyLibraryInit()....function is %s\n", __func__);
printf("rstatus6 %d", rStatus);
        return CY_ERROR_DRIVER_OPEN_FAILED;
    }

 
}
/*
   The Api Closes the handle and needs to be called only if CyGetNumDevices
   or CyOpen is called
 */
CY_RETURN_STATUS CyClose (
        CY_HANDLE handle
        )
{
    CY_DEVICE *device;
    libusb_device_handle *devHandle;

    if (handle == NULL)
        return CY_ERROR_INVALID_HANDLE;
    device = (CY_DEVICE *)handle;
    devHandle = device->devHandle;
    if (device->deviceType == CY_TYPE_UART) {
        CyUartClearRts (handle);
        CyUartClearDtr (handle);
        CyUartSetHwFlowControl (handle, CY_UART_FLOW_CONTROL_DISABLE);
    }
    if (glDriverInit == true){
        if (device->deviceType == CY_TYPE_SPI || device->deviceType == CY_TYPE_UART){
            if (device->spiThreadId != 0 || device->uartThreadId != 0){
            	CyAbortEventNotification(handle);
            }
        }
        if (pthread_mutex_destroy (&device->readLock)){
            CY_DEBUG_PRINT_ERROR ("CY:Error de initializing the read mutex .. Function is %s \n", __func__);
            return CY_ERROR_REQUEST_FAILED; 
        }
        if (pthread_mutex_destroy (&device->writeLock)){
            CY_DEBUG_PRINT_ERROR ("CY:Error de initializing the write mutex .. Function is %s \n", __func__);
            return CY_ERROR_REQUEST_FAILED; 
        }
        if (pthread_mutex_destroy (&device->notificationLock)){
            CY_DEBUG_PRINT_ERROR ("CY:Error de initializing the write mutex .. Function is %s \n", __func__);
            return CY_ERROR_REQUEST_FAILED; 
        }
        libusb_close ((libusb_device_handle*)devHandle);
        free (device);
    }
    return CY_SUCCESS;
}
/*
   This Api will reset the pipe and clears the endpoint
 */
CY_RETURN_STATUS CyResetPipe (
        CY_HANDLE handle,
        UINT8 endPointAddress
        )
{
    UINT32 rStatus;
    CY_DEVICE *device;
    libusb_device_handle *devHandle;

    if (handle == NULL)
        return CY_ERROR_INVALID_HANDLE;
    device = (CY_DEVICE *)handle;
    devHandle = device->devHandle;

    rStatus = libusb_clear_halt ((libusb_device_handle *)devHandle, endPointAddress);
    if (rStatus != LIBUSB_SUCCESS){
        CY_DEBUG_PRINT_ERROR ("CY:Error in resetting the pipe ... \n");
        return CY_ERROR_REQUEST_FAILED;
    }
    return CY_SUCCESS;
}   
/*
   This Api will get the library version,patch
   and build number 
 */
CY_RETURN_STATUS CyGetLibraryVersion (
        CY_HANDLE handle,
        PCY_LIBRARY_VERSION version
        )
{
    version->majorVersion = CY_US_VERSION_MAJOR;
    version->minorVersion = CY_US_VERSION_MINOR;
    version->patch = CY_US_VERSION_PATCH;
    version->buildNumber = CY_US_VERSION_BUILD;
    return CY_SUCCESS;
}
