/*
 * usb_pnp_notify.c
 *
 * usb pnp notify adapter of linux
 *
 * Copyright (c) 2020-2021 Huawei Device Co., Ltd.
 *
 * This software is licensed under the terms of the GNU General Public
 * License version 2, as published by the Free Software Foundation, and
 * may be copied, distributed, and modified under those terms.
 *
 * This program 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 General Public License for more details.
 *
 */

#include "usb_pnp_notify.h"
#include <linux/delay.h>
#include <linux/kthread.h>
#include <linux/notifier.h>
#include <linux/usb.h>
#include "hdf_device_desc.h"
#include "hdf_log.h"
#include "osal_mem.h"
#include "securec.h"

#define HDF_LOG_TAG USB_PNP_NOTIFY
#ifndef USB_GADGET_ADD
#define USB_GADGET_ADD 0x0005
#endif
#ifndef USB_GADGET_REMOVE
#define USB_GADGET_REMOVE 0x0006
#endif

static wait_queue_head_t g_usbPnpNotifyReportWait;
static wait_queue_head_t g_gadgetPnpNotifyReportWait;
static struct task_struct *g_usbPnpNotifyReportThread = NULL;
static struct task_struct *g_gadgetPnpNotifyReportThread = NULL;
static enum UsbPnpNotifyServiceCmd g_usbPnpNotifyCmdType = USB_PNP_NOTIFY_ADD_INTERFACE;
static enum UsbPnpNotifyRemoveType g_usbPnpNotifyRemoveType = USB_PNP_NOTIFY_REMOVE_BUS_DEV_NUM;
static uint8_t g_gadgetPnpNotifyType = 0;
static uint64_t g_preAcion = 0;
struct OsalMutex g_usbSendEventLock;
struct OsalMutex g_gadgetSendEventLock;
struct usb_device *g_usbDevice = NULL;
struct UsbPnpAddRemoveInfo g_usbPnpInfo;
struct DListHead g_usbPnpInfoListHead;
#if USB_PNP_NOTIFY_TEST_MODE == true
struct UsbPnpNotifyMatchInfoTable *g_testUsbPnpInfo = NULL;
#endif

static struct UsbPnpDeviceInfo *UsbPnpNotifyCreateInfo(void)
{
    struct UsbPnpDeviceInfo *infoTemp = NULL;
    static int32_t idNum = 1;
    int32_t ret;

    infoTemp = (struct UsbPnpDeviceInfo *)OsalMemCalloc(sizeof(struct UsbPnpDeviceInfo));
    if (infoTemp == NULL) {
        HDF_LOGE("%s:%d OsalMemAlloc failed", __func__, __LINE__);
        return NULL;
    }

    if (idNum == INT32_MAX) {
        idNum = 1;
    }
    infoTemp->id = idNum;
    OsalMutexInit(&infoTemp->lock);
    infoTemp->status = USB_PNP_DEVICE_INIT_STATUS;
    DListHeadInit(&infoTemp->list);
    DListInsertTail(&infoTemp->list, &g_usbPnpInfoListHead);
    idNum++;

    return infoTemp;
}

static struct UsbPnpDeviceInfo *UsbPnpNotifyFindInfo(struct UsbInfoQueryPara queryPara)
{
    struct UsbPnpDeviceInfo *infoPos = NULL;
    struct UsbPnpDeviceInfo *infoTemp = NULL;
    bool findFlag = false;

    if (DListIsEmpty(&g_usbPnpInfoListHead)) {
        HDF_LOGE("%s:%d usb pnp list head is empty.", __func__, __LINE__);
        return NULL;
    }

    DLIST_FOR_EACH_ENTRY_SAFE(infoPos, infoTemp, &g_usbPnpInfoListHead, struct UsbPnpDeviceInfo, list) {
        switch (queryPara.type) {
            case USB_INFO_NORMAL_TYPE:
                if ((infoPos->info.devNum == queryPara.devNum) && (infoPos->info.busNum == queryPara.busNum)) {
                    findFlag = true;
                }
                break;
            case USB_INFO_ID_TYPE:
                if (infoPos->id == queryPara.id) {
                    findFlag = true;
                }
                break;
            case USB_INFO_DEVICE_ADDRESS_TYPE:
                if (infoPos->info.usbDevAddr == queryPara.usbDevAddr) {
                    findFlag = true;
                }
                break;
            default:
                break;
        }

        if (findFlag) {
            break;
        }
    }

    if (!findFlag) {
        HDF_LOGE("%s:%d the usb pnp info to be find does not exist.", __func__, __LINE__);
        return NULL;
    }
    return infoPos;
}

static HDF_STATUS UsbPnpNotifyDestroyInfo(struct UsbPnpDeviceInfo *deviceInfo)
{
    HDF_STATUS ret = HDF_SUCCESS;
    struct UsbPnpDeviceInfo *infoPos = NULL;
    struct UsbPnpDeviceInfo *infoTemp = NULL;
    bool findFlag = false;

    if (deviceInfo == NULL) {
        ret = HDF_FAILURE;
        HDF_LOGE("%s:%d the deviceInfo is NULL, ret=%d ", __func__, __LINE__, ret);
        return ret;
    }

    if (DListIsEmpty(&g_usbPnpInfoListHead)) {
        HDF_LOGI("%s:%d the g_usbPnpInfoListHead is empty.", __func__, __LINE__);
        return HDF_SUCCESS;
    }

    DLIST_FOR_EACH_ENTRY_SAFE(infoPos, infoTemp, &g_usbPnpInfoListHead, struct UsbPnpDeviceInfo, list) {
        if (infoPos->id == deviceInfo->id) {
            findFlag = true;
            DListRemove(&infoPos->list);
            OsalMemFree((void *)infoPos);
            infoPos = NULL;
            break;
        }
    }

    if (!findFlag) {
        ret = HDF_FAILURE;
        HDF_LOGE("%s:%d the deviceInfoto be destroyed does not exist, ret=%d ", __func__, __LINE__, ret);
    }

    return ret;
}

static int32_t UsbPnpNotifyAddInitInfo(struct UsbPnpDeviceInfo *deviceInfo, union UsbPnpDeviceInfoData infoData)
{
    int32_t ret = HDF_SUCCESS;
    uint8_t i;

    deviceInfo->info.usbDevAddr = (uint64_t)infoData.usbDev;
    deviceInfo->info.devNum = infoData.usbDev->devnum;
    if (infoData.usbDev->bus == NULL) {
        HDF_LOGE("%s infoData.usbDev->bus is NULL", __func__);
        ret = HDF_ERR_INVALID_PARAM;
        goto OUT;
    }
    deviceInfo->info.busNum = infoData.usbDev->bus->busnum;

    deviceInfo->info.deviceInfo.vendorId = le16_to_cpu(infoData.usbDev->descriptor.idVendor);
    deviceInfo->info.deviceInfo.productId = le16_to_cpu(infoData.usbDev->descriptor.idProduct);
    deviceInfo->info.deviceInfo.bcdDeviceLow = le16_to_cpu(infoData.usbDev->descriptor.bcdDevice);
    deviceInfo->info.deviceInfo.bcdDeviceHigh = le16_to_cpu(infoData.usbDev->descriptor.bcdDevice);
    deviceInfo->info.deviceInfo.deviceClass = infoData.usbDev->descriptor.bDeviceClass;
    deviceInfo->info.deviceInfo.deviceSubClass = infoData.usbDev->descriptor.bDeviceSubClass;
    deviceInfo->info.deviceInfo.deviceProtocol = infoData.usbDev->descriptor.bDeviceProtocol;

    if (infoData.usbDev->actconfig == NULL) {
        HDF_LOGE("%s infoData.usbDev->actconfig is NULL", __func__);
        ret = HDF_ERR_INVALID_PARAM;
        goto OUT;
    }
    deviceInfo->info.numInfos = infoData.usbDev->actconfig->desc.bNumInterfaces;
    for (i = 0; i < deviceInfo->info.numInfos; i++) {
        if ((infoData.usbDev->actconfig->interface[i] == NULL) ||
            (infoData.usbDev->actconfig->interface[i]->cur_altsetting == NULL)) {
            HDF_LOGE("%{public}s interface[%{public}hhu] or interface[%{public}hhu]->cur_altsetting is NULL",
                __func__, i, i);
            ret = HDF_ERR_INVALID_PARAM;
            goto OUT;
        }
        deviceInfo->info.interfaceInfo[i].interfaceClass =
        infoData.usbDev->actconfig->interface[i]->cur_altsetting->desc.bInterfaceClass;
        deviceInfo->info.interfaceInfo[i].interfaceSubClass =
        infoData.usbDev->actconfig->interface[i]->cur_altsetting->desc.bInterfaceSubClass;
        deviceInfo->info.interfaceInfo[i].interfaceProtocol =
        infoData.usbDev->actconfig->interface[i]->cur_altsetting->desc.bInterfaceProtocol;
        deviceInfo->info.interfaceInfo[i].interfaceNumber =
        infoData.usbDev->actconfig->interface[i]->cur_altsetting->desc.bInterfaceNumber;

        HDF_LOGI("%s:%d i=%d, interfaceInfo=0x%x-0x%x-0x%x-0x%x",
            __func__, __LINE__, i, deviceInfo->info.interfaceInfo[i].interfaceClass,
            deviceInfo->info.interfaceInfo[i].interfaceSubClass,
            deviceInfo->info.interfaceInfo[i].interfaceProtocol,
            deviceInfo->info.interfaceInfo[i].interfaceNumber);
    }

OUT:
    return ret;
}

static void UsbPnpNotifyAddInterfaceInitInfo(struct UsbPnpDeviceInfo *deviceInfo,
    union UsbPnpDeviceInfoData infoData, struct UsbPnpNotifyMatchInfoTable *infoTable)
{
    uint8_t i;
    uint8_t j;

    for (i = 0; i < deviceInfo->info.numInfos; i++) {
        if ((infoData.infoData->interfaceClass == deviceInfo->info.interfaceInfo[i].interfaceClass)
            && (infoData.infoData->interfaceSubClass == deviceInfo->info.interfaceInfo[i].interfaceSubClass)
            && (infoData.infoData->interfaceProtocol == deviceInfo->info.interfaceInfo[i].interfaceProtocol)
            && (infoData.infoData->interfaceNumber == deviceInfo->info.interfaceInfo[i].interfaceNumber)) {
            if (g_usbPnpNotifyCmdType == USB_PNP_NOTIFY_REMOVE_INTERFACE) {
                deviceInfo->interfaceRemoveStatus[i] = true;
            } else if (g_usbPnpNotifyCmdType == USB_PNP_NOTIFY_ADD_INTERFACE) {
                deviceInfo->interfaceRemoveStatus[i] = false;
            }
        }
    }

    if (g_usbPnpNotifyCmdType == USB_PNP_NOTIFY_REMOVE_INTERFACE) {
        infoTable->numInfos = 1;
        infoTable->interfaceInfo[0].interfaceClass = infoData.infoData->interfaceClass;
        infoTable->interfaceInfo[0].interfaceSubClass = infoData.infoData->interfaceSubClass;
        infoTable->interfaceInfo[0].interfaceProtocol = infoData.infoData->interfaceProtocol;
        infoTable->interfaceInfo[0].interfaceNumber = infoData.infoData->interfaceNumber;
    } else {
        for (i = 0, j = 0; i < deviceInfo->info.numInfos; i++) {
            if (deviceInfo->interfaceRemoveStatus[i] == true) {
                HDF_LOGI("%s:%d j=%hhu deviceInfo->interfaceRemoveStatus[%hhu] is true!", __func__, __LINE__, j, i);
                continue;
            }
            infoTable->interfaceInfo[j].interfaceClass = deviceInfo->info.interfaceInfo[i].interfaceClass;
            infoTable->interfaceInfo[j].interfaceSubClass = deviceInfo->info.interfaceInfo[i].interfaceSubClass;
            infoTable->interfaceInfo[j].interfaceProtocol = deviceInfo->info.interfaceInfo[i].interfaceProtocol;
            infoTable->interfaceInfo[j].interfaceNumber = deviceInfo->info.interfaceInfo[i].interfaceNumber;
            j++;

            HDF_LOGI("%s:%d i=%hhu, j=%hhu, interfaceInfo=0x%x-0x%x-0x%x-0x%x",
                __func__, __LINE__, i, j - 1, infoTable->interfaceInfo[j - 1].interfaceClass,
                infoTable->interfaceInfo[j - 1].interfaceSubClass,
                infoTable->interfaceInfo[j - 1].interfaceProtocol,
                infoTable->interfaceInfo[j - 1].interfaceNumber);
        }
        infoTable->numInfos = j;
    }
}

static int32_t UsbPnpNotifyInitInfo(
    struct HdfSBuf *sbuf, struct UsbPnpDeviceInfo *deviceInfo, union UsbPnpDeviceInfoData infoData)
{
    int32_t ret = HDF_SUCCESS;
    const void *data = NULL;

    if ((g_usbPnpNotifyCmdType == USB_PNP_NOTIFY_ADD_INTERFACE) ||
        (g_usbPnpNotifyCmdType == USB_PNP_NOTIFY_REMOVE_INTERFACE)) {
        static struct UsbPnpNotifyMatchInfoTable infoTable;

        infoTable.usbDevAddr = deviceInfo->info.usbDevAddr;
        infoTable.devNum = deviceInfo->info.devNum;
        infoTable.busNum = deviceInfo->info.busNum;
        infoTable.deviceInfo.vendorId = deviceInfo->info.deviceInfo.vendorId;
        infoTable.deviceInfo.productId = deviceInfo->info.deviceInfo.productId;
        infoTable.deviceInfo.bcdDeviceLow = deviceInfo->info.deviceInfo.bcdDeviceLow;
        infoTable.deviceInfo.bcdDeviceHigh = deviceInfo->info.deviceInfo.bcdDeviceHigh;
        infoTable.deviceInfo.deviceClass = deviceInfo->info.deviceInfo.deviceClass;
        infoTable.deviceInfo.deviceSubClass = deviceInfo->info.deviceInfo.deviceSubClass;
        infoTable.deviceInfo.deviceProtocol = deviceInfo->info.deviceInfo.deviceProtocol;
        infoTable.removeType = g_usbPnpNotifyRemoveType;

        UsbPnpNotifyAddInterfaceInitInfo(deviceInfo, infoData, &infoTable);

        data = (const void *)(&infoTable);
    } else if (g_usbPnpNotifyCmdType == USB_PNP_NOTIFY_REPORT_INTERFACE) {
        ret = UsbPnpNotifyAddInitInfo(deviceInfo, infoData);
        if (ret != HDF_SUCCESS) {
            goto OUT;
        }

        data = (const void *)(&deviceInfo->info);
    } else {
        data = (const void *)(&deviceInfo->info);
    }

    if (!HdfSbufWriteBuffer(sbuf, data, sizeof(struct UsbPnpNotifyMatchInfoTable))) {
        HDF_LOGE("%s:%d sbuf write data failed", __func__, __LINE__);
        ret = HDF_FAILURE;
        goto OUT;
    }

OUT:
    return ret;
}

static int32_t UsbPnpNotifyGetDeviceInfo(void *eventData, union UsbPnpDeviceInfoData *pnpInfoData,
    struct UsbPnpDeviceInfo **deviceInfo)
{
    struct UsbInfoQueryPara infoQueryPara;

    if ((g_usbPnpNotifyCmdType == USB_PNP_NOTIFY_ADD_INTERFACE)
        || (g_usbPnpNotifyCmdType == USB_PNP_NOTIFY_REMOVE_INTERFACE)) {
        pnpInfoData->infoData = (struct UsbPnpAddRemoveInfo *)eventData;
    } else {
        pnpInfoData->usbDev = (struct usb_device *)eventData;
    }

    if ((g_usbPnpNotifyCmdType == USB_PNP_NOTIFY_ADD_INTERFACE)
        || (g_usbPnpNotifyCmdType == USB_PNP_NOTIFY_REMOVE_INTERFACE)) {
        infoQueryPara.type = USB_INFO_NORMAL_TYPE;
        infoQueryPara.devNum = pnpInfoData->infoData->devNum;
        infoQueryPara.busNum = pnpInfoData->infoData->busNum;
        *deviceInfo = UsbPnpNotifyFindInfo(infoQueryPara);
    } else if ((g_usbPnpNotifyCmdType == USB_PNP_NOTIFY_ADD_DEVICE)
        || (g_usbPnpNotifyCmdType == USB_PNP_NOTIFY_REMOVE_DEVICE)) {
        infoQueryPara.type = USB_INFO_DEVICE_ADDRESS_TYPE;
        infoQueryPara.usbDevAddr = (uint64_t)pnpInfoData->usbDev;
        *deviceInfo = UsbPnpNotifyFindInfo(infoQueryPara);
    } else {
        *deviceInfo = UsbPnpNotifyCreateInfo();
    }

    if (*deviceInfo == NULL) {
        HDF_LOGE("%s:%d create or find info failed", __func__, __LINE__);
        return HDF_FAILURE;
    }

    (*deviceInfo)->info.removeType = g_usbPnpNotifyRemoveType;

    return HDF_SUCCESS;
}

static int32_t UsbPnpNotifyHdfSendEvent(const struct HdfDeviceObject *deviceObject,
    void *eventData)
{
    int32_t ret;
    struct UsbPnpDeviceInfo *deviceInfo = NULL;
    struct HdfSBuf *data = NULL;
    union UsbPnpDeviceInfoData pnpInfoData;

    if ((deviceObject == NULL) || (eventData == NULL)) {
        HDF_LOGE("%s deviceObject=%p or eventData=%p is NULL", __func__, deviceObject, eventData);
        return HDF_ERR_INVALID_PARAM;
    }

    data = HdfSbufObtainDefaultSize();
    if (data == NULL) {
        HDF_LOGE("%s:%d InitDataBlock failed", __func__, __LINE__);
        return HDF_FAILURE;
    }

    ret = UsbPnpNotifyGetDeviceInfo(eventData, &pnpInfoData, &deviceInfo);
    if (ret != HDF_SUCCESS) {
        HDF_LOGE("%s:%d UsbPnpNotifyGetDeviceInfo failed, ret=%d", __func__, __LINE__, ret);
        goto ERROR_DEVICE_INFO;
    }

    ret = UsbPnpNotifyInitInfo(data, deviceInfo, pnpInfoData);
    if (ret != HDF_SUCCESS) {
        HDF_LOGE("%s:%d UsbPnpNotifyInitInfo failed, ret=%d", __func__, __LINE__, ret);
        goto OUT;
    }

    HDF_LOGI("%s:%d report one device information, %d usbDevAddr=%llu, devNum=%d, busNum=%d, infoTable=%d-0x%x-0x%x!",
        __func__, __LINE__, g_usbPnpNotifyCmdType, deviceInfo->info.usbDevAddr, deviceInfo->info.devNum,
        deviceInfo->info.busNum, deviceInfo->info.numInfos, deviceInfo->info.deviceInfo.vendorId,
        deviceInfo->info.deviceInfo.productId);

    OsalMutexLock(&deviceInfo->lock);
    if (deviceInfo->status == USB_PNP_DEVICE_INIT_STATUS) {
        ret = HdfDeviceSendEvent(deviceObject, g_usbPnpNotifyCmdType, data);
        if (ret != HDF_SUCCESS) {
            OsalMutexUnlock(&deviceInfo->lock);
            HDF_LOGE("%s:%d HdfDeviceSendEvent ret=%d", __func__, __LINE__, ret);
            goto OUT;
        }
        deviceInfo->status = USB_PNP_DEVICE_ADD_STATUS;
    } else {
        ret = HDF_ERR_INVALID_OBJECT;
    }
    OsalMutexUnlock(&deviceInfo->lock);

OUT:
    if ((ret != HDF_SUCCESS) || (g_usbPnpNotifyCmdType == USB_PNP_NOTIFY_REMOVE_DEVICE)) {
        if (UsbPnpNotifyDestroyInfo(deviceInfo) != HDF_SUCCESS) {
            HDF_LOGE("%s:%d UsbPnpNotifyDestroyInfo fail", __func__, __LINE__);
        }
    }
ERROR_DEVICE_INFO:
    HdfSbufRecycle(data);
    return ret;
}

#if USB_PNP_NOTIFY_TEST_MODE == true
static void TestReadPnpInfo(struct HdfSBuf *data)
{
    uint32_t infoSize;
    bool flag;

    flag = HdfSbufReadBuffer(data, (const void **)(&g_testUsbPnpInfo), &infoSize);
    if ((!flag) || (g_testUsbPnpInfo == NULL)) {
        HDF_LOGE("%s: fail to read g_testUsbPnpInfo, flag=%d", __func__, flag);
        return;
    }

    HDF_LOGI("%s:%d infoSize=%d read success!", __func__, __LINE__, infoSize);
}

static void TestPnpNotifyFillInfoTable(struct UsbPnpNotifyMatchInfoTable *infoTable)
{
    int8_t i;

    infoTable->usbDevAddr = g_testUsbPnpInfo->usbDevAddr;
    infoTable->devNum = g_testUsbPnpInfo->devNum;
    if (g_usbPnpNotifyCmdType == USB_PNP_NOTIFY_REMOVE_TEST) {
        infoTable->busNum = -1;
    } else {
        infoTable->busNum = g_testUsbPnpInfo->busNum;
    }

    infoTable->deviceInfo.vendorId = g_testUsbPnpInfo->deviceInfo.vendorId;
    infoTable->deviceInfo.productId = g_testUsbPnpInfo->deviceInfo.productId;
    infoTable->deviceInfo.bcdDeviceLow = g_testUsbPnpInfo->deviceInfo.bcdDeviceLow;
    infoTable->deviceInfo.bcdDeviceHigh = g_testUsbPnpInfo->deviceInfo.bcdDeviceHigh;
    infoTable->deviceInfo.deviceClass = g_testUsbPnpInfo->deviceInfo.deviceClass;
    infoTable->deviceInfo.deviceSubClass = g_testUsbPnpInfo->deviceInfo.deviceSubClass;
    infoTable->deviceInfo.deviceProtocol = g_testUsbPnpInfo->deviceInfo.deviceProtocol;

    infoTable->removeType = g_usbPnpNotifyRemoveType;

    if (g_usbPnpNotifyCmdType != USB_PNP_NOTIFY_REMOVE_TEST) {
        infoTable->numInfos = g_testUsbPnpInfo->numInfos;
        for (i = 0; i < infoTable->numInfos; i++) {
            infoTable->interfaceInfo[i].interfaceClass = g_testUsbPnpInfo->interfaceInfo[i].interfaceClass;
            infoTable->interfaceInfo[i].interfaceSubClass = g_testUsbPnpInfo->interfaceInfo[i].interfaceSubClass;
            infoTable->interfaceInfo[i].interfaceProtocol = g_testUsbPnpInfo->interfaceInfo[i].interfaceProtocol;
            infoTable->interfaceInfo[i].interfaceNumber = g_testUsbPnpInfo->interfaceInfo[i].interfaceNumber;
        }
    }
}

static int32_t TestPnpNotifyHdfSendEvent(const struct HdfDeviceObject *deviceObject)
{
    struct UsbPnpNotifyMatchInfoTable infoTable;
    struct HdfSBuf *data = NULL;

    if ((deviceObject == NULL) || (g_testUsbPnpInfo == NULL)) {
        HDF_LOGE("%s deviceObject=%px or g_testUsbPnpInfo=%px is NULL", __func__, deviceObject, g_testUsbPnpInfo);
        return HDF_ERR_INVALID_PARAM;
    }

    data = HdfSbufObtainDefaultSize();
    if (data == NULL) {
        HDF_LOGE("%s InitDataBlock failed", __func__);
        return HDF_FAILURE;
    }

    TestPnpNotifyFillInfoTable(&infoTable);

    if (!HdfSbufWriteBuffer(data, (const void *)(&infoTable), sizeof(struct UsbPnpNotifyMatchInfoTable))) {
        HDF_LOGE("%s: sbuf write infoTable failed", __func__);
        goto OUT;
    }

    HDF_LOGI("%s: report one device information, %d usbDev=%llu, devNum=%d, busNum=%d, infoTable=%d-0x%x-0x%x!", \
        __func__, g_usbPnpNotifyCmdType, infoTable.usbDevAddr, infoTable.devNum, infoTable.busNum, \
        infoTable.numInfos, infoTable.deviceInfo.vendorId, infoTable.deviceInfo.productId);

    int32_t ret = HdfDeviceSendEvent(deviceObject, g_usbPnpNotifyCmdType, data);
    if (ret != HDF_SUCCESS) {
        HDF_LOGE("%s: HdfDeviceSendEvent error=%d", __func__, ret);
        goto OUT;
    }

    HdfSbufRecycle(data);
    return ret;

OUT:
    HdfSbufRecycle(data);
    return HDF_FAILURE;
}
#endif

static int32_t GadgetPnpNotifyHdfSendEvent(const struct HdfDeviceObject *deviceObject)
{
    if (deviceObject == NULL) {
        HDF_LOGE("%s deviceObject is null", __func__);
        return HDF_ERR_INVALID_PARAM;
    }

    struct HdfSBuf *data = NULL;
    data = HdfSbufObtainDefaultSize();
    if (data == NULL) {
        HDF_LOGE("%s:%d InitDataBlock failed", __func__, __LINE__);
        return HDF_FAILURE;
    }
    if (!HdfSbufWriteUint8(data, g_gadgetPnpNotifyType)) {
        HDF_LOGE("%s, UsbEcmRead HdfSbufWriteInt8 error", __func__);
        HdfSbufRecycle(data);
        return HDF_FAILURE;
    }
    int32_t ret = HdfDeviceSendEvent(deviceObject, g_gadgetPnpNotifyType, data);
    if (ret != HDF_SUCCESS) {
        HDF_LOGE("%s:%d HdfDeviceSendEvent ret = %d", __func__, __LINE__, ret);
    }

    HdfSbufRecycle(data);
    return ret;
}

static int32_t UsbPnpNotifyFirstReport(struct usb_device *usbDev, void *data)
{
    int32_t ret;
    if (data == NULL) {
        HDF_LOGE("%{pubilc}s:%{pubilc}d data is NULL", __func__, __LINE__);
        return HDF_FAILURE;
    }
    
    struct HdfDeviceIoClient *client = (struct HdfDeviceIoClient *)data;

    ret = UsbPnpNotifyHdfSendEvent(client->device, usbDev);

    HDF_LOGI("%s: report all device information!", __func__);

    return ret;
}

static int32_t UsbPnpNotifyReportThread(void* arg)
{
    int32_t ret;
    struct HdfDeviceObject *deviceObject = (struct HdfDeviceObject *)arg;

    while (!kthread_should_stop()) {
#if USB_PNP_NOTIFY_TEST_MODE == false
        ret = wait_event_interruptible(g_usbPnpNotifyReportWait, g_usbDevice != NULL);
#else
        ret = wait_event_interruptible(g_usbPnpNotifyReportWait,
            ((g_usbDevice != NULL) || (g_testUsbPnpInfo != NULL)));
#endif
        if (!ret) {
            HDF_LOGI("%s: UsbPnpNotifyReportThread start!", __func__);
        }

        OsalMutexLock(&g_usbSendEventLock);
#if USB_PNP_NOTIFY_TEST_MODE == true
        if ((g_usbPnpNotifyCmdType == USB_PNP_NOTIFY_ADD_TEST) || \
            (g_usbPnpNotifyCmdType == USB_PNP_NOTIFY_REMOVE_TEST)) {
            ret = TestPnpNotifyHdfSendEvent(deviceObject);
        } else {
#endif
            if ((g_usbPnpNotifyCmdType == USB_PNP_NOTIFY_ADD_INTERFACE)
                || (g_usbPnpNotifyCmdType == USB_PNP_NOTIFY_REMOVE_INTERFACE)) {
                ret = UsbPnpNotifyHdfSendEvent(deviceObject, &g_usbPnpInfo);
            } else {
                ret = UsbPnpNotifyHdfSendEvent(deviceObject, g_usbDevice);
            }
#if USB_PNP_NOTIFY_TEST_MODE == true
        }
#endif
        if (ret != HDF_SUCCESS) {
            HDF_LOGI("%s: UsbPnpNotifyHdfSendEvent error=%d!", __func__, ret);
        }

        g_usbDevice = NULL;
#if USB_PNP_NOTIFY_TEST_MODE == true
        g_testUsbPnpInfo = NULL;
#endif
        OsalMutexUnlock(&g_usbSendEventLock);

        msleep(USB_PNP_NOTIFY_MSLEEP_TIME);
    }

    HDF_LOGI("%s: usb pnp notify handle kthread exiting!", __func__);

    return 0;
}

static int32_t GadgetPnpNotifyReportThread(void* arg)
{
    int32_t ret;
    struct HdfDeviceObject *deviceObject = (struct HdfDeviceObject *)arg;

    while (!kthread_should_stop()) {
        ret = wait_event_interruptible(g_gadgetPnpNotifyReportWait,
            (g_gadgetPnpNotifyType != 0));
        if (!ret) {
            HDF_LOGI("%s: GadgetPnpNotifyReportThread start!", __func__);
        }
        OsalMutexLock(&g_gadgetSendEventLock);
        ret = GadgetPnpNotifyHdfSendEvent(deviceObject);
        if (ret != HDF_SUCCESS) {
            HDF_LOGI("%s: UsbPnpNotifyHdfSendEvent error=%d!", __func__, ret);
        }
        g_gadgetPnpNotifyType = 0;
        OsalMutexUnlock(&g_gadgetSendEventLock);
        msleep(USB_PNP_NOTIFY_MSLEEP_TIME);
    }

    HDF_LOGI("%s: gadget pnp notify handle kthread exiting!", __func__);

    return 0;
}

static int32_t UsbPnpNotifyCallback(struct notifier_block *self, unsigned long action, void *dev)
{
    int32_t ret;
    struct UsbInfoQueryPara infoQueryPara;
    struct UsbPnpDeviceInfo *deviceInfo = NULL;
    union UsbPnpDeviceInfoData pnpInfoData;

    HDF_LOGI("%s: action=0x%lx", __func__, action);

    switch (action) {
        case USB_DEVICE_ADD:
            pnpInfoData.usbDev = dev;
            deviceInfo = UsbPnpNotifyCreateInfo();
            if (deviceInfo == NULL) {
                HDF_LOGE("%s:%d USB_DEVICE_ADD create info failed", __func__, __LINE__);
            } else {
                ret = UsbPnpNotifyAddInitInfo(deviceInfo, pnpInfoData);
                if (ret != HDF_SUCCESS) {
                    HDF_LOGE("%s:%d USB_DEVICE_ADD UsbPnpNotifyAddInitInfo error", __func__, __LINE__);
                } else {
                    OsalMutexLock(&g_usbSendEventLock);
                    g_usbDevice = dev;
                    g_usbPnpNotifyCmdType = USB_PNP_NOTIFY_ADD_DEVICE;
                    OsalMutexUnlock(&g_usbSendEventLock);
                    wake_up_interruptible(&g_usbPnpNotifyReportWait);
                }
            }
            break;
        case USB_DEVICE_REMOVE:
            infoQueryPara.type = USB_INFO_DEVICE_ADDRESS_TYPE;
            infoQueryPara.usbDevAddr = (uint64_t)dev;
            deviceInfo = UsbPnpNotifyFindInfo(infoQueryPara);
            if (deviceInfo == NULL) {
                HDF_LOGE("%s:%d USB_DEVICE_REMOVE find info failed", __func__, __LINE__);
            } else {
                OsalMutexLock(&deviceInfo->lock);
                if (deviceInfo->status != USB_PNP_DEVICE_INIT_STATUS) {
                    deviceInfo->status = USB_PNP_DEVICE_INIT_STATUS;
                } else {
                    deviceInfo->status = USB_PNP_DEVICE_REMOVE_STATUS;
                }
                OsalMutexUnlock(&deviceInfo->lock);
                OsalMutexLock(&g_usbSendEventLock);
                g_usbDevice = dev;
                g_usbPnpNotifyCmdType = USB_PNP_NOTIFY_REMOVE_DEVICE;
                g_usbPnpNotifyRemoveType = USB_PNP_NOTIFY_REMOVE_BUS_DEV_NUM;
                OsalMutexUnlock(&g_usbSendEventLock);
                wake_up_interruptible(&g_usbPnpNotifyReportWait);
            }
            break;
        case USB_GADGET_ADD:
        case USB_GADGET_REMOVE:
            OsalMutexLock(&g_gadgetSendEventLock);  
            if (g_preAcion == action) {
                OsalMutexUnlock(&g_gadgetSendEventLock);
                break;
            }
            if (action == USB_GADGET_ADD) {
                g_gadgetPnpNotifyType  = USB_PNP_DRIVER_GADGET_ADD;
            } else {
                g_gadgetPnpNotifyType = USB_PNP_DRIVER_GADGET_REMOVE;
            }
            OsalMutexUnlock(&g_gadgetSendEventLock);
            HDF_LOGI("%s:%d g_gadgetPnpNotifyType = %hhu", __func__, __LINE__, g_gadgetPnpNotifyType);
            wake_up_interruptible(&g_gadgetPnpNotifyReportWait);
            g_preAcion = action;
            break;
        default:
            HDF_LOGI("%s: the action = 0x%lx is not defined!", __func__, action);
            break;
    }

    return NOTIFY_OK;
}

static struct notifier_block g_usbPnpNotifyNb = {
    .notifier_call =    UsbPnpNotifyCallback,
};

static void UsbPnpNotifyReadPnpInfo(struct HdfSBuf *data)
{
    struct UsbInfoQueryPara infoQueryPara;
    struct UsbPnpDeviceInfo *deviceInfo = NULL;
    struct UsbPnpAddRemoveInfo *usbPnpInfo = NULL;
    uint32_t infoSize;
    bool flag;

    flag = HdfSbufReadBuffer(data, (const void **)(&usbPnpInfo), &infoSize);
    if ((!flag) || (usbPnpInfo == NULL)) {
        HDF_LOGE("%s: fail to read g_usbPnpInfo, flag=%d", __func__, flag);
        return;
    }

    g_usbPnpInfo.devNum = usbPnpInfo->devNum;
    g_usbPnpInfo.busNum = usbPnpInfo->busNum;
    g_usbPnpInfo.interfaceNumber = usbPnpInfo->interfaceNumber;
    g_usbPnpInfo.interfaceClass = usbPnpInfo->interfaceClass;
    g_usbPnpInfo.interfaceSubClass = usbPnpInfo->interfaceSubClass;
    g_usbPnpInfo.interfaceProtocol = usbPnpInfo->interfaceProtocol;

    g_usbDevice = (struct usb_device *)&g_usbPnpInfo;

    infoQueryPara.type = USB_INFO_NORMAL_TYPE;
    infoQueryPara.devNum = usbPnpInfo->devNum;
    infoQueryPara.busNum = usbPnpInfo->busNum;
    deviceInfo = UsbPnpNotifyFindInfo(infoQueryPara);
    if (deviceInfo == NULL) {
        HDF_LOGE("%s:%d add or remove interface find info failed", __func__, __LINE__);
    } else {
        OsalMutexLock(&deviceInfo->lock);
        if (deviceInfo->status != USB_PNP_DEVICE_INIT_STATUS) {
            deviceInfo->status = USB_PNP_DEVICE_INIT_STATUS;
        } else {
            deviceInfo->status = USB_PNP_DEVICE_INTERFACE_STATUS;
        }
        OsalMutexUnlock(&deviceInfo->lock);
    }

    HDF_LOGI("%s:%d infoSize=%d g_usbPnpInfo=%px-%d-%d-%d-%d-%d-%d read success!",
        __func__, __LINE__, infoSize, &g_usbPnpInfo, g_usbPnpInfo.devNum, g_usbPnpInfo.busNum,
        g_usbPnpInfo.interfaceNumber, g_usbPnpInfo.interfaceClass, g_usbPnpInfo.interfaceSubClass,
        g_usbPnpInfo.interfaceProtocol);
}

static int32_t UsbPnpGetDevices(struct HdfSBuf *reply)
{
    int32_t ret = HDF_SUCCESS;
    struct UsbPnpDeviceInfo *infoPos = NULL;
    struct UsbPnpDeviceInfo *infoTemp = NULL;

    if (DListIsEmpty(&g_usbPnpInfoListHead)) {
        return ret;
    }
    DLIST_FOR_EACH_ENTRY_SAFE(infoPos, infoTemp, &g_usbPnpInfoListHead, struct UsbPnpDeviceInfo, list){
        if (!HdfSbufWriteInt32(reply, infoPos->info.busNum)) {
            break;
        }
        if (!HdfSbufWriteInt32(reply, infoPos->info.devNum)) {
            break;
        }
        if (!HdfSbufWriteUint8(reply, infoPos->info.deviceInfo.deviceClass)) {
            break;
        }
        if (!HdfSbufWriteUint8(reply, infoPos->info.deviceInfo.deviceSubClass)) {
            break;
        }
        if (!HdfSbufWriteUint8(reply, infoPos->info.deviceInfo.deviceProtocol)) {
            break;
        }
        if (!HdfSbufWriteUint8(reply, infoPos->status)) {
            break;
        }
    }
    return ret;
}

static int32_t UsbPnpNotifyDispatch(struct HdfDeviceIoClient *client, int32_t cmd,
    struct HdfSBuf *data, struct HdfSBuf *reply)
{
    int32_t ret = HDF_SUCCESS;

    HDF_LOGI("%s: received cmd = %d", __func__, cmd);

    OsalMutexLock(&g_usbSendEventLock);
    if (USB_PNP_DRIVER_GETDEVICES != cmd) {
        g_usbPnpNotifyCmdType = cmd;
    }

    switch (cmd) {
        case USB_PNP_NOTIFY_ADD_INTERFACE:
            UsbPnpNotifyReadPnpInfo(data);
            wake_up_interruptible(&g_usbPnpNotifyReportWait);
            break;
        case USB_PNP_NOTIFY_REMOVE_INTERFACE:
            UsbPnpNotifyReadPnpInfo(data);
            g_usbPnpNotifyRemoveType = USB_PNP_NOTIFY_REMOVE_INTERFACE_NUM;
            wake_up_interruptible(&g_usbPnpNotifyReportWait);
            break;
        case USB_PNP_NOTIFY_REPORT_INTERFACE:
            usb_for_each_dev((void *)client, UsbPnpNotifyFirstReport);
            break;
#if USB_PNP_NOTIFY_TEST_MODE == true
        case USB_PNP_NOTIFY_ADD_TEST:
            TestReadPnpInfo(data);
            wake_up_interruptible(&g_usbPnpNotifyReportWait);
            break;
        case USB_PNP_NOTIFY_REMOVE_TEST:
            TestReadPnpInfo(data);
            g_usbPnpNotifyRemoveType = g_testUsbPnpInfo->removeType;
            wake_up_interruptible(&g_usbPnpNotifyReportWait);
            break;
#endif
        case USB_PNP_DRIVER_GETDEVICES:
            UsbPnpGetDevices(reply);
            break;
        default:
            ret = HDF_ERR_NOT_SUPPORT;
            break;
    }
    OsalMutexUnlock(&g_usbSendEventLock);

    if (!HdfSbufWriteInt32(reply, INT32_MAX)) {
        HDF_LOGE("%s: reply int32 fail", __func__);
    }

    return ret;
}

static int32_t UsbPnpNotifyBind(struct HdfDeviceObject *device)
{
    static struct IDeviceIoService pnpNotifyService = {
        .Dispatch = UsbPnpNotifyDispatch,
    };

    HDF_LOGI("%s: Enter!", __func__);

    if (device == NULL) {
        HDF_LOGE("%s: device is NULL!", __func__);
        return HDF_ERR_INVALID_OBJECT;
    }

    device->service = &pnpNotifyService;

    return HDF_SUCCESS;
}

static int32_t UsbPnpNotifyInit(struct HdfDeviceObject *device)
{
    static bool firstInitFlag = true;

    HDF_LOGI("%s: Enter!", __func__);

    if (device == NULL) {
        HDF_LOGE("%s: device is NULL", __func__);
        return HDF_ERR_INVALID_OBJECT;
    }

    if (firstInitFlag) {
        firstInitFlag = false;
        DListHeadInit(&g_usbPnpInfoListHead);
    }

    init_waitqueue_head(&g_usbPnpNotifyReportWait);
    init_waitqueue_head(&g_gadgetPnpNotifyReportWait);

    OsalMutexInit(&g_usbSendEventLock);
    OsalMutexInit(&g_gadgetSendEventLock);

    /* Add a new notify for usb pnp notify module. */
    usb_register_notify(&g_usbPnpNotifyNb);

    /* Create thread to handle send usb interface information. */
    if (NULL == g_usbPnpNotifyReportThread) {
        g_usbPnpNotifyReportThread = kthread_run(UsbPnpNotifyReportThread,
            (void *)device, "usb pnp notify handle kthread");
        if (g_usbPnpNotifyReportThread == NULL) {
            HDF_LOGE("%s: kthread_run g_usbPnpNotifyReportThread is NULL", __func__);
        }
    } else {
        HDF_LOGI("%s: g_usbPnpNotifyReportThread is already running!", __func__);
    }

    /* Create thread to handle gadeget information. */
    if (NULL == g_gadgetPnpNotifyReportThread) {
        g_gadgetPnpNotifyReportThread = kthread_run(GadgetPnpNotifyReportThread,
            (void *)device, "gadget pnp notify handle kthread");
        if (g_usbPnpNotifyReportThread == NULL) {
            HDF_LOGE("%s: kthread_run g_usbPnpNotifyReportThread is NULL", __func__);
        }
    } else {
        HDF_LOGI("%s: g_devPnpNotifyReportThread is already running!", __func__);
    }

    return HDF_SUCCESS;
}

static void UsbPnpNotifyRelease(struct HdfDeviceObject *device)
{
    if (device == NULL) {
        HDF_LOGI("%s: device is null", __func__);
        return;
    }

    if (g_usbPnpNotifyReportThread == NULL) {
        HDF_LOGI("%s: g_usbPnpNotifyReportThread is not running!", __func__);
    } else {
        kthread_stop(g_usbPnpNotifyReportThread);
    }
    if (g_gadgetPnpNotifyReportThread == NULL) {
        HDF_LOGI("%s: g_usbPnpNotifyReportThread is not running!", __func__);
    } else {
        kthread_stop(g_gadgetPnpNotifyReportThread);
    }

    usb_unregister_notify(&g_usbPnpNotifyNb);

    OsalMutexDestroy(&g_usbSendEventLock);
    OsalMutexDestroy(&g_gadgetSendEventLock);

    HDF_LOGI("%s: release done!", __func__);

    return;
}

struct HdfDriverEntry g_usbPnpNotifyEntry = {
    .moduleVersion = 1,
    .Bind = UsbPnpNotifyBind,
    .Init = UsbPnpNotifyInit,
    .Release = UsbPnpNotifyRelease,
    .moduleName = "HDF_USB_PNP_NOTIFY",
};

HDF_INIT(g_usbPnpNotifyEntry);